Odos router signature-validation bug enables arbitrary token drains
Exploit Transactions
0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6Victim Addresses
0xb6333e994fd02a9255e794c177efbdeb1fe779c7Base0x833589fcd6edb6e08f4c7c32d4f71b54bda02913Base0x4200000000000000000000000000000000000006Base0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935Base0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1bBase0xcbb7c0000ab88b473b1f5afd9ef808440eed33bfBase0x940181a94a35a4569e4529a3cdfb74e38fd98631Base0xecac9c5f704e954931349da37f60e39f515c11c1Base0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22Base0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452Base0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42BaseLoss Breakdown
Similar Incidents
TSURUWrapper onERC1155Received bug mints unbacked tokens and drains WETH
34%SynapLogicErc20 Router Flash-Loan Over-Mint Exploit
34%USDC drain via unchecked Uniswap V3-style callback
31%Treasury allowance-router abuse drains USDC/USDT/WBTC cross-chain
31%MPRO Staking Proxy unwrapWETH Flash-Loan Exploit (Base)
30%Base DUCKVADER infinite mint + Uniswap drain
30%Root Cause Analysis
Odos router signature-validation bug enables arbitrary token drains
On Base (chainid 8453, block 25431001), an unprivileged EOA 0x4015d786e33c1842c3e4d27792098e4a3612fc0e invokes an attacker-controlled contract 0x22a7da241a39f189a8aec269a6f11a238b6086fc.
This contract calls the public OdosLimitOrderRouter.isValidSigImpl function multiple times with carefully crafted EIP‑6492–style signatures.
Each call causes OdosLimitOrderRouter (0xb6333e994fd02a9255e794c177efbdeb1fe779c7) to execute arbitrary ERC‑20 transfer calls from its own address, draining its entire balances in several ERC‑20 tokens directly to the attacker EOA.
At a high level, the root cause is that the router inherits UniversalSigValidator and exposes isValidSigImpl(address,bytes32,bytes,bool) as a public function. When called with EIP‑6492‑encoded signatures and allowSideEffects = true, this function performs attacker‑controlled external calls from the router (via create2Factory.call(factoryCalldata)) without access control or side‑effect restrictions, and then uses the Identity precompile to satisfy the ERC‑1271 magic value. This bypasses the router’s intended owner/liquidator‑only withdrawal mechanisms and allows full draining of router‑held funds in a single transaction.
Key Background
-
OdosLimitOrderRouter design
OdosLimitOrderRouteris an EIP‑712–based limit order router that can hold ERC‑20 balances on‑chain.- It includes owner‑only functions
swapRouterFundsandtransferRouterFundsto move router‑held funds, with an additional privilegedliquidatorAddressallowed to callswapRouterFunds. - Under normal design intent, movements of router‑held funds should only be possible via these explicitly access‑controlled functions.
-
SignatureValidator and UniversalSigValidator
-
The router inherits
SignatureValidator, which itself extendsUniversalSigValidator, an implementation of EIP‑6492 and ERC‑1271 designed to support counterfactual smart wallets. -
The key function is:
function isValidSigImpl( address _signer, bytes32 _hash, bytes calldata _signature, bool allowSideEffects ) public returns (bool)(From verified router project source, UniversalSigValidator implementation.)
This function is intended as a generic helper for validating signatures, including counterfactual deployments. -
For normal EIP‑1271 order validation, the router uses
isValidSig(account, orderHash, signature), which internally callsisValidSigImplwithallowSideEffects = false. In that path, any side effects during counterfactual validation are reverted so that signature checks cannot accidentally change state. -
However, the raw
isValidSigImplentrypoint withallowSideEffects = trueremains publicly callable on the router.
-
-
EIP‑6492 side‑effectful behavior
-
Inside
UniversalSigValidator.isValidSigImpl, when a signature is marked as counterfactual via the EIP‑6492 detection suffix, the implementation:-
Detects the suffix constant.
-
Decodes the signature into
(create2Factory, factoryCalldata, sigToValidate). -
If
address(_signer).code.length == 0, it executes:(bool success, bytes memory err) = create2Factory.call(factoryCalldata);from the caller’s context.
-
-
On
OdosLimitOrderRouter, this call executes withmsg.sender = address(this)(the router), meaning anyfactoryCalldatacan be used to perform arbitrary calls from the router’s address, including ERC‑20transferoperations of router‑held balances. -
After this call,
isValidSigImplproceeds to an ERC‑1271 check: if_signeris a contract (or treated as such in the logic), it calls:IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate)and returns
trueif the magic value0x1626ba7e(ERC1271_SUCCESS) is observed.
-
-
Public exposure of isValidSigImpl on router
- The router’s contract interface exposes
isValidSigImplas a public function via inheritance fromUniversalSigValidator. - This public entrypoint takes caller‑supplied
_signer,_hash,_signature, andallowSideEffects, and it is not restricted by any access control or scoped to limit which contracts may be called via EIP‑6492.
- The router’s contract interface exposes
Vulnerability & Root Cause Analysis
Vulnerability Brief
UniversalSigValidator.isValidSigImpl is exposed as a public function on OdosLimitOrderRouter. When an attacker invokes it with:
- A counterfactual EIP‑6492 signature (ending in the detection suffix),
- A carefully crafted
(create2Factory, factoryCalldata)pair encoded inside the signature, and allowSideEffects = true,
the router executes an arbitrary external call using create2Factory.call(factoryCalldata) from the router’s address, with no access control or limitation on side effects.
By setting create2Factory to ERC‑20 token contracts and encoding transfer calls that move the router’s entire token balance to the attacker EOA, the adversary can drain router‑held balances. The ERC‑1271 check is satisfied by using the Identity precompile as _signer, which echoes input bytes such that the first four bytes equal the ERC‑1271 success magic value.
Root Cause Detail
In the verified router source (src/Contract.sol in the cloned project for 0xb6333e994f...779c7), the UniversalSigValidator implementation is:
contract UniversalSigValidator {
bytes32 private constant ERC6492_DETECTION_SUFFIX =
0x6492649264926492649264926492649264926492649264926492649264926492;
bytes4 private constant ERC1271_SUCCESS = 0x1626ba7e;
function isValidSigImpl(
address _signer,
bytes32 _hash,
bytes calldata _signature,
bool allowSideEffects
) public returns (bool) {
uint256 contractCodeLen = address(_signer).code.length;
bytes memory sigToValidate;
bool isCounterfactual = _signature.length >= 32
&& bytes32(_signature[_signature.length-32:_signature.length]) == ERC6492_DETECTION_SUFFIX;
if (isCounterfactual) {
address create2Factory;
bytes memory factoryCalldata;
(create2Factory, factoryCalldata, sigToValidate) =
abi.decode(_signature[0:_signature.length-32], (address, bytes, bytes));
if (contractCodeLen == 0) {
(bool success, bytes memory err) = create2Factory.call(factoryCalldata);
if (!success) revert ERC6492DeployFailed(err);
}
} else {
sigToValidate = _signature;
}
if (isCounterfactual || contractCodeLen > 0) {
try IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate)
returns (bytes4 magicValue) {
bool isValid = magicValue == ERC1271_SUCCESS;
if (contractCodeLen == 0 && isCounterfactual && !allowSideEffects) {
assembly {
mstore(0, isValid)
revert(31, 1)
}
}
return isValid;
} catch (bytes memory err) { revert ERC1271Revert(err); }
}
// fallback to ecrecover ...
}
}
Caption: Core part of UniversalSigValidator.isValidSigImpl, showing the EIP‑6492 suffix detection, decoding into (create2Factory, factoryCalldata, sigToValidate), the side‑effectful create2Factory.call(factoryCalldata) when contractCodeLen == 0, and the ERC‑1271 check using ERC1271_SUCCESS. This code runs in the context of the caller (here, OdosLimitOrderRouter).
Origin: Verified OdosLimitOrderRouter project source (src/Contract.sol).
Key observations:
- When
isCounterfactualis true and_signerhas no bytecode (contractCodeLen == 0), the function unconditionally executescreate2Factory.call(factoryCalldata)and only checks that it does not revert. - There is no restriction that:
create2Factorymust be a specific deployment factory, orfactoryCalldatamust contain deployment bytecode or be side‑effect‑free.
- The call is performed from the router’s address when invoked via the router, so any ERC‑20
transferencoded infactoryCalldatawill transfer tokens held by the router.
The router itself inherits SignatureValidator, which wraps isValidSigImpl for normal EIP‑1271 validation:
contract SignatureValidator is UniversalSigValidator {
// ...
function _getOrderOwnerOrRevert(
bytes32 orderHash,
bytes calldata encodedSignature,
SignatureValidationMethod validationMethod
) internal returns (address account) {
if (validationMethod == SignatureValidationMethod.EIP712) {
account = ECDSA.recover(orderHash, encodedSignature);
} else if (validationMethod == SignatureValidationMethod.EIP1271) {
assembly {
account := shr(96, calldataload(encodedSignature.offset))
}
bytes calldata signature = encodedSignature[20:];
if (!isValidSig(account, orderHash, signature)) {
revert InvalidEip1271Signature(orderHash, account, signature);
}
} else { /* PreSign path */ }
}
}
Here, isValidSig(account, orderHash, signature) calls isValidSigImpl with allowSideEffects = false and uses a try/catch mechanism to undo side effects for counterfactual signatures. This path is reasonably safe when only used internally.
However, because OdosLimitOrderRouter inherits UniversalSigValidator directly, it exposes isValidSigImpl as a public function with an explicit allowSideEffects parameter. An attacker can skip the router’s order flow and directly call:
OdosLimitOrderRouter.isValidSigImpl(
signer, // chosen by attacker
hash, // can be 0x00, not enforced
signature,// attacker-crafted EIP-6492 payload
true // allowSideEffects
);
The root cause is therefore a design‑level misuse of a side‑effectful signature validation helper:
- Side‑effectful primitive:
UniversalSigValidator.isValidSigImplis designed to be used carefully, often off‑chain or with restricted parameters, because EIP‑6492 allows it to execute arbitrary calls during validation. - On‑chain fund‑holding contract:
OdosLimitOrderRouteris a fund‑holding contract that must not allow arbitrary users to execute calls from its context. - Public exposure without constraints: By exposing
isValidSigImplpublicly withallowSideEffects = true, the router unintentionally grants any caller the ability to execute arbitrary calls from its own address. - EIP‑1271 magic value spoofing: Using the Identity precompile (
0x0000000000000000000000000000000000000004) as_signer, the attacker ensures that the subsequent ERC‑1271 check always returns the expected magic value0x1626ba7e, makingisValidSigImplreturntrueeven after executing the malicious call.
Vulnerable Components
-
OdosLimitOrderRouter(0xb6333e994fd02a9255e794c177efbdeb1fe779c7) on Base:- Inherits
SignatureValidator→UniversalSigValidator. - Exposes
isValidSigImpl(address,bytes32,bytes,bool)as a public function. - Holds ERC‑20 token balances that are meant to be moved only via owner/liquidator‑restricted functions.
- Inherits
-
UniversalSigValidator.isValidSigImplimplementation:- Located in
src/Contract.solof the verified router project. - Implements EIP‑6492 by decoding signatures into
(create2Factory, factoryCalldata, sigToValidate)and performingcreate2Factory.call(factoryCalldata)from the caller’s context.
- Located in
Exploit Preconditions
For the exploit to succeed, the following conditions must hold (all verified in on‑chain trace and metadata):
-
Router holds token balances:
OdosLimitOrderRouterholds non‑zero balances of multiple ERC‑20 tokens at or before block 25431001, including:- FiatTokenProxy‑style stablecoin
0x833589fcd6edb6e08f4c7c32d4f71b54bda02913 - WETH9
0x4200000000000000000000000000000000000006 - ERC‑20 at
0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935 - OptimismMintableERC20 at
0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b - Proxy token at
0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf - ERC‑20 at
0x940181a94a35a4569e4529a3cdfb74e38fd98631 - ERC‑20 at
0xecac9c5f704e954931349da37f60e39f515c11c1 - ERC‑20 at
0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22 - OssifiableProxy‑style token at
0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452 - FiatTokenProxy‑style stablecoin at
0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42
These balances are confirmed via
balanceOfcalls in the exploit trace. - FiatTokenProxy‑style stablecoin
-
Router bytecode and ABI correspond to the verified project:
- The on‑chain bytecode for
0xb6333e...779c7matches the verifiedOdosLimitOrderRouterimplementation that:- Inherits
UniversalSigValidator. - Exposes
isValidSigImplas a public function in its ABI.
- Inherits
- The on‑chain bytecode for
-
Attacker can craft EIP‑6492 signatures:
- The attacker constructs EIP‑6492‑style signatures whose encoded
(create2Factory, factoryCalldata)pairs invoke ERC‑20transferfrom the router’s address to the attacker EOA for each token. - The EIP‑6492 suffix is appended so that
isValidSigImpltreats the signature as counterfactual and executescreate2Factory.call(factoryCalldata).
- The attacker constructs EIP‑6492‑style signatures whose encoded
-
Identity precompile behavior on Base:
- Base honors the standard Identity precompile at
0x0000000000000000000000000000000000000004, which returns its calldata unmodified. - The attacker chooses
_signer = Identityand craftssigToValidatesuch that:- The first four bytes of the echoed data equal
0x1626ba7e(ERC1271_SUCCESS).
- The first four bytes of the echoed data equal
- As a result, the ERC‑1271 check always passes, and
isValidSigImplreturnstrue.
- Base honors the standard Identity precompile at
Security Principles Violated
-
No side effects in signature verification:
Signature verification must not allow attackers to cause arbitrary state changes. Here,isValidSigImplallows arbitrary external calls under attacker control whenallowSideEffects = true. -
Least privilege and access control:
Movement of router‑held funds should be restricted to owner/liquidator viaswapRouterFundsandtransferRouterFunds. The publicisValidSigImplpath bypasses these controls completely. -
Separation of concerns:
A powerful, side‑effectful validation helper intended for controlled environments (EIP‑6492) is reused inside a fund‑holding router without constraining its possible side effects, turning it into an arbitrary call primitive.
Adversary Flow Analysis
High‑Level Strategy
The adversary executes a single transaction on Base where:
- The attacker EOA sends a normal EIP‑1559 (type‑2) transaction to the exploit contract.
- The exploit contract calls
OdosLimitOrderRouter.isValidSigImplrepeatedly with carefully crafted EIP‑6492‑style signatures andallowSideEffects = true. - For each call, the router:
- Performs an attacker‑controlled external call to an ERC‑20 token contract via
create2Factory.call(factoryCalldata). - Transfers its entire balance of that token to the attacker EOA.
- Completes the ERC‑1271 check using the Identity precompile, returning
true.
- Performs an attacker‑controlled external call to an ERC‑20 token contract via
This sequence drains all router‑held balances in the targeted ERC‑20 tokens in one transaction.
Adversary‑Related Accounts
-
Attacker EOA
- Chain: Base (8453)
- Address:
0x4015d786e33c1842c3e4d27792098e4a3612fc0e - Role:
- Sender of the adversary‑crafted transaction
0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6. - Direct recipient of all ERC‑20 transfers emitted with
from = OdosLimitOrderRouterandto = 0x4015...fc0ein the trace.
- Sender of the adversary‑crafted transaction
-
Attacker Contract
- Chain: Base (8453)
- Address:
0x22a7da241a39f189a8aec269a6f11a238b6086fc - Role:
- Callee of the attacker’s transaction, invoked as
0x22A7dA241A39F189a8Aec269A6F11A238B6086fc::exploit(...). - Orchestrates the sequence of
OdosLimitOrderRouter::isValidSigImplcalls that drain router‑held funds by targeting different ERC‑20 token contracts in each call.
- Callee of the attacker’s transaction, invoked as
Victim Contracts / Assets
-
OdosLimitOrderRouter
- Chain: Base (8453)
- Address:
0xb6333e994fd02a9255e794c177efbdeb1fe779c7 - Verified contract; fund‑holding router and primary victim of the exploit.
-
Router‑held token contracts (victim assets)
- FiatTokenProxy‑style stablecoin at
0x833589fcd6edb6e08f4c7c32d4f71b54bda02913(verified project). - WETH9 at
0x4200000000000000000000000000000000000006. - ERC‑20 token at
0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935(labelledTokenin trace). - OptimismMintableERC20 at
0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b. - Proxy token at
0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf. - ERC‑20 token at
0x940181a94a35a4569e4529a3cdfb74e38fd98631. - ERC‑20 token at
0xecac9c5f704e954931349da37f60e39f515c11c1. - ERC‑20 token at
0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22. - OssifiableProxy‑style token at
0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452(verified project). - FiatTokenProxy‑style stablecoin at
0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42(verified project).
- FiatTokenProxy‑style stablecoin at
Lifecycle Stage: Exploit Transaction
-
Transaction details
- Chain: Base (8453)
- Tx hash:
0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6 - Block:
25431001 - Mechanism:
other(direct arbitrary call to router’s publicisValidSigImplvia attacker contract).
-
Trace‑backed call flow
A representative segment from the collected trace:
0x22A7dA241A39F189a8Aec269A6F11A238B6086fc::exploit( OdosLimitOrderRouter: [0xB6333E994Fd02a9255E794C177EfBDEB1FE779C7], [0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x4200..., ...] ) ├─ OdosLimitOrderRouter::isValidSigImpl( │ Identity: [0x0000000000000000000000000000000000000004], │ 0x000...000, // _hash = 0 │ 0x000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913 │ 0000000000000000000000000000000000000000000000000000000000000060 │ ... a9059cbb (transfer) ... 4015d786e33c1842c3e4d27792098e4a3612fc0e │ ... amount ... │ ... 649264926492... (EIP-6492 suffix), │ true │ ) │ ├─ FiatTokenProxy::fallback(...) │ │ ├─ FiatTokenV2_2::transfer( │ │ │ from: OdosLimitOrderRouter, │ │ │ to: 0x4015D786e33c1842c3e4d27792098e4A3612Fc0e, │ │ │ value: 15578334373 │ │ │ ) [delegatecall] │ │ └─ emit Transfer(from = router, to = attacker, ...) │ ├─ PRECOMPILES::identity(...) [staticcall] │ │ └─ returns bytes starting with 0x1626ba7e │ └─ returns trueCaption: Excerpt from the seed transaction trace showing the attacker contract calling
OdosLimitOrderRouter::isValidSigImplwith_signer = Identity,_hash = 0x0, and a crafted EIP‑6492 signature whose decodedfactoryCalldatatriggersFiatTokenProxy::fallback/FiatTokenV2_2::transferfrom the router to the attacker. The Identity precompile then returns data starting with0x1626ba7e, satisfying the ERC‑1271 magic value.
Origin: Seed transaction trace (trace.cast.log) for0xd10faa5b...5df9f6.Similar calls appear for each affected token (FiatTokenProxy‑style stablecoins, WETH9, OptimismMintableERC20, other ERC‑20s), always with:
_signer = 0x0000000000000000000000000000000000000004(Identity precompile)._hash = 0x00.signatureencoding:- The target token contract as
create2Factory. - ERC‑20
transfer(routerBalance, attackerEOA)asfactoryCalldata. - The EIP‑6492 detection suffix.
- The target token contract as
allowSideEffects = true.
For each such call, the router’s entire balance in the targeted token is transferred to the attacker EOA, and
isValidSigImplreturnstrue.
Impact & Losses
Token‑Level Loss Overview
The exploit drains all router‑held balances for the targeted ERC‑20 tokens. Quantified amounts from the analysis:
-
FiatTokenProxy‑style stablecoin (
0x833589fcd6edb6e08f4c7c32d4f71b54bda02913)15,578,334,373units transferred fromOdosLimitOrderRouterto0x4015d7...fc0e.- An additional
198,830,527units transferred in a separate call within the same transaction.
-
WETH9 (
0x4200000000000000000000000000000000000006)2.261323351186171128WETH transferred fromOdosLimitOrderRouterto0x4015d7...fc0e.
-
Multiple additional ERC‑20 tokens held by the router
- At least:
81,182,355,184,994,926,311,507units of0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935.1,514,424,244,715,040,557,606units of0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b.2,343,323units of0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf.2,134,216,454,655,905,106,108units of0x940181a94a35a4569e4529a3cdfb74e38fd98631.576,319units of0xecac9c5f704e954931349da37f60e39f515c11c1.0.144206352825325002e18units of0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22.0.122592242770994685e18units of0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452.- The routed balance of
0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42.
- At least:
All of these transfers have:
from = OdosLimitOrderRouter (0xb6333e...779c7),to = attacker EOA (0x4015d7...fc0e),- And appear within the exploit transaction trace.
Aggregate Impact
-
Router fund depletion:
After the transaction,OdosLimitOrderRouter’s balances in the listed ERC‑20 tokens are reduced to zero (or to the residual post‑transfer amounts determined by the exploit payloads), representing a full depletion of router‑held funds for those assets. -
Attacker profit:
The attacker’s portfolio gains the full set of drained ERC‑20 balances. The analysis notes:- The gas cost is approximately
0.000015877492ETH‑equivalent (from the native balance delta). - ERC‑20 balance deltas for valuation are too large/complex for automatic exact computation by the tracer, but the trace shows router balances being zeroed and the same amounts credited to the attacker.
- Given standard ERC‑20 semantics and the magnitudes involved, the attacker’s net USD‑denominated portfolio value strictly increases after accounting for gas.
- The gas cost is approximately
-
Loss characterization:
These funds are held by the router on behalf of its users or protocol operations. The exploit bypasses all designed withdrawal and routing logic, directly transferring assets from the router to the attacker in a single step.
ACT Opportunity (Pre‑State & Exploitability)
Pre‑State Definition (pre_state_sigma_B)
The pre‑state σ_B is defined as the Base chain state immediately before transaction 0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6 in block 25431001, where:
OdosLimitOrderRouter(0xb6333e994fd02a9255e794c177efbdeb1fe779c7) holds non‑zero balances of the ERC‑20 tokens listed above (stablecoins, WETH9, and several other ERC‑20s).- The router’s bytecode matches the verified implementation containing
UniversalSigValidator.isValidSigImpl(address,bytes32,bytes,bool).
This pre‑state is supported by:
- Seed metadata for the transaction (block, tx, participants).
- The exploit trace showing pre‑call balances.
- Verified contract source for the router and relevant token projects.
Transaction Sequence (transaction_sequence_b)
The ACT opportunity focuses on a single adversary‑crafted transaction:
- Index:
1 - Chainid:
8453(Base) - Tx hash:
0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6 - Type:
adversary-crafted
Inclusion feasibility:
- An unprivileged EOA (
0x4015d7...fc0e) sends a standard type‑2 transaction on Base to the attacker contract0x22a7da241a39f189a8aec269a6f11a238b6086fc. - The exploit contract calls the publicly exposed
OdosLimitOrderRouter.isValidSigImplfunction with attacker‑chosen arguments (router address, token addresses, EIP‑6492‑encoded signature payloads, Identity precompile as signer). - All inputs are derivable from public contract metadata and bytecode; no privileged roles or nonstandard inclusion mechanisms are required.
This confirms that, given pre‑state σ_B, any party could have submitted a similar transaction to realize the same exploit and profit.
Exploit Predicate (exploit_predicate)
- Type:
profit - Attacker address:
0x4015d786e33c1842c3e4d27792098e4a3612fc0e - Fees paid:
- Approximately
0.000015877492ETH‑equivalent in gas, inferred from the native balance delta. - This fee is negligible relative to the drained token value.
- Approximately
- Value before / after:
- Exact USD valuation is not computed in the artifacts, but:
- Before the transaction, the attacker does not hold the router’s token balances.
- After the transaction, the attacker holds large amounts of those tokens; the router no longer does.
- The analysis concludes the attacker’s net portfolio value is strictly positive after fees.
- Exact USD valuation is not computed in the artifacts, but:
The exploit predicate is thus satisfied: given σ_B, executing this transaction increases the attacker’s net value in terms of the chosen reference asset (USD).
References
-
[1] Seed transaction metadata
Human‑readable metadata for the seed transaction0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6on Base (block, gas, participants, and environment). -
[2] Execution trace for exploit transaction
Fullcast run -vvvv‑style trace for the exploit transaction, showing:0x22A7dA...::exploit(...)callingOdosLimitOrderRouter::isValidSigImpl.- The sequence of token
balanceOfandtransfercalls. - Transfers with
from = routerandto = attackerfor each affected ERC‑20.
-
[3] Verified OdosLimitOrderRouter and UniversalSigValidator source
The project cloned for0xb6333e994fd02a9255e794c177efbdeb1fe779c7, containing:UniversalSigValidator.isValidSigImpl.SignatureValidatorand its_getOrderOwnerOrRevertlogic.OdosLimitOrderRouterimplementation with owner/liquidator‑only fund movement functions.
-
[4] Verified FiatTokenProxy‑style token project (
0x60a3e35c...db1adb42)
ContainsFiatTokenProxyand underlying token implementation used in parts of the exploit. -
[5] Verified FiatTokenProxy project (
0x833589fc...9bda02913)
Provides the stablecoin implementation from which the router’s large stablecoin balances are drained. -
[6] Verified OssifiableProxy‑based token project (
0xc1cba3fc...f0ee452)
Confirms the behavior of the OssifiableProxy‑style token involved in the exploit and supports the trace‑observed transfers from the router to the attacker.
These references collectively provide the on‑chain code, transaction trace, and metadata needed to fully reconstruct and validate the vulnerability, exploit path, and resulting losses.