Skip to main content

Automatic Attribution on Base

Once your project is registered on Base Dashboard, the Base App will auto-append your Builder Code to transactions its users make in your app (e.g. via your app, or the Base App’s browser). This attributes that activity to your project and qualifies you for potential future rewards.

Integrating Outside the Base App

If users also access your app on the web or through other clients, you’ll need to integrate the dataSuffix parameter to capture that activity. When you register a project on Base Dashboard, you will receive a Builder Code—a random string (e.g., bc_b7k3p9da) that you’ll use to generate your attribution suffix. With Viem, configure dataSuffix once on the wallet client to append your Builder Code to every transaction it sends. With Wagmi, pass dataSuffix on each write call.
You can find your code anytime under Settings → Project Settings → Builder Code. Switch the format to Encoded String to copy the full ERC-8021 suffix, the same hex value Attribution.toDataSuffix returns.

Quick Setup with Wagmi

Wagmi does not apply a dataSuffix set in createConfig to transactions sent through a connected wallet, such as an injected browser extension. Wagmi passes createConfig options to its public client only, not to the connector client that sends sendTransaction, writeContract and sendCalls requests, so those transactions go out without your Builder Code. Pass the suffix on each call as shown below. See wagmi issue #5248 for status.
1

Install Dependencies

Install the required packages. Requires viem version 2.45.0 or higher.
Install Wagmi attribution dependencies
2

Generate Your Suffix

Generate the ERC-8021 suffix once and import it wherever your app sends a transaction.
attribution.ts
3

Pass the Suffix on Every Write

Pass dataSuffix to useSendTransaction and useWriteContract, which append it to the calldata before the wallet signs. For useSendCalls, pass it in the dataSuffix capability; the connected wallet appends it, so this requires a wallet that supports the dataSuffix capability.
SendButton.tsx
To confirm the suffix is applied, send a test transaction and check its input data as described in Verify Attribution.

Quick Setup with Viem

1

Install Dependencies

Install the required packages. Requires viem version 2.45.0 or higher.
Install Viem attribution dependencies
2

Configure Your Wallet Client

Add the dataSuffix option when creating your wallet client. See the viem wallet client docs for more configuration options.
client.ts
3

Send Transactions as Usual

Calls to sendTransaction and writeContract through this client append your Builder Code automatically. sendCalls passes it to the wallet as the dataSuffix capability.
send-transaction.ts

Using CDP Wallets

Coinbase Developer Platform (CDP) Wallets support Builder Codes on user operations from smart accounts. Pass dataSuffix when you send a user operation so your Builder Code is appended; no contract changes required. This works across the React hooks (useSendUserOperation), Node (TypeScript), and Python SDKs. See Builder Codes in the CDP Wallets documentation for setup instructions, including how to generate the suffix with Attribution.toDataSuffix from ox/erc8021.

Using Privy

Privy provides a dataSuffix plugin that automatically appends your Builder Code to all transactions—including both EOA transactions and ERC-4337 smart wallet user operations. See the Privy Builder Codes integration guide for setup instructions.

Verify Attribution

To confirm your Builder Code is being appended correctly: 1. Use a Block Explorer (Basescan, Etherscan, etc.)
  • Find your transaction hash
  • View the input data field
  • Verify the last 16 bytes are the 8021 repeating
  • Decode the suffix to confirm your Builder Code is present
2. Open Source Tools
  • Use the Builder Code Validation tool
  • Select transaction type
  • Enter the transaction or UserOperation hash
  • Click the Check Attribution button

Track User Analytics

Builder Codes tell you which onchain transactions came from your app. To measure active users, retention and conversion, join that onchain data with the signals your app already sees when someone opens it, connects a wallet and sends a transaction. This section shows where each signal comes from and how to read it. Where you store the data and which analytics stack you use is up to you.
1

Identify Users by Wallet Address

The connected wallet address is the key that joins in-app activity to onchain activity. Read it from the wallet’s EIP-1193 provider, which every wallet library exposes, and normalize it to lowercase so the same address always matches.
identify-wallet.ts
For smart accounts, this is the smart contract account address. Onchain, it appears as the sender of each UserOperation, not as the from of the outer transaction (see step 3).
2

Capture In-App Signals at Each Journey Stage

Each stage of the funnel has a signal your app can read directly. Store each one with the wallet address and a timestamp.Record the transaction hash and the action it performs in your app (for example, mint, swap or deposit) at submit time. The hash is what links an in-app action to its onchain outcome.
resolve-call-bundle.ts
3

Read Transaction Outcomes Onchain

Onchain data is the source of truth for whether a transaction happened, who sent it and whether it carried your Builder Code. Where each field lives depends on the account type:The ERC-8021 suffix sits at the end of the calldata and reads backwards: the 16-byte marker 0x80218021802180218021802180218021, then a 1-byte schema ID, then a 1-byte length, then the comma-separated codes as ASCII. For example, bc_b7k3p9da produces the suffix 0x62635f62376b33703964610b0080218021802180218021802180218021. Match on the decoded codes rather than the raw bytes, because a wallet can add its own code next to yours.The function below takes a transaction hash recorded in step 2 and returns one row per user action, for both account types. It uses Viem and ox, which are already used on this page; any RPC client works the same way.
get-attributed-activity.ts
To backfill history, or to catch transactions sent outside your app’s UI, scan Base blocks (or your contracts’ logs with eth_getLogs) for calldata that ends with the ERC-8021 marker, and decode each match the same way.
4

Measure ETH and USDC Volume

Base Dashboard reports spot trading volume, borrow TVL and lending TVL for your project. To measure the ETH or USDC that your attributed transactions move, read it from the same transaction and receipt you fetched in step 3:
  • ETH: the value of an EOA transaction. For a smart account, ETH moves in an internal call from the account, so it is not in the outer transaction value or in any log. Read it from a call trace (debug_traceTransaction with the callTracer) on a node or provider that supports it.
  • USDC and other ERC-20 tokens: Transfer logs in the receipt emitted by the token contract, filtered to transfers sent from the user’s address. Filtering by sender also separates the UserOperations in a bundle, since each one has its own sender.
get-attributed-volume.ts
Sum these values per day, week or month to report the volume attributed to your Builder Code. To report a single currency, convert each amount at the price for its block timestamp from a price source of your choice.
5

Compute the Metrics

Join the in-app signals from step 2 with the onchain rows from step 3 on the lowercased wallet address, and on the transaction hash for the submit and success stages. Then:
  1. Active users: count distinct addresses per UTC day, week or month. Report connected users and transacting users (at least one row with success and attributed both true) separately, since the gap between them is your activation rate.
  2. Retention: assign each address to a cohort by the week of its first successful attributed transaction, then measure the share of each cohort that transacts again 1, 7 and 30 days later.
  3. Funnel: count addresses that reach each stage (open → connect → submit → success), split by acquisition source. Drop-offs between submit and success split into rejected signatures (error code 4001), reverted transactions (success is false) and transactions without your Builder Code (attributed is false), which usually means a client is not configured with dataSuffix.