### Install @hashspan/viem Source: https://github.com/selimaytac/hashspan/blob/main/packages/viem/README.md Install the required packages for the @hashspan/viem extension. ```sh npm install @hashspan/viem @opentelemetry/api viem ``` -------------------------------- ### Install @hashspan/x402 and dependencies Source: https://github.com/selimaytac/hashspan/blob/main/packages/x402/README.md Install the required packages for x402 integration, including the core library, EVM scheme, fetch adapter, OpenTelemetry API, and viem. ```sh npm install @hashspan/x402 @x402/core @x402/evm @x402/fetch @opentelemetry/api viem ``` -------------------------------- ### Usage example with tracker and mppx client Source: https://github.com/selimaytac/hashspan/blob/main/packages/mpp/README.md Shows how to set up a shared transaction tracker, create a viem client with @hashspan/viem, create an mppx client, and register the @hashspan/mpp adapter. The example makes a fetch call and flushes before process exit. Requires a private key and a reader client. ```ts import { createTxTracker } from '@hashspan/core'; import { withHashspan } from '@hashspan/mpp'; import { withHashspan as withViemHashspan } from '@hashspan/viem'; import { Mppx, tempo } from 'mppx/client'; import { createClient, createPublicClient, http, publicActions, walletActions } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { tempo as tempoChain } from 'viem/chains'; // One tracker for both adapters, so that a payment span links to the payer's own send span and each // transaction gets one confirm span. const tracker = createTxTracker(); const account = privateKeyToAccount(privateKey); const wallet = createClient({ account, chain: tempoChain, transport: http() }) .extend(publicActions) .extend(walletActions) .extend(withViemHashspan({ tracker })); const mppx = Mppx.create({ methods: [tempo({ account, getClient: () => wallet })], polyfill: false, }); // Registers observers on the client; call it once per client. const hashspan = withHashspan(mppx, { tracker, reader: createPublicClient({ chain: tempoChain, transport: http() }), }); await mppx.fetch('https://api.example.com/weather'); // payment span, then confirm span // Before a short-lived process exits: await hashspan.flush(); ``` -------------------------------- ### Install @hashspan/core Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Install the core package and its required peer dependency. ```sh npm install @hashspan/core @opentelemetry/api ``` -------------------------------- ### Install @hashspan/cdp and dependencies Source: https://github.com/selimaytac/hashspan/blob/main/packages/cdp/README.md Install the required packages to use the CDP adapter with OpenTelemetry and viem. ```sh npm install @hashspan/cdp @coinbase/cdp-sdk @opentelemetry/api viem ``` -------------------------------- ### Initialize development environment Source: https://github.com/selimaytac/hashspan/blob/main/CONTRIBUTING.md Commands to set up the project environment, install dependencies, and run tests. ```sh nvm use # Node version from .nvmrc corepack enable pnpm pnpm install make tools # project-local Anvil and bundler in ./.tools for integration tests pnpm test # unit tests pnpm test:integration # integration tests against Anvil ``` -------------------------------- ### Install @hashspan/mpp and dependencies Source: https://github.com/selimaytac/hashspan/blob/main/packages/mpp/README.md Install the package along with required dependencies: @hashspan/core, @hashspan/viem, mppx, @opentelemetry/api, and viem. Requires Node.js 22.3+, mppx 0.13.5+, and viem 2.57.1+. ```sh npm install @hashspan/mpp @hashspan/core @hashspan/viem mppx @opentelemetry/api viem ``` -------------------------------- ### Install dependencies Source: https://github.com/selimaytac/hashspan/blob/main/README.md Install the required Hashspan, OpenTelemetry, and viem packages, along with tsx for execution and anvil for a local chain. ```sh npm install @hashspan/viem @opentelemetry/api viem npm install @opentelemetry/sdk-node # unless your app already sets up an OpenTelemetry SDK npm install -D tsx # runs agent.ts below anvil # in a second terminal: a local chain ``` -------------------------------- ### Install Hashspan dependencies Source: https://github.com/selimaytac/hashspan/blob/main/skills/hashspan/SKILL.md Run these commands to install the core Hashspan packages and OpenTelemetry SDK dependencies. ```sh npm install @hashspan/viem @opentelemetry/api viem npm install @opentelemetry/sdk-node ``` -------------------------------- ### Run Local Demo Source: https://github.com/selimaytac/hashspan/blob/main/examples/ai-sdk-agent/README.md Commands to start the local lab environment and run the agent demo. ```sh make lab-up # Jaeger UI on http://localhost:16686 make demo # starts a local chain, runs the agent, stops the chain ``` -------------------------------- ### Project Setup and Build Commands Source: https://github.com/selimaytac/hashspan/blob/main/AGENTS.md Standard commands for managing dependencies, building the project, and running various test suites. ```bash pnpm install pnpm build pnpm test pnpm test:integration pnpm test:load pnpm --dir integrations install pnpm test:integrations pnpm lint pnpm format pnpm typecheck pnpm test:coverage pnpm check:packages pnpm api:check pnpm api:update pnpm smoke:packages make lab-up make anvil make demo make lab-pause make lab-nuke ``` -------------------------------- ### Trace Hardhat tests with node:test Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Example of setting up Hashspan tracing within a Hardhat test suite. The SDK is started once per process and flushed after the test suite completes. ```ts import assert from 'node:assert/strict'; import { after, describe, it } from 'node:test'; import { withHashspan } from '@hashspan/viem'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { network } from 'hardhat'; // Hardhat runs the test files in its own process: start the SDK once, before the first test. const sdk = new NodeSDK({ serviceName: 'counter-tests' }); sdk.start(); describe('Counter', async () => { const { viem } = await network.create(); const hashspan = withHashspan(); const publicClient = (await viem.getPublicClient()).extend(hashspan); const [sender] = await viem.getWalletClients(); if (sender === undefined) throw new Error('The network has no accounts'); const client = { public: publicClient, wallet: sender.extend(hashspan) }; after(async () => { await hashspan.flush(); await sdk.shutdown(); }); it('adds the increment', async () => { const counter = await viem.deployContract('Counter', [], { client }); const hash = await counter.write.incBy([5n]); await publicClient.waitForTransactionReceipt({ hash }); assert.equal(await counter.read.x(), 5n); }); }); ``` -------------------------------- ### Run AI SDK agent demo Source: https://github.com/selimaytac/hashspan/blob/main/README.md Commands to install dependencies and execute the AI SDK agent demo against a local chain. ```sh nvm use && corepack enable pnpm && pnpm install make lab-up # Jaeger UI on http://localhost:16686 make demo ``` -------------------------------- ### Run integration tests Source: https://github.com/selimaytac/hashspan/blob/main/CONTRIBUTING.md Commands to install and run tests for the integrations workspace. ```sh pnpm --dir integrations install pnpm test:integrations ``` -------------------------------- ### Configure ViemWalletProvider with Hashspan Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md This example demonstrates how to set up ViemWalletProvider with Hashspan for background transaction confirmation. It requires the OpenTelemetry SDK to be initialized beforehand and uses a type cast for the wallet client to ensure compatibility with AgentKit's specific Viem version requirements. ```ts import { ViemWalletProvider } from '@coinbase/agentkit'; import { withHashspan } from '@hashspan/viem'; import { trace } from '@opentelemetry/api'; import { createWalletClient, http, parseEther } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; // Start the OpenTelemetry SDK before this, as in the quick start. const rpcUrl = 'http://127.0.0.1:8545'; // Anvil const account = mnemonicToAccount('test test test test test test test test test test test junk'); // The provider waits for receipts on a client of its own: background confirmation records them. const hashspan = withHashspan({ confirm: { mode: 'background' } }); const walletClient = createWalletClient({ account, chain: foundry, transport: http(rpcUrl) }).extend( hashspan, ); const walletProvider = new ViemWalletProvider( // AgentKit pins its own viem: see below. walletClient as unknown as ConstructorParameters[0], { rpcUrl }, ); await trace.getTracer('my-agent').startActiveSpan('pay_vendor', async (span) => { try { // The amount in wei, as a decimal string. await walletProvider.nativeTransfer( '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', parseEther('0.01').toString(), ); } finally { span.end(); } }); // Before a short-lived process exits, then shut the SDK down: await hashspan.flush(); ``` -------------------------------- ### UserOperationConfirmInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input parameters for starting a user operation confirmation tracking operation. ```APIDOC ## UserOperationConfirmInput ### Description Input parameters for starting a user operation confirmation tracking operation. ### Properties - **chainId** (number) - Required chain ID. - **startTime** (TimeInput | undefined) - Optional start time. - **userOpHash** (string) - Required user operation hash. ``` -------------------------------- ### Send and confirm a smart account user operation Source: https://github.com/selimaytac/hashspan/blob/main/packages/cdp/README.md This example demonstrates creating a smart account and using it to send a user operation, followed by waiting for confirmation to generate the corresponding spans. ```ts const owner = await cdp.evm.getOrCreateAccount({ name: 'owner' }); const smartAccount = await cdp.evm.getOrCreateSmartAccount({ name: 'treasury', owner }); const { userOpHash } = await smartAccount.sendUserOperation({ network: 'base-sepolia', calls: [{ to, value, data: '0x' }], }); // send span await smartAccount.waitForUserOperation({ userOpHash }); // confirm span ``` -------------------------------- ### Run Jaeger via Docker Source: https://github.com/selimaytac/hashspan/blob/main/docs/backends.md Starts a Jaeger instance with UI and OTLP ports bound to localhost. ```sh docker run --rm -p 127.0.0.1:16686:16686 -p 127.0.0.1:4318:4318 jaegertracing/jaeger:2.21.0 ``` -------------------------------- ### PaymentInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input parameters for starting a payment tracking operation. ```APIDOC ## PaymentInput ### Description Input parameters for starting a payment tracking operation. ### Properties - **amount** (bigint | string | undefined) - Optional payment amount. - **asset** (string | undefined) - Optional asset identifier. - **chainId** (number) - Required chain ID. - **hash** (string | undefined) - Optional transaction hash. - **mpp** (MppPaymentDetails | undefined) - Optional MPP payment details. - **payer** (string | undefined) - Optional payer address. - **protocol** (string) - Required protocol identifier. - **recipient** (string | undefined) - Optional recipient address. - **sender** (PaymentSender | undefined) - Optional sender type. - **startTime** (TimeInput | undefined) - Optional start time. - **x402** (X402PaymentDetails | undefined) - Optional X402 payment details. ``` -------------------------------- ### SendInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input parameters for starting a send tracking operation. ```APIDOC ## SendInput ### Description Input parameters for starting a send tracking operation. ### Properties - **authorizations** (readonly AuthorizationInput[] | undefined) - Optional list of authorizations. - **chainId** (number) - Required chain ID. - **feeAsset** (string | undefined) - Optional fee asset. - **from** (string | undefined) - Optional sender address. - **functionArguments** (readonly unknown[] | undefined) - Optional function arguments. - **functionName** (string | undefined) - Optional function name. - **functionSelector** (string | undefined) - Optional function selector. - **nonce** (number | undefined) - Optional nonce. - **startTime** (TimeInput | undefined) - Optional start time. - **to** (string | undefined) - Optional recipient address. - **value** (bigint | undefined) - Optional value. ``` -------------------------------- ### tracker.startPayment Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a payment span for payments settled by another party on chain, such as x402 or MPP. ```APIDOC ## tracker.startPayment ### Description Starts a payment span for a payment that another party settles on chain. The span is a `payment {chainId}` span. ### Signature `tracker.startPayment(params)` ### Parameters - **params** (object) - Required - Parameters for the payment span. - **chainId** (number) - Required - The chain ID. - **protocol** (string) - Required - The payment protocol (e.g., 'x402', 'MPP'). - **payer** (string) - Required - The payer address. - **recipient** (string) - Required - The recipient address. - **asset** (string) - Required - The asset identifier. - **amount** (string) - Required - The payment amount. - **sender** (string) - Optional - Set to 'payer' if the payer sends or signs the settlement transaction. - **hash** (string) - Optional - The transaction hash if known before settlement. ### Returns - **payment** (object) - A handle with `end`, `fail`, `timeout`, and `link` methods. ### Example ```ts const payment = tracker.startPayment({ chainId, protocol, payer, recipient, asset, amount }); ``` ``` -------------------------------- ### TraceQL Queries for Tempo Source: https://github.com/selimaytac/hashspan/blob/main/docs/backends.md Example TraceQL queries to filter spans by error type or tool name. ```text {span.error.type = "reverted"} | select(span.blockchain.tx.revert.reason, span.blockchain.tx.fee) {span.gen_ai.tool.name = "withdraw_from_vault"} >> {span.blockchain.operation.name = "confirm" && status = error} ``` -------------------------------- ### tracker.startSend Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a send span for a transaction. Returns a handle with context, end, and fail methods. ```APIDOC ## tracker.startSend ### Description Starts a send span for a transaction, representing the period from about to send until the transaction hash is known or sending fails. ### Signature `tracker.startSend(params)` ### Parameters - **params** (object) - Required - Parameters for the send span. - **chainId** (number) - Required - The chain ID. - **from** (string) - Required - The sender address. - **to** (string) - Required - The recipient address. - **value** (string) - Required - The value being sent. - **functionName** (string) - Optional - The function name being called. ### Returns - **send** (object) - A handle with: - **context** (object) - The OpenTelemetry context to run the send operation in. - **end(result)** (function) - Ends the span with the transaction hash. - **fail(error, options?)** (function) - Fails the span with an error. ### Example ```ts const send = tracker.startSend({ chainId: 8453, from, to, value, functionName: 'transfer' }); ``` ``` -------------------------------- ### Configure Hashspan with permissionless smart account client Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Apply withHashspan() before permissionless's smartAccountActions to ensure user operation spans are traced. This setup uses createBundlerClient to build the client manually. ```ts import { withHashspan } from '@hashspan/viem'; import { smartAccountActions } from 'permissionless'; import { createPublicClient, http, parseEther } from 'viem'; import { createBundlerClient } from 'viem/account-abstraction'; import { foundry } from 'viem/chains'; // Start the OpenTelemetry SDK before this, as in the quick start. const hashspan = withHashspan(); const client = createPublicClient({ chain: foundry, transport: http('http://127.0.0.1:8545') }); // Anvil // The client createSmartAccountClient({ account, client, bundlerTransport }) builds, with hashspan applied before // permissionless's actions, so that they send through the traced user operation actions. const smartAccountClient = createBundlerClient({ account, client, transport: http(bundlerUrl) }) .extend(hashspan) .extend(smartAccountActions); // A user operation send span, and its confirm span as permissionless waits for the operation's receipt. const hash = await smartAccountClient.sendTransaction({ to: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', value: parseEther('0.001'), }); // `hash` is the bundle transaction's: a wait for it on a traced client would record the bundle, not the operation. console.log(hash); // Before a short-lived process exits, then shut the SDK down: await hashspan.flush(); ``` -------------------------------- ### Mastra Agent Integration Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Example of setting up a Mastra agent with Hashspan observability. It uses a mock model to execute a tool and exports spans via OTLP. ```ts import { Mastra } from '@mastra/core'; import { Agent } from '@mastra/core/agent'; import { createTool } from '@mastra/core/tools'; import { Observability } from '@mastra/observability'; import { OtelBridge } from '@mastra/otel-bridge'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { MockLanguageModelV4 } from 'ai/test'; import { createPublicClient, createWalletClient, http, parseEther } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; import { z } from 'zod'; import { withHashspan } from '@hashspan/viem'; // Exports over OTLP to http://localhost:4318. Start it before the agent runs a tool. const sdk = new NodeSDK({ serviceName: 'my-agent' }); sdk.start(); // Anvil's public test mnemonic: its first account is funded on every Anvil chain. const account = mnemonicToAccount('test test test test test test test test test test test junk'); const transport = http('http://127.0.0.1:8545'); const hashspan = withHashspan(); const wallet = createWalletClient({ account, chain: foundry, transport }).extend(hashspan); const reader = createPublicClient({ chain: foundry, transport }).extend(hashspan); const payVendor = createTool({ id: 'pay_vendor', description: 'Pays the vendor an amount of ETH.', inputSchema: z.object({ amountEth: z.string() }), execute: async ({ amountEth }) => { const to = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Anvil's second account const hash = await wallet.sendTransaction({ to, value: parseEther(amountEth) }); const receipt = await reader.waitForTransactionReceipt({ hash }); return { hash, status: receipt.status }; }, }); // A scripted model instead of a provider, so no API key is needed: it calls the tool once, then answers. const usage = { inputTokens: { total: 50, noCache: 50, cacheRead: 0, cacheWrite: 0 }, outputTokens: { total: 20, text: 20, reasoning: 0 }, }; const model = new MockLanguageModelV4({ doGenerate: [ { content: [{ type: 'tool-call', toolCallId: 'call_1', toolName: 'pay_vendor', input: '{"amountEth":"0.01"}' }], finishReason: { unified: 'tool-calls', raw: undefined }, usage, warnings: [], }, { content: [{ type: 'text', text: 'Paid the vendor 0.01 ETH.' }], finishReason: { unified: 'stop', raw: undefined }, usage, warnings: [], }, ], }); const agent = new Agent({ id: 'treasury', name: 'treasury', instructions: 'Pay vendors when asked.', model, tools: { pay_vendor: payVendor }, }); // The bridge runs each tool inside its execute_tool span, so the send and confirm spans become its children. new Mastra({ agents: { treasury: agent }, observability: new Observability({ configs: { default: { serviceName: 'my-agent', bridge: new OtelBridge() } }, }), }); const result = await agent.generate('Pay the vendor 0.01 ETH.'); console.log(result.text); // Before the process exits: hashspan's pending spans first, then the SDK. await hashspan.flush(); await sdk.shutdown(); ``` -------------------------------- ### TxTracker Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Interface for starting tracking operations for sends, payments, confirmations, and user operations. ```APIDOC ## TxTracker ### Description Interface for starting tracking operations for sends, payments, confirmations, and user operations. ### Methods - **startCallBatchConfirm(input: CallBatchConfirmInput, parent?: Context): CallBatchConfirmHandle** - Starts tracking a batch confirm operation. - **startCallBatchSend(input: CallBatchInput, parent?: Context): CallBatchSendHandle** - Starts tracking a batch send operation. - **startConfirm(input: ConfirmInput, parent?: Context): ConfirmHandle** - Starts tracking a confirm operation. - **startPayment(input: PaymentInput, parent?: Context): PaymentHandle** - Starts tracking a payment operation. - **startSend(input: SendInput, parent?: Context): SendHandle** - Starts tracking a send operation. - **startUserOperationConfirm(input: UserOperationConfirmInput, parent?: Context): UserOperationConfirmHandle** - Starts tracking a user operation confirm. - **startUserOperationSend(input: UserOperationInput, parent?: Context): UserOperationSendHandle** - Starts tracking a user operation send. ### Properties - **paymentSenders** (readonly PaymentSender[]) - List of payment senders. ``` -------------------------------- ### tracker.startConfirm Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a confirm span for a transaction, waiting for the receipt. Multiple callers can share the same confirm span. ```APIDOC ## tracker.startConfirm ### Description Starts a confirm span for a transaction, representing the wait for the receipt. Multiple calls for the same transaction share one confirm span. ### Signature `tracker.startConfirm(params)` ### Parameters - **params** (object) - Required - Parameters for the confirm span. - **chainId** (number) - Required - The chain ID. - **hash** (string) - Required - The transaction hash. - **confirmations** (number) - Optional - Number of confirmations to wait for. ### Returns - **confirm** (object) - A handle with `end`, `fail`, and `timeout` methods. ### Example ```ts const confirm = tracker.startConfirm({ chainId: 8453, hash }); ``` ``` -------------------------------- ### Trace a Hardhat script with Hashspan Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Example of tracing a deployment and contract interaction in a Hardhat script. Requires extending the public and wallet clients with withHashspan() and passing them to helpers. ```ts import { withHashspan } from '@hashspan/viem'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { network } from 'hardhat'; // Exports over OTLP, to http://localhost:4318 unless OTEL_EXPORTER_OTLP_ENDPOINT says otherwise. const sdk = new NodeSDK({ serviceName: 'counter-script' }); sdk.start(); const { viem } = await network.create(); const hashspan = withHashspan(); // hardhat-viem creates new clients for each helper: extend them, and pass them to every helper that sends. const publicClient = (await viem.getPublicClient()).extend(hashspan); const [sender] = await viem.getWalletClients(); if (sender === undefined) throw new Error('The network has no accounts'); const client = { public: publicClient, wallet: sender.extend(hashspan) }; // The deployment records a confirm span only: deployContract is not traced yet. const counter = await viem.deployContract('Counter', [], { client }); const hash = await counter.write.incBy([5n]); const receipt = await publicClient.waitForTransactionReceipt({ hash }); console.log(`${hash} ${receipt.status}`); // Before the script ends: await hashspan.flush(); await sdk.shutdown(); ``` -------------------------------- ### Run payout worker commands Source: https://github.com/selimaytac/hashspan/blob/main/examples/payout-worker/README.md Commands to start the local lab environment, Anvil blockchain, and the payout worker from the repository root. ```sh make lab-up # Jaeger UI on http://localhost:16686 make anvil # in a second terminal: a local chain on :8545 pnpm --filter @hashspan/example-payout-worker start ``` -------------------------------- ### Add hashspan skill via npx Source: https://github.com/selimaytac/hashspan/blob/main/README.md Use the Agent Skills installer to add hashspan to a TypeScript project. ```sh npx skills add selimaytac/hashspan ``` -------------------------------- ### Agent Trace Output Source: https://github.com/selimaytac/hashspan/blob/main/examples/ai-sdk-agent/README.md Example of the hierarchical trace output generated by the agent, showing tool execution and transaction status. ```text invoke_agent gen_ai.agent.name=treasury-agent ├─ step 1 │ └─ execute_tool pay_vendor │ ├─ send 31337 blockchain.tx.value=250000000000000000 │ └─ confirm 31337 blockchain.tx.status=success, blockchain.tx.fee=... └─ step 2 └─ execute_tool withdraw_from_vault ├─ send 31337 blockchain.contract.function.name=withdraw └─ confirm 31337 blockchain.tx.status=reverted blockchain.tx.revert.reason=WithdrawalLimitExceeded(100000000000000000, 1000000000000000000) ``` -------------------------------- ### Run grafana/otel-lgtm with Prometheus Feature Source: https://github.com/selimaytac/hashspan/blob/main/docs/backends.md Starts the LGTM stack with the created-timestamp-zero-ingestion feature enabled in Prometheus to ensure metrics accuracy for short-lived processes. ```sh docker run --rm -p 127.0.0.1:3000:3000 -p 127.0.0.1:4318:4318 \ -e PROMETHEUS_EXTRA_ARGS=--enable-feature=created-timestamp-zero-ingestion grafana/otel-lgtm:0.37.0 ``` -------------------------------- ### tracker.startCallBatchConfirm Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a confirm span for a call batch. The span is ended with end(status) which takes the batch status. ```APIDOC ## tracker.startCallBatchConfirm ### Description Starts a confirm span for a call batch. The span is ended with end(status) which takes the batch status. ### Method startCallBatchConfirm ### Parameters #### Input - **chainId** (string) - Required - The chain ID. - **id** (string) - Required - The batch id returned by the wallet. ### Notes - Accepts an optional parent Context as a second argument. - Accepts startTime in the input for recording after the fact. - Safe to call: failures are reported through diag and never thrown. ``` -------------------------------- ### tracker.startUserOperationConfirm Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a confirm span for a user operation. The span is ended with end(receipt) which takes the operation's receipt. ```APIDOC ## tracker.startUserOperationConfirm ### Description Starts a confirm span for a user operation. The span is ended with end(receipt) which takes the operation's receipt. ### Method startUserOperationConfirm ### Parameters #### Input - **chainId** (string) - Required - The chain ID. - **userOpHash** (string) - Required - The user operation hash. ### Notes - Accepts an optional parent Context as a second argument. - Accepts startTime in the input for recording after the fact. - Safe to call: failures are reported through diag and never thrown. ``` -------------------------------- ### createTxTracker Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Factory function that creates a TxTracker instance with optional configuration options. Use this to start tracking blockchain transactions. ```APIDOC ## createTxTracker ### Description Creates a new TxTracker instance with the provided options. The returned tracker can be used to track blockchain transactions. ### Signature createTxTracker(given?: TxTrackerOptions): TxTracker ### Parameters - **given** (TxTrackerOptions) - Optional - Configuration options for the tracker. ### Returns - **TxTracker** - The created tracker instance. ``` -------------------------------- ### createTxTracker Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Creates a transaction tracker instance used to start send, confirm, and payment spans. It accepts an options object with an agent name. ```APIDOC ## createTxTracker ### Description Creates a transaction tracker instance for tracing transaction lifecycle spans. ### Signature `createTxTracker(options)` ### Parameters - **options** (object) - Required - Configuration object. - **agent.name** (string) - Required - Name of the agent. ### Returns - **tracker** (object) - The tracker instance with methods `startSend`, `startConfirm`, `startPayment`, and `paymentSenders`. ### Example ```ts import { createTxTracker } from '@hashspan/core'; const tracker = createTxTracker({ agent: { name: 'treasury-bot' } }); ``` ``` -------------------------------- ### tracker.startUserOperationSend Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a send span for a user operation of an ERC-4337 smart account. The span is ended with end({ userOpHash }) or fail(error). ```APIDOC ## tracker.startUserOperationSend ### Description Starts a send span for a user operation of an ERC-4337 smart account. The span is ended with end({ userOpHash }) or fail(error). ### Method startUserOperationSend ### Parameters #### Input - **chainId** (string) - Required - The chain ID. - **sender** (string) - Required - The sender address. - **entryPoint** (string) - Required - The entry point address. - **callCount** (number) - Required - The number of calls in the user operation. ### Notes - Accepts an optional parent Context as a second argument. - Accepts startTime in the input for recording after the fact. - Safe to call: failures are reported through diag and never thrown. ``` -------------------------------- ### tracker.startCallBatchSend Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/README.md Starts a send span for a batch of calls handed to a wallet with EIP-5792 wallet_sendCalls. The span is ended with end({ id }) or fail(error). ```APIDOC ## tracker.startCallBatchSend ### Description Starts a send span for a batch of calls handed to a wallet with EIP-5792 wallet_sendCalls. The span is ended with end({ id }) or fail(error). ### Method startCallBatchSend ### Parameters #### Input - **chainId** (string) - Required - The chain ID. - **sender** (string) - Required - The sender address. - **callCount** (number) - Required - The number of calls in the batch. ### Notes - Accepts an optional parent Context as a second argument. - Accepts startTime in the input for recording after the fact. - Safe to call: failures are reported through diag and never thrown. ``` -------------------------------- ### Automated MCP Instrumentation Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Use the MCPInstrumentation class to automatically handle trace propagation. This should be initialized in both the agent and server processes after the OpenTelemetry SDK starts and before the transport connects. ```ts import { MCPInstrumentation } from '@arizeai/openinference-instrumentation-mcp'; import * as clientStdioModule from '@modelcontextprotocol/sdk/client/stdio.js'; import * as serverStdioModule from '@modelcontextprotocol/sdk/server/stdio.js'; // In the agent's process and in the server's, after the OpenTelemetry SDK starts and before the transport // connects: the client's requests carry the active trace context in `_meta`, and the server handles each request // in that context. new MCPInstrumentation().manuallyInstrument({ clientStdioModule, serverStdioModule }); ``` -------------------------------- ### Manual MCP Tracing with OpenTelemetry Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Manually propagate trace context by injecting it into the `_meta` field of MCP tool calls. This setup requires passing OpenTelemetry environment variables to the server process via the StdioClientTransport. ```ts import { Client } from '@modelcontextprotocol/client'; import { getDefaultEnvironment, StdioClientTransport } from '@modelcontextprotocol/client/stdio'; import { context, propagation, trace } from '@opentelemetry/api'; import { NodeSDK } from '@opentelemetry/sdk-node'; const sdk = new NodeSDK({ serviceName: 'my-agent' }); sdk.start(); /** The active trace context as `_meta` keys (`traceparent`, `tracestate`, `baggage`) for one request. */ function traceMeta(): Record { const meta: Record = {}; propagation.inject(context.active(), meta); return meta; } // A stdio server gets only a few variables of this environment, such as PATH and HOME: pass on its // OpenTelemetry and RPC settings. It runs mcp-server.ts next to this file, with this process's Node.js flags (tsx's). const settings = Object.entries(process.env).filter( (entry): entry is [string, string] => (entry[0].startsWith('OTEL_') || entry[0] === 'RPC_URL') && entry[1] !== undefined, ); const transport = new StdioClientTransport({ command: process.execPath, args: [...process.execArgv, new URL('./mcp-server.ts', import.meta.url).pathname], env: { ...getDefaultEnvironment(), ...Object.fromEntries(settings) }, }); const client = new Client({ name: 'my-agent', version: '1.0.0' }); await client.connect(transport); // The agent's tool call: the server's send and confirm spans become children of this span. await trace.getTracer('my-agent').startActiveSpan('pay_vendor', async (span) => { try { const result = await client.callTool({ name: 'pay_vendor', arguments: { amountEth: '0.01' }, _meta: traceMeta(), }); console.log(result.content); } finally { span.end(); } }); // Closing ends the server's stdin, so it exports its spans and exits; then this process's spans. await client.close(); await sdk.shutdown(); ``` -------------------------------- ### Record send span and watch transaction with shared tracker Source: https://github.com/selimaytac/hashspan/blob/main/packages/viem/README.md Use the core tracker with withHashspan() to record a send span covering the API call, then watch the transaction with hashspan.watch(). The send span nests HTTP spans and the confirm span links to it. Requires installing @hashspan/core at the minor of @hashspan/viem. ```TypeScript const tracker = createTxTracker(); const hashspan = withHashspan({ tracker }); const reader = createPublicClient({ chain: baseSepolia, transport: http() }); const send = tracker.startSend({ chainId: baseSepolia.id, from, to, value }); let hash: `0x${string}`; try { // The API call runs in the send span's context, so its HTTP span nests under the send span. ({ transactionHash: hash } = await context.with(send.context, () => walletApi.send(tx))); send.end({ hash }); } catch (error) { send.fail(error); throw error; } hashspan.watch(reader, { hash }); // linked to the send span ``` -------------------------------- ### Run Base Sepolia Demo Source: https://github.com/selimaytac/hashspan/blob/main/examples/ai-sdk-agent/README.md Commands to initialize the environment and run the agent on the Base Sepolia testnet. ```sh make lab-up set -a; . /path/to/base-sepolia.env; set +a make demo-base-sepolia ``` -------------------------------- ### Run the agent Source: https://github.com/selimaytac/hashspan/blob/main/README.md Configure the package type and execute the agent script. ```sh npm pkg set type=module # agent.ts uses top-level await npx tsx agent.ts # prints nothing on success: the trace is in Jaeger ``` -------------------------------- ### Initialize and use withHashspan Source: https://github.com/selimaytac/hashspan/blob/main/packages/viem/README.md Extend viem clients with the hashspan extension to automatically trace transactions. ```ts import { createPublicClient, createWalletClient, http } from 'viem'; import { baseSepolia } from 'viem/chains'; import { withHashspan } from '@hashspan/viem'; const hashspan = withHashspan(); const wallet = createWalletClient({ account, chain: baseSepolia, transport: http() }).extend(hashspan); const reader = createPublicClient({ chain: baseSepolia, transport: http() }).extend(hashspan); // Inside an agent tool, while the framework's span is active: const hash = await wallet.sendTransaction({ to, value }); // send span await reader.waitForTransactionReceipt({ hash }); // confirm span, linked to the send span ``` -------------------------------- ### Initialize x402 with Hashspan Source: https://github.com/selimaytac/hashspan/blob/main/packages/x402/README.md Configure the x402 client with an EVM scheme and wrap the fetch function with payment capabilities. Ensure hashspan is flushed before the process exits. ```ts import { withHashspan } from '@hashspan/x402'; import { x402Client } from '@x402/core/client'; import { registerExactEvmScheme } from '@x402/evm/exact/client'; import { wrapFetchWithPayment } from '@x402/fetch'; import { createPublicClient, http } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; import { baseSepolia } from 'viem/chains'; const client = new x402Client(); registerExactEvmScheme(client, { signer: privateKeyToAccount(privateKey) }); // Registers hooks on the client; call it once per client. const hashspan = withHashspan(client, { reader: createPublicClient({ chain: baseSepolia, transport: http() }), }); const fetchWithPayment = wrapFetchWithPayment(fetch, client); await fetchWithPayment('https://api.example.com/weather'); // payment span, then confirm span // Before a short-lived process exits: await hashspan.flush(); ``` -------------------------------- ### Initialize CdpClient with withHashspan Source: https://github.com/selimaytac/hashspan/blob/main/packages/cdp/README.md Wrap the CdpClient with withHashspan to enable transaction tracing. A reader (viem public client) is optional but recommended for recording confirmation spans. ```ts import { CdpClient } from '@coinbase/cdp-sdk'; import { withHashspan } from '@hashspan/cdp'; import { createPublicClient, http } from 'viem'; import { baseSepolia } from 'viem/chains'; const cdp = new CdpClient(); // Wraps the client in place; call it once, right after creating the client. const hashspan = withHashspan(cdp, { reader: createPublicClient({ chain: baseSepolia, transport: http() }), }); const account = await cdp.evm.getOrCreateAccount({ name: 'treasury' }); await account.sendTransaction({ network: 'base-sepolia', transaction: { to, value } }); // send + confirm spans // Before a short-lived process exits: await hashspan.flush(); ``` -------------------------------- ### ConfirmInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input for confirming a transaction, including chain ID, hash, optional confirmations, and start time. ```APIDOC ## ConfirmInput ### Description Input interface for confirming a transaction. ### Properties - **chainId** (number) - Required - The chain ID. - **confirmations** (number) - Optional - The number of confirmations. - **hash** (string) - Required - The transaction hash. - **startTime** (TimeInput) - Optional - The start time. ``` -------------------------------- ### withHashspan(client, options?) Source: https://github.com/selimaytac/hashspan/blob/main/packages/x402/etc/x402.api.md Wraps a Viem client object with x402 functionality, returning a HashspanX402 interface. The options allow customization of confirmation timeout, revert reason decoding, and reader client. ```APIDOC ## withHashspan(client, options?) ### Description Wraps a Viem client object with x402 functionality, returning a HashspanX402 interface. The options allow customization of confirmation timeout, revert reason decoding, and reader client. ### Method Function ### Signature withHashspan(client: object, options?: WithHashspanX402Options): HashspanX402 ### Parameters - **client** (object) - Required - The Viem client object to wrap. - **options** (WithHashspanX402Options) - Optional - Configuration options for the wrapper. ### Returns - **HashspanX402** - The wrapped client with x402 methods. ### Example ```ts import { withHashspan } from '@hashspan/x402'; const client = withHashspan(viemClient, { confirmTimeoutMs: 5000 }); ``` ``` -------------------------------- ### CallBatchConfirmInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input for confirming a call batch, including chain ID, ID, and optional start time. ```APIDOC ## CallBatchConfirmInput ### Description Input interface for confirming a call batch. ### Properties - **chainId** (number) - Required - The chain ID. - **id** (string) - Required - The batch ID. - **startTime** (TimeInput) - Optional - The start time. ``` -------------------------------- ### Configure Hashspan with GOAT SDK Source: https://github.com/selimaytac/hashspan/blob/main/docs/integrations.md Initializes Hashspan with background confirmation mode and extends the viem wallet client for use with GOAT. ```ts // GOAT waits for receipts on a client it derives from this one: background confirmation records them. const hashspan = withHashspan({ confirm: { mode: 'background' } }); const wallet = viem(createWalletClient({ account, chain, transport: http() }).extend(hashspan)); ``` -------------------------------- ### Configure agent.ts Source: https://github.com/selimaytac/hashspan/blob/main/README.md Initialize the OpenTelemetry NodeSDK and instrument viem clients with Hashspan to trace transactions. ```ts import { trace } from '@opentelemetry/api'; import { NodeSDK } from '@opentelemetry/sdk-node'; import { createPublicClient, createWalletClient, http, parseEther } from 'viem'; import { mnemonicToAccount } from 'viem/accounts'; import { foundry } from 'viem/chains'; import { withHashspan } from '@hashspan/viem'; // Exports over OTLP to http://localhost:4318. Start it before the first transaction. const sdk = new NodeSDK({ serviceName: 'my-agent' }); sdk.start(); // Anvil's public test mnemonic: its first account is funded on every Anvil chain. const account = mnemonicToAccount('test test test test test test test test test test test junk'); const transport = http('http://127.0.0.1:8545'); const hashspan = withHashspan(); const wallet = createWalletClient({ account, chain: foundry, transport }).extend(hashspan); const reader = createPublicClient({ chain: foundry, transport }).extend(hashspan); // Stands in for your agent's tool call: the send and confirm spans become its children. await trace.getTracer('my-agent').startActiveSpan('pay_vendor', async (span) => { try { const to = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Anvil's second account const hash = await wallet.sendTransaction({ to, value: parseEther('0.01') }); await reader.waitForTransactionReceipt({ hash }); } finally { span.end(); } }); // Before the process exits: hashspan's pending spans first, then the SDK. await hashspan.flush(); await sdk.shutdown(); ``` -------------------------------- ### Configure background confirmation with withHashspan Source: https://github.com/selimaytac/hashspan/blob/main/packages/viem/README.md Extends a viem wallet client with the hashspan middleware, setting the confirmation mode to background with a specified timeout. ```ts const wallet = createWalletClient({ account, chain, transport: http() }).extend( withHashspan({ confirm: { mode: 'background', timeoutMs: 60_000 } }), ); ``` -------------------------------- ### CallBatchInput Source: https://github.com/selimaytac/hashspan/blob/main/packages/core/etc/core.api.md Input for creating a call batch, with chain ID, optional call count, sender, and start time. ```APIDOC ## CallBatchInput ### Description Input interface for creating a call batch. ### Properties - **callCount** (number) - Optional - The number of calls in the batch. - **chainId** (number) - Required - The chain ID. - **sender** (string) - Optional - The sender address. - **startTime** (TimeInput) - Optional - The start time. ``` -------------------------------- ### Configure Base Sepolia Environment Source: https://github.com/selimaytac/hashspan/blob/main/examples/ai-sdk-agent/README.md Configuration file for Base Sepolia credentials. ```sh # base-sepolia.env, readable only by you (chmod 600) BASE_SEPOLIA_PRIVATE_KEY=0x... BASE_SEPOLIA_RPC_URL=https://sepolia.base.org # optional; this is the default ``` -------------------------------- ### Apply withHashspan extension order Source: https://github.com/selimaytac/hashspan/blob/main/packages/viem/README.md Demonstrates the correct order for applying the withHashspan extension relative to other extensions like publicActions to ensure actions are properly traced. ```ts // Traced walletClient.extend(publicActions).extend(withHashspan()); // Not traced: publicActions replaces waitForTransactionReceipt walletClient.extend(withHashspan()).extend(publicActions); ```