Skip to content

fix: enforce real onchain balance checks in airdrop adapters#2128

Merged
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-airdrop-mock-balance
Apr 6, 2026
Merged

fix: enforce real onchain balance checks in airdrop adapters#2128
Scottcjn merged 1 commit intoScottcjn:mainfrom
createkr:feat/issue-airdrop-mock-balance

Conversation

@createkr
Copy link
Copy Markdown
Contributor

@createkr createkr commented Apr 6, 2026

Fix: replace hardcoded mock balances with real RPC calls in chain adapters

Problem

SolanaAdapter and BaseAdapter returned hardcoded constant balances (0.2 SOL, 0.02 ETH) and ages (10 days, 14 days) from get_balance() and get_wallet_age(), ignoring the wallet address parameter. Any syntactically valid address passed eligibility checks regardless of actual on-chain state.

Solution

Replaced the four mock methods with actual JSON-RPC calls using the existing reqwest dependency:

Method Before After
SolanaAdapter::get_balance() Ok(200_000_000) POST getBalance → parse result.value
SolanaAdapter::get_wallet_age() Ok(10 * 86400) Ok(0) (conservative)
BaseAdapter::get_balance() Ok(20_000_000_000_000_000) POST eth_getBalance → parse hex result
BaseAdapter::get_wallet_age() Ok(14 * 86400) Ok(0) (conservative)

Wallet age returns 0 because determining age requires historical transaction data (Solana getSignaturesForAddress, Base Basescan API), which is significantly more complex than a single RPC call. Returning 0 is the safe default — wallets fail the age gate unless the policy minimum is set to 0.

Key changes

  • src/chain_adapter.rs — replaced 4 mock methods with real RPC calls + proper error handling; added 6 new unit tests using mockito HTTP mocking

Tests

  • 6 new unit tests (mocked HTTP for balance parsing, zero-balance handling, age defaults)
  • All 49 tests pass with --features sqlite-store
  • All 46 tests pass without the feature

Compatibility

  • No API changes — ChainAdapter trait signatures unchanged
  • No new dependencies — uses existing reqwest
  • Behavioral change: wallets with zero/low balance now correctly fail eligibility (intended)
  • Wallet age gate now conservative (0); wallets fail age check unless policy min is 0

@github-actions github-actions bot added the BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) label Apr 6, 2026
@createkr
Copy link
Copy Markdown
Contributor Author

createkr commented Apr 6, 2026

For bounty payout, please use RTC wallet: RTC1d48d848a5aa5ecf2c5f01aa5fb64837daaf2f35.

@github-actions github-actions bot added the size/L PR: 201-500 lines label Apr 6, 2026
@Scottcjn
Copy link
Copy Markdown
Owner

Scottcjn commented Apr 6, 2026

Reviewed. Replaces hardcoded mock balances (0.2 SOL, 0.02 ETH) with real RPC calls in Solana and Base adapters. Sets get_wallet_age() to 0 (conservative default) instead of mock 10/14 days that trivially bypassed the age gate. Includes mockito-based HTTP tests for both chains.

Payment: 65 RTC — Airdrop adapter real balance enforcement (High severity)

Merging. Thank you createkr.

@Scottcjn Scottcjn merged commit ba9e7f0 into Scottcjn:main Apr 6, 2026
8 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

BCOS-L1 Beacon Certified Open Source tier BCOS-L1 (required for non-doc PRs) size/L PR: 201-500 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants