T09 · Insecure Skill Coding Practices
Error
- Location
- onboard.ts:17
- Finding
- Value-Bearing Transactions Are Sent to a Placeholder Zero Address<![CDATA[ ## Vulnerability Details **File Location**: `onboard.ts:17-19`, `onboard.ts:145-161`, `onboard.ts:182-188`, `onboard.ts:278-284` **Vulnerability Type**: Unsafe blockchain configuration and error suppression **Risk Level**: High ### Vulnerable Code ```ts const CONFIG = { // TODO: Replace with actual deployed addresses FREN_REGISTRY: '0x0000000000000000000000000000000000000000' as `0x${string}`, FRENCOIN_FACTORY: '0x0000000000000000000000000000000000000000' as `0x${string}`, MAGIC_TOKEN: '0x0000000000000000000000000000000000000000' as `0x${string}`, ``` The preliminary registry failure is ignored: ```ts try { const existingFrenId = await publicClient.readContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'getFrenByOwner', args: [account.address] }); if (existingFrenId > 0n) { console.log(`\n⚠️ You're already registered as Fren #${existingFrenId}`); console.log('Run "check-status" to see your Fren details.'); return; } } catch (e) { // Not registered yet, continue } ``` The script then submits a value-bearing transaction to that address: ```ts const hash = await walletClient.writeContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'registerFren', args: [options.name, options.bio, metadata], value: CONFIG.CREATION_FEE_ETH, }); ``` The treasury operation uses the same invalid destination: ```ts const hash = await walletClient.writeContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'claimTreasury', args: [amountWei] }); ``` ### Technical Analysis The onboarding implementation uses the Ethereum zero address as the registry destination even though the operation transfers a configured creation fee of `0.01 ETH`. A transaction to an address without contract bytecode does not execute the declared ABI function. A value-bearing transaction may nevertheless be accepted by the network and transfer the attach ...[truncated 2210 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Replace every placeholder with a verified, deployed Base contract address before exposing transaction commands. 2. Reject the zero address explicitly: ```ts if (CONFIG.FREN_REGISTRY === zeroAddress) { throw new Error('FREN_REGISTRY is not configured'); } ``` 3. Query `publicClient.getBytecode()` and refuse to proceed unless the destination contains contract bytecode. 4. Verify the connected chain ID before signing any transaction. 5. Do not interpret arbitrary registry-read failures as “not registered.” Continue only when the contract returns a valid, explicit unregistered result. 6. Simulate the contract call before submission and verify the expected return behavior. 7. Present the destination, value, chain, and decoded function call to the user for explicit approval. 8. After confirmation, validate emitted registration events and query the registry state instead of treating receipt status alone as proof that all advertised resources were created. 9. Add automated tests that reject zero addresses, EOAs, wrong-chain contracts, malformed RPC responses, and failed registry reads. ]]>
