Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
251 changes: 133 additions & 118 deletions docs/specifications/builder-codes/for-app-developers.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,18 @@ Once your project is registered on [Base Dashboard](https://dashboard.base.org/)

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](https://dashboard.base.org/), you will receive a **Builder Code**—a random string (e.g., `bc_b7k3p9da`) that you'll use to generate your attribution suffix. The recommended approach is to configure `dataSuffix` at the client level, which appends your Builder Code to all transactions.
When you register a project on [Base Dashboard](https://dashboard.base.org/), 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.

<Tip>
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.
</Tip>

## Quick Setup with Wagmi

<Warning>
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](https://github.com/wevm/wagmi/issues/5248) for status.
</Warning>

<Steps>
<Step title="Install Dependencies">
Install the required packages. Requires viem version `2.45.0` or higher.
Expand All @@ -29,53 +33,112 @@ When you register a project on [Base Dashboard](https://dashboard.base.org/), yo
```
</Step>

<Step title="Configure Your Wagmi Client">
Add the `dataSuffix` option to your Wagmi config. This automatically appends your Builder Code to all transactions.
<Step title="Generate Your Suffix">
Generate the ERC-8021 suffix once and import it wherever your app sends a transaction.

```typescript config.ts lines expandable wrap
import { createConfig, http } from "wagmi";
import { base } from "wagmi/chains";
```typescript attribution.ts lines wrap
import { Attribution } from "ox/erc8021";

// Get your Builder Code from Base Dashboard > Settings > Project Settings > Builder Code
const DATA_SUFFIX = Attribution.toDataSuffix({
export const DATA_SUFFIX = Attribution.toDataSuffix({
codes: ["YOUR-BUILDER-CODE"],
});

export const config = createConfig({
chains: [base],
transports: {
[base.id]: http(),
},
dataSuffix: DATA_SUFFIX,
});
```
</Step>

<Step title="Use Wagmi Hooks as Usual">
With the config in place, all transactions automatically include your Builder Code—no changes to your hooks or components. This works with both `useSendTransaction` and `useSendCalls`.

```tsx App.tsx lines expandable wrap
import { useSendTransaction } from "wagmi";
import { parseEther } from "viem";

function SendButton() {
const { sendTransaction } = useSendTransaction();

return (
<button
onClick={() =>
sendTransaction({
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: parseEther("0.01"),
})
}
>
Send ETH
</button>
);
}
```
<Step title="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](/specifications/builder-codes/for-wallet-developers).

<Tabs>
<Tab title="useSendTransaction">
```tsx SendButton.tsx lines expandable wrap
import { useSendTransaction } from "wagmi";
import { parseEther } from "viem";
import { DATA_SUFFIX } from "./attribution";

function SendButton() {
const { sendTransaction } = useSendTransaction();

return (
<button
onClick={() =>
sendTransaction({
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: parseEther("0.01"),
dataSuffix: DATA_SUFFIX,
})
}
>
Send ETH
</button>
);
}
```
</Tab>
<Tab title="useWriteContract">
```tsx MintButton.tsx lines expandable wrap
import { useWriteContract } from "wagmi";
import { abi } from "./abi";
import { DATA_SUFFIX } from "./attribution";

function MintButton() {
const { writeContract } = useWriteContract();

return (
<button
onClick={() =>
writeContract({
address: "0xYourContractAddress",
abi,
functionName: "mint",
args: [1n],
dataSuffix: DATA_SUFFIX,
})
}
>
Mint
</button>
);
}
```
</Tab>
<Tab title="useSendCalls">
```tsx SendCallsButton.tsx lines expandable wrap
import { useSendCalls } from "wagmi";
import { parseEther } from "viem";
import { DATA_SUFFIX } from "./attribution";

function SendCallsButton() {
const { sendCalls } = useSendCalls();

return (
<button
onClick={() =>
sendCalls({
calls: [
{
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: parseEther("0.01"),
},
],
capabilities: {
dataSuffix: {
value: DATA_SUFFIX,
optional: true,
},
},
})
}
>
Send calls
</button>
);
}
```
</Tab>
</Tabs>

To confirm the suffix is applied, send a test transaction and check its input data as described in [Verify Attribution](#verify-attribution).
</Step>
</Steps>

Expand Down Expand Up @@ -112,7 +175,7 @@ When you register a project on [Base Dashboard](https://dashboard.base.org/), yo
</Step>

<Step title="Send Transactions as Usual">
All transactions sent through this client automatically include your Builder Code.
Calls to `sendTransaction` and `writeContract` through this client append your Builder Code automatically. `sendCalls` passes it to the wallet as the `dataSuffix` capability.

```typescript send-transaction.ts lines expandable wrap
import { parseEther } from "viem";
Expand All @@ -138,84 +201,6 @@ Privy provides a `dataSuffix` plugin that automatically appends your Builder Cod

See the [Privy Builder Codes integration guide](https://docs.privy.io/recipes/evm/base-builder-codes) for setup instructions.

## Legacy: Per-Transaction Approach

<Accordion title="Appending dataSuffix Per-Transaction">
If you need to append the suffix on a per-transaction basis rather than at the client level, you can pass `dataSuffix` directly to the transaction.

<Tabs>
<Tab title="useSendTransaction">
```tsx App.tsx lines expandable wrap
import { useSendTransaction } from "wagmi";
import { parseEther } from "viem";
import { Attribution } from "ox/erc8021";

const DATA_SUFFIX = Attribution.toDataSuffix({
codes: ["YOUR-BUILDER-CODE"],
});

function App() {
const { sendTransaction } = useSendTransaction();

return (
<button
onClick={() =>
sendTransaction({
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: parseEther("0.01"),
dataSuffix: DATA_SUFFIX,
})
}
>
Send ETH
</button>
);
}
```
</Tab>
<Tab title="useSendCalls">
When using `useSendCalls`, pass the suffix via the `capabilities` object. This requires the connected wallet to support the `dataSuffix` capability.

```tsx App.tsx lines expandable wrap
import { useSendCalls } from "wagmi";
import { parseEther } from "viem";
import { Attribution } from "ox/erc8021";

const DATA_SUFFIX = Attribution.toDataSuffix({
codes: ["YOUR-BUILDER-CODE"],
});

function App() {
const { sendCalls } = useSendCalls();

return (
<button
onClick={() =>
sendCalls({
calls: [
{
to: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
value: parseEther("1"),
},
],
capabilities: {
dataSuffix: {
value: DATA_SUFFIX,
optional: true,
},
},
})
}
>
Send calls
</button>
);
}
```
</Tab>
</Tabs>
</Accordion>

## Verify Attribution

To confirm your Builder Code is being appended correctly:
Expand Down Expand Up @@ -244,6 +229,7 @@ Builder Codes tell you which onchain transactions came from your app. To measure
| D1, D7 and D30 retention | The date each address was first seen, and whether it came back in a later period | Block timestamp of the address's first successful attributed transaction |
| Conversion funnel | Open, connect, submit, success, joined on wallet address and transaction hash | Page load, `eth_requestAccounts`, the hash or call bundle ID returned on submit, and the receipt |
| Onchain activity attributed to your app | Transactions whose ERC-8021 suffix contains your Builder Code | `input` of the transaction, or `callData` of each UserOperation |
| ETH and USDC volume | Value and token transfers sent by the user in an attributed transaction | Transaction `value` and `Transfer` logs in the receipt (see step 4) |

<Steps>
<Step title="Identify Users by Wallet Address">
Expand Down Expand Up @@ -366,6 +352,35 @@ Builder Codes tell you which onchain transactions came from your app. To measure
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.
</Step>

<Step title="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`.

```typescript get-attributed-volume.ts lines expandable wrap
import { erc20Abi, isAddressEqual, parseEventLogs, type Address, type Transaction, type TransactionReceipt } from "viem";

// USDC on Base
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";

// Pass the transaction and receipt fetched in step 3, and the sender of each row where success and attributed are true
export function getAttributedVolume(tx: Transaction, receipt: TransactionReceipt, sender: Address) {
const usdcTransfers = parseEventLogs({ abi: erc20Abi, eventName: "Transfer", logs: receipt.logs })
.filter((log) => isAddressEqual(log.address, USDC) && isAddressEqual(log.args.from, sender));

return {
// Native ETH sent directly by an EOA. Smart account ETH transfers need a call trace.
ethWei: isAddressEqual(tx.from, sender) ? tx.value : 0n,
// USDC uses 6 decimals: format with formatUnits(usdc, 6)
usdc: usdcTransfers.reduce((sum, log) => sum + log.args.value, 0n),
};
}
```

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.
</Step>

<Step title="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:

Expand Down
2 changes: 1 addition & 1 deletion docs/specifications/builder-codes/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Each code has associated metadata. Onchain metadata primarily includes a "payout
## Benefits

- **Rewards:** If your app drives transactions, Builder Codes let Base automatically attribute that usage back to you, unlocking rewards as the program expands.
- **Analytics:** Base Dashboard shows spot trading volume, borrow TVL and lending TVL derived from your Builder Code activity. To measure users, retention and conversion yourself, see [Track User Analytics](/specifications/builder-codes/for-app-developers#track-user-analytics).
- **Analytics:** Base Dashboard shows spot trading volume, borrow TVL and lending TVL derived from your Builder Code activity. To measure users, retention, conversion and the ETH or USDC volume your attributed transactions move yourself, see [Track User Analytics](/specifications/builder-codes/for-app-developers#track-user-analytics).
- **Visibility:** Apps with Builder Codes can show up in discovery surfaces like App Leaderboards, Base App store, and ecosystem spotlights.

## FAQ
Expand Down
Loading