Skip to main content
Attribution links on-chain deposit and withdrawal volume back to your integration — enabling fee sharing and volume reporting in the Developer Portal. The SDK embeds attribution automatically on every transaction it builds.

Setup

You must request a builder code from Gauntlet — it is not self-serve. Request one as part of your partnership onboarding. The Gauntlet indexer must recognize your specific code for volume to be counted against your integration; using an arbitrary string will append bytes to calldata but attribution will not be tracked. Your builder code is separate from your API key — you may receive them together or at different stages of onboarding. Once you have it, set builderCode on GauntletClient:
import { GauntletClient, AttributionMode } from '@gauntlet-xyz/sdk'

const client = new GauntletClient({
  evmClients: { ... },
  wallet: walletClient,
  builderCode: 'your-builder-code',          // issued by Gauntlet — request from the partnerships team
  attributionMode: AttributionMode.PUBLIC,   // default — no need to set explicitly
})
Without builderCode, AttributionMode.PUBLIC appends nothing and transactions are unattributed.

How It Works

The SDK encodes attribution as an ERC-8021 calldata suffix appended to every transaction. It does not affect contract execution — the Gauntlet indexer reads it to attribute the volume. ERC-8021 format:
0x{builderCode as UTF-8 hex}{byte length (1 byte)}{schema ID: 00}{16-byte marker: "8021" × 8}
For example, builder code "acme" (4 bytes) becomes 0x61636d650400 followed by the 16-byte marker 80218021802180218021802180218021.

Sending Transactions

How attribution is carried depends on which submission path you use.

Path 1 — step.payload + sendTransaction

Attribution is baked into payload.data — the full calldata is already {ABI-encoded call}{attribution suffix}. The wallet receives a single opaque hex string and sends it as-is. Attribution cannot be lost regardless of what wallet or provider you use.
for (const step of steps) {
  // payload.data = ABI calldata + attribution suffix, pre-concatenated
  await walletClient.sendTransaction(step.payload)
}
Best for: backend scripts, server-side signing, embedded wallets (Privy, Dynamic, passkey signers), any flow that uses eth_sendRawTransaction directly, or anywhere you want eth_call pre-simulation on the exact calldata that will be broadcast.

Path 2 — step.tx + writeContract

Attribution is in step.tx.attribution as standalone bytes. You must pass it as dataSuffix to writeContract — wagmi appends it to the ABI-encoded calldata before sending. If dataSuffix is omitted or the EIP-1193 provider strips it, the transaction succeeds on-chain but volume is not attributed.
for (const step of steps) {
  await walletClient.writeContract({
    address: step.tx.address,
    abi: step.tx.abi,
    functionName: step.tx.functionName,
    args: step.tx.args,
    account: step.tx.account,
    dataSuffix: step.tx.attribution, // ← required for attribution; omitting silently drops it
  })
}
Best for: browser wallets via wagmi (MetaMask, Coinbase Wallet, WalletConnect), or when you need wagmi’s type-safe simulation hooks (simulateContract). Note: Most major EIP-1193 providers used with wagmi honor dataSuffix. If you’re using a custom or obscure provider, verify it forwards unknown WriteContractParameters fields before relying on this path for attribution.

Attribution Modes

enum AttributionMode {
  PUBLIC  = 'public',   // ERC-8021 builder code appended — default
  ENCODED = 'encoded',  // not yet implemented
  PRIVATE = 'private',  // not yet implemented
}
Currently only PUBLIC is supported. ENCODED and PRIVATE throw UnimplementedFeatureError if used.

Monitor Attributed Activity

Monitoring attribution is under development and not currently available.

Initialize

import { GauntletClient } from '@gauntlet-xyz/sdk'

const client = new GauntletClient({
  apiKey: process.env.GAUNTLET_API_KEY,
})

Query Attributed Activity

API
const API_BASE_URL = 'https://api.gauntlet.xyz'
const API_KEY = process.env.GAUNTLET_API_KEY!

const { data: txns } = await fetch(
  `${API_BASE_URL}/v1/users/0xUserWallet/transactions?limit=20`,
  { headers: { Authorization: `Bearer ${API_KEY}` } },
).then(r => r.json())
// returns:
// [
//   {
//     type: "deposit",
//     vault_id: "8453:0x000000000001cdb57e58fa75fe420a0f4d6640d5", // chain_id:address
//     amount: "1000.00",
//     timestamp: "2026-01-15T10:30:00Z"
//   },
//   ...
// ]

const { data: events } = await fetch(
  `${API_BASE_URL}/v1/events?type=deposit&limit=100`,
  { headers: { Authorization: `Bearer ${API_KEY}` } },
).then(r => r.json())
// returns:
// [
//   {
//     vault_id: "8453:0x000000000001cdb57e58fa75fe420a0f4d6640d5",
//     amount: "1000.00",
//     sender: "0x...",
//     timestamp: "2026-01-15T10:30:00Z"
//   },
//   ...
// ]
Use the vault’s chain_id:address string (e.g. gtBTC on Ethereum is 1:0xeff0ae5b39271b33f448cd408b51dc8aa72a672b) to filter events by vault: GET /v1/events?vault_id=1:0xeff0ae5b39271b33f448cd408b51dc8aa72a672b. Use transactions for user-facing activity and confirmations. Use events for monitoring, reconciliation, and reporting.

Trend Activity Over Time

API
const params = new URLSearchParams({
  field: 'amount',
  agg: 'sum',
  grain: 'day',
  type: 'deposit',
  limit: '30',
})

const { data: points } = await fetch(
  `${API_BASE_URL}/v1/events/timeseries?${params}`,
  { headers: { Authorization: `Bearer ${API_KEY}` } },
).then(r => r.json())
// returns:
// [
//   { timestamp: "2026-03-01", value: "1500000" },
//   { timestamp: "2026-03-02", value: "1200000" },
//   ...
// ]

Monitoring Options

ApproachBest for
APIProduct analytics, reporting, reconciliation — fastest path
Developer PortalInternal ops review and quick checks
On-chain verificationTrustless audit or custom indexers

On-Chain Verification

For trustless monitoring, read events directly from the gtUSDa vault on Base:
import { createPublicClient, http, parseAbiItem } from 'viem'
import { base } from 'viem/chains'

const client = createPublicClient({
  chain: base,
  transport: http(process.env.RPC_URL_BASE!),
})

const depositEvent = parseAbiItem(
  'event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)'
)

const GTUSDA_VAULT = '0x000000000001CdB57E58Fa75Fe420a0f4D6640D5'

const logs = await client.getLogs({
  address: GTUSDA_VAULT,
  event: depositEvent,
  fromBlock: 'latest',
})
// returns:
// [
//   {
//     args: {
//       sender: "0x...",
//       assets: 1000000n,
//       shares: 968000000000000000000n
//     },
//     blockNumber: 12345678n,
//     transactionHash: "0x..."
//   }
// ]

What’s Next

Attribution without SDK

Add attribution manually using Privy embedded wallets or wagmi.

SDK Reference

Full result shape, type definitions, and error reference.

Deposit Your First Dollar

SDK-based deposit and withdrawal flow using the Gauntlet SDK.

Deployed Vaults

gtUSDa contract addresses for on-chain verification.