|
| 1 | +# Builder Codes Implementation |
| 2 | + |
| 3 | +# Implementation for Apps |
| 4 | +## **1\. Get your Builder Code \+ Suffix** |
| 5 | + |
| 6 | +When you register on **base.dev**, we give you: |
| 7 | + |
| 8 | +* **Builder Code** → a random string (e.g., `"k3p9da"`) |
| 9 | +* **ERC-8021 Suffix** → already fully encoded as a hex string (e.g., `"0x…"`) |
| 10 | + |
| 11 | +You do **not** build, validate, or encode anything — you just use the suffix we provide. |
| 12 | + |
| 13 | +## **2\. Append the suffix to all transactions** |
| 14 | + |
| 15 | +### **A. EOA Transactions** |
| 16 | + |
| 17 | +If your app sends a normal `eth_sendTransaction`: |
| 18 | + |
| 19 | +`tx.data = tx.data + dataSuffix;` |
| 20 | + |
| 21 | +### **B. Smart Account / ERC-4337 UserOps** |
| 22 | + |
| 23 | +If your app constructs User Operations: |
| 24 | + |
| 25 | +`userOp.callData = userOp.callData + dataSuffix;` |
| 26 | + |
| 27 | +Smart accounts require putting the suffix inside the `callData` of the userOp. |
| 28 | + |
| 29 | +### **C. Using `wallet_sendCalls` (ERC-5792) — Recommended** |
| 30 | + |
| 31 | +The simplest and most reliable way. |
| 32 | + |
| 33 | +Pass the suffix through the capability: |
| 34 | + |
| 35 | +`{` |
| 36 | + `"capabilities": [` |
| 37 | + `{ "dataSuffix": "0x..." }` |
| 38 | + `]` |
| 39 | +`}` |
| 40 | + |
| 41 | +Wallets automatically attach it to the correct place (EOA or 4337). |
| 42 | + |
| 43 | +## **3\. No other changes needed** |
| 44 | + |
| 45 | +You **do not** need to: |
| 46 | + |
| 47 | +* Understand the random code |
| 48 | +* Decode or encode suffix bytes |
| 49 | +* Check schema IDs |
| 50 | +* Interact with the registry |
| 51 | +* Change your smart contracts |
| 52 | +* Modify backend systems |
| 53 | + |
| 54 | +**Just attach the suffix. Everything else is standardized and handled by wallets \+ Base.dev.** |
| 55 | + |
| 56 | +## **🧪 Minimal Example** |
| 57 | + |
| 58 | +`const calls = [{` |
| 59 | + `to: contract,` |
| 60 | + `data: calldata` |
| 61 | +`}];` |
| 62 | + |
| 63 | +`wallet_sendCalls({` |
| 64 | + `calls,` |
| 65 | + `capabilities: [` |
| 66 | + `{ dataSuffix } // provided by base.dev` |
| 67 | + `]` |
| 68 | +`});` |
| 69 | + |
| 70 | +## **🎉 You’re Done** |
| 71 | + |
| 72 | +Once you add the suffix: |
| 73 | + |
| 74 | +* Your app is fully ERC-8021 compliant |
| 75 | +* Your activity is attributed onchain |
| 76 | +* You unlock analytics, rewards, rankings, and future incentives |
| 77 | +* Wallets and chains can reliably recognize your app |
| 78 | + |
| 79 | +**We give you a random code.** |
| 80 | + **We give you the suffix.** |
| 81 | + **You attach it.** |
| 82 | + **That’s it.** |
| 83 | + |
| 84 | +## **⚡️ Simple Integration Guide (Wagmi \+ ERC-8021 Capability)** |
| 85 | + |
| 86 | +Below is a dead-simple example of how an app attaches the ERC-8021 suffix using wagmi. |
| 87 | + |
| 88 | +Assume: |
| 89 | + |
| 90 | +`const dataSuffix = "0x1234abcd..."; // provided by base.dev` |
| 91 | + |
| 92 | +## **1\. Using wagmi’s `sendCalls` (ERC-5792)** |
| 93 | + |
| 94 | +*(Recommended path — shortest, safest, wallet-native)* |
| 95 | + |
| 96 | +`import { useSendCalls } from 'wagmi/experimental'` |
| 97 | + |
| 98 | +`const dataSuffix = "0x1234abcd..." // provided by base.dev` |
| 99 | + |
| 100 | +`function SendTx() {` |
| 101 | + `const { sendCalls } = useSendCalls()` |
| 102 | + |
| 103 | + `async function submit() {` |
| 104 | + `await sendCalls({` |
| 105 | + `calls: [` |
| 106 | + `{` |
| 107 | + `to: "0xYourContract",` |
| 108 | + `data: "0xYourCalldata"` |
| 109 | + `}` |
| 110 | + `],` |
| 111 | + `capabilities: [` |
| 112 | + `{` |
| 113 | + `dataSuffix // ERC-8021 capability` |
| 114 | + `}` |
| 115 | + `]` |
| 116 | + `})` |
| 117 | + `}` |
| 118 | + |
| 119 | + `return <button onClick={submit}>Send</button>` |
| 120 | +`}` |
| 121 | + |
| 122 | +That’s it. No suffix encoding, no registry lookups, no 4337 branching — wagmi \+ the wallet handle all of it. |
| 123 | + |
| 124 | +## **2\. If you’re using viem directly** |
| 125 | + |
| 126 | +`import { walletClient } from './client'` |
| 127 | + |
| 128 | +`const dataSuffix = "0x1234abcd..."` |
| 129 | + |
| 130 | +`await walletClient.sendCalls({` |
| 131 | + `calls: [` |
| 132 | + `{` |
| 133 | + `to: "0xYourContract",` |
| 134 | + `data: "0xYourCalldata"` |
| 135 | + `}` |
| 136 | + `],` |
| 137 | + `capabilities: [` |
| 138 | + `{ dataSuffix }` |
| 139 | + `]` |
| 140 | +`})` |
| 141 | + |
| 142 | +## **3\. If you’re still using `writeContract` (EOA only)** |
| 143 | + |
| 144 | +*(Not recommended, but supported)* |
| 145 | + You can manually append to calldata: |
| 146 | + |
| 147 | +`await writeContract({` |
| 148 | + `address: "0xYourContract",` |
| 149 | + `abi,` |
| 150 | + `functionName: "yourFn",` |
| 151 | + `args: [a, b, c],` |
| 152 | + `// WAGMI builds calldata for you; we append the suffix` |
| 153 | + `dataSuffix,` |
| 154 | +`})` |
| 155 | + |
| 156 | +If your wagmi version doesn’t expose a `dataSuffix` field, simply concatenate manually: |
| 157 | + |
| 158 | +`const encoded = encodeFunctionData({` |
| 159 | + `abi,` |
| 160 | + `functionName: "yourFn",` |
| 161 | + `args: [a, b, c],` |
| 162 | +`})` |
| 163 | + |
| 164 | +`const dataWithSuffix = encoded + dataSuffix.slice(2)` |
| 165 | + |
| 166 | +`await sendTransaction({` |
| 167 | + `to: "0xYourContract",` |
| 168 | + `data: dataWithSuffix,` |
| 169 | +`})` |
| 170 | + |
| 171 | +## **4\. ERC-4337 (UserOps) — only if you construct them manually** |
| 172 | + |
| 173 | +Almost no apps do this directly. But if they do: |
| 174 | + |
| 175 | +`userOp.callData = userOp.callData + dataSuffix.slice(2)` |
| 176 | + |
| 177 | +And send it through your bundler or wallet SDK. |
| 178 | + |
| 179 | +## **💡 Summary for builders** |
| 180 | + |
| 181 | +**You don’t need to understand ERC-8021.** |
| 182 | + **You only need your suffix.** |
| 183 | + |
| 184 | +Then: |
| 185 | + |
| 186 | +### **Using wagmi \+ 5792** |
| 187 | + |
| 188 | +`capabilities: [{ dataSuffix }]` |
| 189 | + |
| 190 | +### **Everything else happens automatically.** |
| 191 | + |
| 192 | +# Implementation for Wallets |
| 193 | +## **⚡️ ERC-8021 Integration Guide for Wallets** |
| 194 | + |
| 195 | +**Add support for attribution codes with minimal changes.** |
| 196 | + |
| 197 | +This guide shows exactly how to: |
| 198 | + |
| 199 | +1. Accept the `dataSuffix` capability from apps |
| 200 | + |
| 201 | +2. Append the suffix correctly to outgoing transactions |
| 202 | + |
| 203 | +3. Support both EOAs and ERC-4337 smart accounts |
| 204 | + |
| 205 | +4. Do all of the above with almost zero logic or schema awareness |
| 206 | + |
| 207 | +## **1\. Support the ERC-5792 `dataSuffix` capability** |
| 208 | + |
| 209 | +Wallets should accept: |
| 210 | + |
| 211 | +`interface DataSuffixCapability {` |
| 212 | + `dataSuffix: string; // hex-encoded bytes` |
| 213 | +`}` |
| 214 | + |
| 215 | +This appears inside the `capabilities` array of `wallet_sendCalls`. |
| 216 | + |
| 217 | +**What you must do:** |
| 218 | + |
| 219 | +* Look for any capability object that includes `dataSuffix` |
| 220 | + |
| 221 | +* Extract the hex string |
| 222 | + |
| 223 | +* Store it for use when constructing the transaction or user operation |
| 224 | + |
| 225 | +**No decoding, no validation, no schema handling is required.** |
| 226 | + |
| 227 | +Wallets do *not* need to understand what the suffix means—just pass it through verbatim. |
| 228 | + |
| 229 | +## **2\. Append the suffix to calldata depending on account type** |
| 230 | + |
| 231 | +Wallets only need **one conditional branch**. |
| 232 | + |
| 233 | +## **A. If the user is sending an EOA transaction** |
| 234 | + |
| 235 | +Append the suffix to the transaction `data`: |
| 236 | + |
| 237 | +`tx.data = concat(tx.data, dataSuffix)` |
| 238 | + |
| 239 | +This is literally bytes concatenation. |
| 240 | + |
| 241 | +## **B. If the user is sending an ERC-4337 user operation** |
| 242 | + |
| 243 | +Append the suffix to the **userOp.callData**, not the transaction-level calldata: |
| 244 | + |
| 245 | +`userOp.callData = concat(userOp.callData, dataSuffix)` |
| 246 | + |
| 247 | +This ensures parsers see the suffix where the call is actually executed. |
| 248 | + |
| 249 | +## **C. When using `wallet_sendCalls` (ERC-5792)** |
| 250 | + |
| 251 | +If your wallet supports ERC-5792, this is the recommended path. |
| 252 | + |
| 253 | +Steps: |
| 254 | + |
| 255 | +1. Read `capabilities[]` |
| 256 | + |
| 257 | +2. Extract `dataSuffix` |
| 258 | + |
| 259 | +3. For each call in `calls[]`: |
| 260 | + |
| 261 | + * If EOA → append to `tx.data` |
| 262 | + |
| 263 | + * If 4337 → append to `userOp.callData` |
| 264 | + |
| 265 | +That’s the entire integration. |
| 266 | + |
| 267 | +## **3\. (Optional) Wallet attribution** |
| 268 | + |
| 269 | +Wallets **may** also include *their own attribution code* (their own ERC-8021 suffix): |
| 270 | + |
| 271 | +* Simply prepend the wallet’s own suffix before the app’s |
| 272 | + |
| 273 | +* No interaction is required with apps |
| 274 | + |
| 275 | +* Multi-code support is built into ERC-8021 |
| 276 | + |
| 277 | +Example: |
| 278 | + |
| 279 | +`finalSuffix = walletSuffix + appSuffix` |
| 280 | + |
| 281 | +This is fully supported by the standard. |
| 282 | + |
| 283 | +## **4\. Error handling** |
| 284 | + |
| 285 | +Wallets **do not** need to: |
| 286 | + |
| 287 | +* Validate the encoding |
| 288 | + |
| 289 | +* Parse or interpret the suffix |
| 290 | + |
| 291 | +* Query registries |
| 292 | + |
| 293 | +* Reject malformed codes |
| 294 | + |
| 295 | +If the suffix is malformed, apps are the ones losing attribution—not the wallet. |
| 296 | + |
| 297 | +**Safe failure mode:** append whatever is provided. |
| 298 | + |
| 299 | +## **5\. Minimal example (EOA)** |
| 300 | + |
| 301 | +`function applySuffixToEOA(tx, capabilities) {` |
| 302 | + `const suffix = capabilities.find(c => c.dataSuffix)?.dataSuffix` |
| 303 | + `if (!suffix) return tx` |
| 304 | + |
| 305 | + `return {` |
| 306 | + `...tx,` |
| 307 | + `data: tx.data + suffix.slice(2)` |
| 308 | + `}` |
| 309 | +`}` |
| 310 | + |
| 311 | +## **6\. Minimal example (ERC-4337)** |
| 312 | + |
| 313 | +`function applySuffixToUserOp(userOp, capabilities) {` |
| 314 | + `const suffix = capabilities.find(c => c.dataSuffix)?.dataSuffix` |
| 315 | + `if (!suffix) return userOp` |
| 316 | + |
| 317 | + `return {` |
| 318 | + `...userOp,` |
| 319 | + `callData: userOp.callData + suffix.slice(2)` |
| 320 | + `}` |
| 321 | +`}` |
| 322 | + |
| 323 | +## **7\. Recap: The full wallet responsibility (very small)** |
| 324 | + |
| 325 | +### **✅ Accept the `dataSuffix` capability** |
| 326 | + |
| 327 | +### **✅ Append suffix to calldata** |
| 328 | + |
| 329 | +* EOAs → `tx.data` |
| 330 | + |
| 331 | +* 4337 → `userOp.callData` |
| 332 | + |
| 333 | +### **Optional: Add wallet attribution code** |
| 334 | + |
| 335 | +### **No: parsing, decoding, validation, or registry interaction** |
| 336 | + |
| 337 | +--- |
| 338 | + |
| 339 | +## **📌 TL;DR for wallet engineers** |
| 340 | + |
| 341 | +**“If a call has a `dataSuffix`, append it to calldata before sending.** |
| 342 | + **Use transaction.data for EOAs, userOp.callData for 4337\. That’s all.”** |
0 commit comments