Skip to main content

What are Builder Codes

Base Builder Codes are an ERC-721 NFT collection where unique codes (e.g. “abc123”) are minted to help identify builders onchain. Each code has associated metadata. Onchain metadata primarily includes a “payout address” where each code declares where potential rewards should be sent to. Offchain metadata includes more details about the app including its name and site.

Automatic Builder-Code Attribution on Base

Once your app is registered on Base.dev, the Base App will auto-append your Base Builder Code to transactions its users make in your app (e.g. via your mini app, or the Base App’s browser). This powers your onchain analytics in Base.dev and qualifies you for potential future rewards.
Builder Code analytics currently only support Smart Account (AA) transactions. EOA support is coming soon. Your attribution data is preserved and will appear once EOA support is activated.

For App Developers

When you register on base.dev, you will receive a Builder Code—a random string (e.g., k3p9da) that you’ll use to generate your attribution suffix. You can find your code anytime under SettingsBuilder Code.
Manual appending outside of the Base app is coming soon.

For Wallet Developers

Wallet providers need to support the dataSuffix capability to enable attribution. This involves accepting the capability and appending the suffix to the calldata before signing.
1

Support the dataSuffix Capability

Your wallet should accept a dataSuffix string in the capabilities object of wallet_sendCalls.
interface DataSuffixCapability {
  dataSuffix: string; // hex-encoded bytes provided by the app
}
2

Append Suffix to Calldata

When constructing the transaction or User Operation, extract the dataSuffix and append it to the calldata.
Append to tx.data.
// Minimal example for EOA
function applySuffixToEOA(tx, capabilities) {
  const suffix = capabilities.find(c => c.dataSuffix)?.dataSuffix
  if (!suffix) return tx

  return {
    ...tx,
    // Append suffix bytes (remove 0x prefix from suffix if tx.data has it)
    data: tx.data + suffix.slice(2)
  }
}
3

(Optional) Add Wallet Attribution

Wallets may also include their own attribution code (their own ERC-8021 suffix) by simply prepending the wallet’s own suffix before the app’s.
  • No interaction required with apps: The wallet handles this independently.
  • Multi-code support: ERC-8021 natively supports multiple attribution codes.
Example:
finalSuffix = walletSuffix + appSuffix
This ensures both the app and the wallet receive onchain attribution.

For Base-Solana Bridge Developers

Onchain Builder codes are currently still not live on mainnet.
Builder codes work with the Base-Solana bridge via the hookData mechanism. Currently available for Solana → Base flows only.
1

Get your Builder Code

When you register on base.dev, you will find a Builder Code under your app’s settings. This is a random string (e.g., k3p9da) that you will use to generate your attribution suffix.
2

Build hookData

ABI-encode the user address, your code, and fee:
bytes memory hookData = abi.encode(
  0xUSER,           // destination address on Base (NOT the Twin)
  0xBUILDER_CODE,   // your builder code in type string memory
  100               // feeBps (100 = 1%)
);
3

Attach to Bridge Message

Set to = BRIDGE_CAMPAIGN_ADDRESS and attach a call to Flywheel.send.
For a bridge with no follow-up call:
to:     <BRIDGE_CAMPAIGN_ADDRESS> // 0xb61A842E4361C53C3f3c376DF3758b330BD6201c on Base Sepolia
amount: 100
call:
  ty:    Call
  to:    <FLYWHEEL_ADDRESS> // 0x00000f14ad09382841db481403d1775adee1179f on Base Sepolia
  value: 0
  data:  abi.encodeWithSelector(
           Flywheel.send.selector,
           <BRIDGE_CAMPAIGN_ADDRESS>, // 0xb61A842E4361C53C3f3c376DF3758b330BD6201c on Base Sepolia
           <wSOL_ADDRESS>,
           hookData
         )
4

Learn More: A Full Implementation Example

Terminally Onchain is a production Next.js app that exposes the bridge via a command terminal UI. Users connect a Solana wallet, type commands such as to bridge and call a contract on Base:You can use Terminally Onchain to test bridge transactions with Builder Codes like so:
bridge 0.0001 sol 0xYOUR_DESTINATION_ADDRESS --with-bc YOUR_BUILDER_CODE --bc-fee YOUR_FEE_BPS
To see how this is implemented, you can take a look at the Github repo:
sol2base/MainContent.tsx
// Constants
const BRIDGE_CAMPAIGN_ADDRESS = "0xb61A842E4361C53C3f3c376DF3758b330BD6201c";
const FLYWHEEL_ADDRESS = "0x00000f14ad09382841db481403d1775adee1179f";
const MULTICALL_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";

// Encode hookData: (address user, string code, uint8 feeBps)
const buildBuilderHookData = (
  destination: string,
  builderCode: string,
  feeBps: number
) => {
  const coder = new AbiCoder();
  return coder.encode(
    ["address", "string", "uint8"],
    [destination, builderCode, feeBps]
  );
};

// Build Flywheel.send call
const buildBuilderCall = (
  destination: string,
  builderCode: string,
  feeBps: number,
  tokenAddress: string
): BaseContractCall => {
  const hookData = buildBuilderHookData(destination, builderCode, feeBps);
  const data = encodeFunctionData({
    abi: FLYWHEEL_ABI,
    functionName: "send",
    args: [BRIDGE_CAMPAIGN_ADDRESS, tokenAddress, hookData],
  });
  return { type: "call", target: FLYWHEEL_ADDRESS, value: "0", data };
};

// Wrap builder + user calls in Multicall (for chained calls)
const buildMulticall = (
  builder: BaseContractCall,
  userCall: BaseContractCall
): BaseContractCall => {
  const data = encodeFunctionData({
    abi: MULTICALL_ABI,
    functionName: "multicall",
    args: [
      [
        { target: builder.target, callData: builder.data },
        { target: userCall.target, callData: userCall.data },
      ],
    ],
  });
  return { type: "delegatecall", target: MULTICALL_ADDRESS, value: "0", data };
};

// Usage: attach builder code to bridge
if (payload.flags.withBc) {
  const builderCall = buildBuilderCall(
    destination,
    builderCode,
    feeBps,
    remoteToken
  );
  callOption = userCall ? buildMulticall(builderCall, userCall) : builderCall;
}

// Set destination to campaign address when using builder codes
const destinationForBridge = builderCode
  ? BRIDGE_CAMPAIGN_ADDRESS
  : payload.destination;
Mainnet support for Builder Codes is not open to all builders yet. If you are interested in getting access, please contact the team on discord.

Give feedback!

Base is constantly working to improve the Builder Codes experience. If you have any feedback, please let the team know here.