feat: prevent sending tokens to warp route and token contract addresses#919
feat: prevent sending tokens to warp route and token contract addresses#919nambrot-agent wants to merge 3 commits intomainfrom
Conversation
Prevent users from accidentally sending tokens to contract addresses on the destination chain by validating the recipient address against: - Warp route contract addresses - Collateral token contract addresses (e.g., USDC) - Well-known blocked addresses (null, precompiles, burn addresses) Validation shows descriptive inline error messages explaining why the address is blocked.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
5 Skipped Deployments
|
📝 WalkthroughWalkthroughAdds a blocked-recipient system: a blacklist constant, helper to build per-chain blocked-address maps from token configs, store plumbing to persist those maps, tests for the helper, and transfer form validation updated to consult the new maps. (50 words) Changes
Sequence Diagram(s)sequenceDiagram
participant App as App Init
participant Store as Store
participant Helper as getBlockedAddressesByChain
participant Form as TransferTokenForm
participant Validation as Validation Logic
App->>Store: initWarpContext(tokenConfig)
Store->>Helper: getBlockedAddressesByChain(tokens)
Helper->>Helper: collect warp route addresses per chain
Helper->>Helper: collect collateral addresses per chain
Helper->>Helper: merge BLOCKED_RECIPIENT_ADDRESSES (lowercased where applicable)
Helper->>Store: return blockedAddressesByChainMap
Store->>Form: provide blockedAddressesByChainMap
Note over Form: user enters recipient and destination chain
Form->>Validation: validate recipient
Validation->>Validation: normalize recipient (lowercase for 0x)
Validation->>Store: lookup blockedAddressesByChainMap[chain].get(recipient)
alt recipient blocked
Validation->>Form: return reason (blocked label)
Form->>Form: show error / prevent submit
else recipient allowed
Validation->>Form: proceed with transfer
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/features/store.ts`:
- Around line 317-324: The addBlockedAddress helper currently lowercases every
address which can collapse distinct case‑sensitive formats; change the
normalization to only lowercase hex EVM addresses (e.g., addresses starting with
"0x" or matching a hex pattern) and leave non‑hex addresses unchanged, and apply
the same normalization change in TransferTokenForm so both places use identical
logic; keep the existing "first reason wins" behavior using result[chain] and
result[chain].has/ set.
src/features/store.ts
Outdated
| // Helper to add address to the map (case-insensitive for EVM compatibility) | ||
| const addBlockedAddress = (chain: ChainName, address: string, reason: string) => { | ||
| result[chain] ||= new Map<string, string>(); | ||
| const normalizedAddress = address.toLowerCase(); | ||
| // Don't overwrite existing entries (first reason wins) | ||
| if (!result[chain].has(normalizedAddress)) { | ||
| result[chain].set(normalizedAddress, reason); | ||
| } |
There was a problem hiding this comment.
Normalize only hex addresses to avoid false positives.
Line 320 lowercases every address. For case‑sensitive formats (e.g., base58), that can collapse distinct addresses and block legit recipients. I’d only case‑fold hex (0x) addys, and mirror the same logic in TransferTokenForm (Line 856).
🔧 Suggested fix
- const normalizedAddress = address.toLowerCase();
+ const normalizedAddress = /^0x[0-9a-fA-F]{40}$/.test(address)
+ ? address.toLowerCase()
+ : address;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Helper to add address to the map (case-insensitive for EVM compatibility) | |
| const addBlockedAddress = (chain: ChainName, address: string, reason: string) => { | |
| result[chain] ||= new Map<string, string>(); | |
| const normalizedAddress = address.toLowerCase(); | |
| // Don't overwrite existing entries (first reason wins) | |
| if (!result[chain].has(normalizedAddress)) { | |
| result[chain].set(normalizedAddress, reason); | |
| } | |
| // Helper to add address to the map (case-insensitive for EVM compatibility) | |
| const addBlockedAddress = (chain: ChainName, address: string, reason: string) => { | |
| result[chain] ||= new Map<string, string>(); | |
| const normalizedAddress = /^0x[0-9a-fA-F]{40}$/.test(address) | |
| ? address.toLowerCase() | |
| : address; | |
| // Don't overwrite existing entries (first reason wins) | |
| if (!result[chain].has(normalizedAddress)) { | |
| result[chain].set(normalizedAddress, reason); | |
| } |
🤖 Prompt for AI Agents
In `@src/features/store.ts` around lines 317 - 324, The addBlockedAddress helper
currently lowercases every address which can collapse distinct case‑sensitive
formats; change the normalization to only lowercase hex EVM addresses (e.g.,
addresses starting with "0x" or matching a hex pattern) and leave non‑hex
addresses unchanged, and apply the same normalization change in
TransferTokenForm so both places use identical logic; keep the existing "first
reason wins" behavior using result[chain] and result[chain].has/ set.
Address CodeRabbit feedback: non-EVM addresses like base58 (Solana) are case-sensitive and should not be lowercased. Now only addresses matching the hex pattern (0x[0-9a-fA-F]+) are normalized to lowercase.
| const addBlockedAddress = (chain: ChainName, address: string, reason: string) => { | ||
| result[chain] ||= new Map<string, string>(); | ||
| const normalizedAddress = /^0x[0-9a-fA-F]+$/i.test(address) ? address.toLowerCase() : address; | ||
| // Don't overwrite existing entries (first reason wins) | ||
| if (!result[chain].has(normalizedAddress)) { | ||
| result[chain].set(normalizedAddress, reason); | ||
| } |
There was a problem hiding this comment.
why not just normalizeAddress from our utils?
| const normalizedRecipient = /^0x[0-9a-fA-F]+$/i.test(recipient) | ||
| ? recipient.toLowerCase() | ||
| : recipient; |
Summary
Prevents users from accidentally sending tokens to the warp route contract address on the destination chain, which is a common mistake that results in lost funds. Also blocks other contract addresses and well-known invalid recipients.
Problem
When bridging tokens (e.g., from Ethereum to Base), users sometimes mistakenly enter the warp route contract address or token contract address as the recipient instead of their wallet address. This results in tokens being permanently lost.
Solution
Validates the recipient address against a blocklist that includes:
User Experience
When a user enters a blocked address as the recipient, they see a descriptive inline error message:
Changes
BLOCKED_RECIPIENT_ADDRESSESconstant for well-known invalid addressesblockedAddressesByChainMapto store state, built from warp route configTesting