# A registered contract won't earn fees if _recipient is a fresh address

Turnstile.register mints NFT to a fresh recipient, but consensus-layer RegisterEvent rejects nonexistent accounts without reverting, so the contract never earns fees and cannot re-register.

- Payout: Contest reward
- Severity: high
- Program: Canto
- Credited: Jeiwan
- Disclosed: 2023-03-31
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-11-canto-findings/issues/93
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-canto%3AH-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:go tech:solidity technique:chained technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Turnstile.sol register() and CSR module RegisterEvent handler
**Root cause:** Application layer allows any nonzero recipient, but consensus-layer RegisterEvent requires the recipient account to already exist in the EVM state store; the error is swallowed in PostTxProcessing so the tx does not revert.
**Steps:**
1. Deploy a contract that calls Turnstile.register(_recipient) with a fresh address (no coins, no tx, no code).
2. register() mints the NFT and sets feeRecipient[smartContract], emitting Register.
3. Consensus RegisterEvent calls evmKeeper.GetAccount(ctx, event.Recipient); returns nil for fresh account.
4. Handler returns ErrNonexistentAcct, but PostTxProcessing does not revert the tx.
5. Contract is marked registered on-chain but never registered in CSR keeper; onlyUnregistered blocks re-registration.
**How found:** Compared the Solidity register() recipient check (only != address(0)) against the Go consensus-layer RegisterEvent check requiring an existing account, then traced GetAccount through Ethermint/Cosmos SDK to confirm fresh accounts return nil and that event-processing errors don't revert.
**Exploitation:** No attacker needed; a user self-inflicts by choosing a fresh recipient address, permanently losing fee eligibility for that contract since register/assign are one-shot.
**Insight:** When a contract emits events consumed by a separate consensus module, diff the app-layer validation against the module's handler validation; silent handler errors create permanent state divergence that one-shot registration functions cannot recover from.
