Skip to content

[Bug] Adapter initialize() is unauthenticated, front-runnable #505

Description

@collinsezedike

Description

BlendAdapter::initialize() (packages/contracts/blend-adapter/src/lib.rs:148-161) sets the adapter's vault, pool, and USDC addresses with no require_auth() call anywhere in the function, only a re-initialization guard (AlreadyInitialized). The same pattern exists in DefindexAdapter::initialize() (packages/contracts/defindex-adapter/src/lib.rs:67-79).

Anyone can call initialize() on a freshly deployed, uninitialized adapter and set themselves as the vault address, becoming the sole party able to invoke deposit/withdraw on it.

This is exploitable in practice because deploy and initialize happen as separate transactions. scripts/deploy-testnet.sh deploys the adapter at line 74 and only initializes it later at lines 84-86, with the mUSDC asset deploy step in between. Anyone watching the ledger in that window could front-run the real initialize call with their own vault address.

Steps to Reproduce

  1. Deploy a BlendAdapter (or DefindexAdapter) contract without initializing it.
  2. Before the legitimate deployer calls initialize(), call initialize() yourself with an address you control as vault.
  3. Observe that the legitimate initialize() call now fails with AlreadyInitialized, and the adapter is permanently bound to the attacker's address.

Expected Behavior

initialize() should require authorization from the intended deployer/admin, and deployment should minimize any window where an uninitialized adapter sits exposed.

Actual Behavior

Any address can call initialize() on an uninitialized adapter with no authorization check, permanently claiming it. The deploy script also leaves a multi-step window between deploying the adapter and initializing it.

Environment

Field Value
Network testnet
Wallet N/A
Protocol affected Blend, DeFindex
Browser (if frontend) N/A
Node.js version N/A
pnpm version N/A

Possible Cause / Fix

Two changes, both in this issue:

  1. Add require_auth() on the deployer/admin address as a parameter to initialize() in both BlendAdapter and DefindexAdapter, matching the pattern MeridianVault::initialize already uses. This is the real fix: it closes the vulnerability in the contract itself, for every deployment path, not just the current script.
  2. Tighten scripts/deploy-testnet.sh so each adapter is initialized immediately after its own deploy call, with no other operations (like the mUSDC asset deploy) in between. This doesn't replace the contract fix, but it shrinks the exposure window as cheap defense in depth, including for any deployment that happens before the contract fix ships.

Additional Context

Found during an independent audit of the adapter contracts, verified directly against current source (packages/contracts/blend-adapter/src/lib.rs, packages/contracts/defindex-adapter/src/lib.rs, scripts/deploy-testnet.sh) rather than taken on the audit's word alone. Same gap exists in both adapters, filing as one issue since the fix is the same shape in each.

Metadata

Metadata

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignbugSomething isn't workingcontractsInvolves writing or testing Rust/Soroban contracts in packages/contractshardComplex implementation spanning multiple packages or involving Soroban contractssecuritySecurity hardening, vulnerability fixes, or audit-related worksorobanInvolves Soroban smart contract invocations or Soroban RPC calls

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions