All incidents

Odos router signature-validation bug enables arbitrary token drains

Share
Jan 23, 2025 16:55 UTCAttackLoss: 15,777.16 USDC, 2.26 WETH +1 moreManually checked1 exploit txWindow: Atomic
Estimated Impact
15,777.16 USDC, 2.26 WETH +1 more
Label
Attack
Exploit Tx
1
Addresses
11
Attack Window
Atomic
Jan 23, 2025 16:55 UTC → Jan 23, 2025 16:55 UTC

Exploit Transactions

TX 1Base
0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6
Jan 23, 2025 16:55 UTCExplorer

Victim Addresses

0xb6333e994fd02a9255e794c177efbdeb1fe779c7Base
0x833589fcd6edb6e08f4c7c32d4f71b54bda02913Base
0x4200000000000000000000000000000000000006Base
0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935Base
0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1bBase
0xcbb7c0000ab88b473b1f5afd9ef808440eed33bfBase
0x940181a94a35a4569e4529a3cdfb74e38fd98631Base
0xecac9c5f704e954931349da37f60e39f515c11c1Base
0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22Base
0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452Base
0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42Base

Loss Breakdown

15,777.16USDC
2.26WETH
81,182.36ERC20

Similar Incidents

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

    • OdosLimitOrderRouter is an EIP‑712–based limit order router that can hold ERC‑20 balances on‑chain.
    • It includes owner‑only functions swapRouterFunds and transferRouterFunds to move router‑held funds, with an additional privileged liquidatorAddress allowed to call swapRouterFunds.
    • 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 extends UniversalSigValidator, 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 calls isValidSigImpl with allowSideEffects = false. In that path, any side effects during counterfactual validation are reverted so that signature checks cannot accidentally change state.

    • However, the raw isValidSigImpl entrypoint with allowSideEffects = true remains 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 with msg.sender = address(this) (the router), meaning any factoryCalldata can be used to perform arbitrary calls from the router’s address, including ERC‑20 transfer operations of router‑held balances.

    • After this call, isValidSigImpl proceeds to an ERC‑1271 check: if _signer is a contract (or treated as such in the logic), it calls:

      IERC1271Wallet(_signer).isValidSignature(_hash, sigToValidate)
      

      and returns true if the magic value 0x1626ba7e (ERC1271_SUCCESS) is observed.

  • Public exposure of isValidSigImpl on router

    • The router’s contract interface exposes isValidSigImpl as a public function via inheritance from UniversalSigValidator.
    • This public entrypoint takes caller‑supplied _signer, _hash, _signature, and allowSideEffects, and it is not restricted by any access control or scoped to limit which contracts may be called via EIP‑6492.

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 isCounterfactual is true and _signer has no bytecode (contractCodeLen == 0), the function unconditionally executes create2Factory.call(factoryCalldata) and only checks that it does not revert.
  • There is no restriction that:
    • create2Factory must be a specific deployment factory, or
    • factoryCalldata must 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 transfer encoded in factoryCalldata will 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.isValidSigImpl is 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: OdosLimitOrderRouter is a fund‑holding contract that must not allow arbitrary users to execute calls from its context.
  • Public exposure without constraints: By exposing isValidSigImpl publicly with allowSideEffects = 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 value 0x1626ba7e, making isValidSigImpl return true even after executing the malicious call.

Vulnerable Components

  • OdosLimitOrderRouter (0xb6333e994fd02a9255e794c177efbdeb1fe779c7) on Base:

    • Inherits SignatureValidatorUniversalSigValidator.
    • 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.
  • UniversalSigValidator.isValidSigImpl implementation:

    • Located in src/Contract.sol of the verified router project.
    • Implements EIP‑6492 by decoding signatures into (create2Factory, factoryCalldata, sigToValidate) and performing create2Factory.call(factoryCalldata) from the caller’s context.

Exploit Preconditions

For the exploit to succeed, the following conditions must hold (all verified in on‑chain trace and metadata):

  • Router holds token balances:
    OdosLimitOrderRouter holds 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 balanceOf calls in the exploit trace.

  • Router bytecode and ABI correspond to the verified project:

    • The on‑chain bytecode for 0xb6333e...779c7 matches the verified OdosLimitOrderRouter implementation that:
      • Inherits UniversalSigValidator.
      • Exposes isValidSigImpl as a public function in its ABI.
  • Attacker can craft EIP‑6492 signatures:

    • The attacker constructs EIP‑6492‑style signatures whose encoded (create2Factory, factoryCalldata) pairs invoke ERC‑20 transfer from the router’s address to the attacker EOA for each token.
    • The EIP‑6492 suffix is appended so that isValidSigImpl treats the signature as counterfactual and executes create2Factory.call(factoryCalldata).
  • Identity precompile behavior on Base:

    • Base honors the standard Identity precompile at 0x0000000000000000000000000000000000000004, which returns its calldata unmodified.
    • The attacker chooses _signer = Identity and crafts sigToValidate such that:
      • The first four bytes of the echoed data equal 0x1626ba7e (ERC1271_SUCCESS).
    • As a result, the ERC‑1271 check always passes, and isValidSigImpl returns true.

Security Principles Violated

  • No side effects in signature verification:
    Signature verification must not allow attackers to cause arbitrary state changes. Here, isValidSigImpl allows arbitrary external calls under attacker control when allowSideEffects = true.

  • Least privilege and access control:
    Movement of router‑held funds should be restricted to owner/liquidator via swapRouterFunds and transferRouterFunds. The public isValidSigImpl path 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:

  1. The attacker EOA sends a normal EIP‑1559 (type‑2) transaction to the exploit contract.
  2. The exploit contract calls OdosLimitOrderRouter.isValidSigImpl repeatedly with carefully crafted EIP‑6492‑style signatures and allowSideEffects = true.
  3. 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.

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 = OdosLimitOrderRouter and to = 0x4015...fc0e in the trace.
  • Attacker Contract

    • Chain: Base (8453)
    • Address: 0x22a7da241a39f189a8aec269a6f11a238b6086fc
    • Role:
      • Callee of the attacker’s transaction, invoked as 0x22A7dA241A39F189a8Aec269A6F11A238B6086fc::exploit(...).
      • Orchestrates the sequence of OdosLimitOrderRouter::isValidSigImpl calls that drain router‑held funds by targeting different ERC‑20 token contracts in each call.

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 (labelled Token in 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).

Lifecycle Stage: Exploit Transaction

  • Transaction details

    • Chain: Base (8453)
    • Tx hash: 0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6
    • Block: 25431001
    • Mechanism: other (direct arbitrary call to router’s public isValidSigImpl via 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 true
    

    Caption: Excerpt from the seed transaction trace showing the attacker contract calling OdosLimitOrderRouter::isValidSigImpl with _signer = Identity, _hash = 0x0, and a crafted EIP‑6492 signature whose decoded factoryCalldata triggers FiatTokenProxy::fallback/FiatTokenV2_2::transfer from the router to the attacker. The Identity precompile then returns data starting with 0x1626ba7e, satisfying the ERC‑1271 magic value.
    Origin: Seed transaction trace (trace.cast.log) for 0xd10faa5b...5df9f6.

    Similar calls appear for each affected token (FiatTokenProxy‑style stablecoins, WETH9, OptimismMintableERC20, other ERC‑20s), always with:

    • _signer = 0x0000000000000000000000000000000000000004 (Identity precompile).
    • _hash = 0x00.
    • signature encoding:
      • The target token contract as create2Factory.
      • ERC‑20 transfer(routerBalance, attackerEOA) as factoryCalldata.
      • The EIP‑6492 detection suffix.
    • allowSideEffects = true.

    For each such call, the router’s entire balance in the targeted token is transferred to the attacker EOA, and isValidSigImpl returns true.


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,373 units transferred from OdosLimitOrderRouter to 0x4015d7...fc0e.
    • An additional 198,830,527 units transferred in a separate call within the same transaction.
  • WETH9 (0x4200000000000000000000000000000000000006)

    • 2.261323351186171128 WETH transferred from OdosLimitOrderRouter to 0x4015d7...fc0e.
  • Multiple additional ERC‑20 tokens held by the router

    • At least:
      • 81,182,355,184,994,926,311,507 units of 0xb33ff54b9f7242ef1593d2c9bcd8f9df46c77935.
      • 1,514,424,244,715,040,557,606 units of 0x0b3e328455c4059eeb9e3f84b5543f74e24e7e1b.
      • 2,343,323 units of 0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf.
      • 2,134,216,454,655,905,106,108 units of 0x940181a94a35a4569e4529a3cdfb74e38fd98631.
      • 576,319 units of 0xecac9c5f704e954931349da37f60e39f515c11c1.
      • 0.144206352825325002e18 units of 0x2ae3f1ec7f1f5012cfeab0185bfc7aa3cf0dec22.
      • 0.122592242770994685e18 units of 0xc1cba3fcea344f92d9239c08c0568f6f2f0ee452.
      • The routed balance of 0x60a3e35cc302bfa44cb288bc5a4f316fdb1adb42.

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.000015877492 ETH‑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.
  • 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 contract 0x22a7da241a39f189a8aec269a6f11a238b6086fc.
  • The exploit contract calls the publicly exposed OdosLimitOrderRouter.isValidSigImpl function 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.000015877492 ETH‑equivalent in gas, inferred from the native balance delta.
    • This fee is negligible relative to the drained token value.
  • 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.

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 transaction 0xd10faa5b33ddb501b1dc6430896c966048271f2510ff9ed681dd6d510c5df9f6 on Base (block, gas, participants, and environment).

  • [2] Execution trace for exploit transaction
    Full cast run -vvvv‑style trace for the exploit transaction, showing:

    • 0x22A7dA...::exploit(...) calling OdosLimitOrderRouter::isValidSigImpl.
    • The sequence of token balanceOf and transfer calls.
    • Transfers with from = router and to = attacker for each affected ERC‑20.
  • [3] Verified OdosLimitOrderRouter and UniversalSigValidator source
    The project cloned for 0xb6333e994fd02a9255e794c177efbdeb1fe779c7, containing:

    • UniversalSigValidator.isValidSigImpl.
    • SignatureValidator and its _getOrderOwnerOrRevert logic.
    • OdosLimitOrderRouter implementation with owner/liquidator‑only fund movement functions.
  • [4] Verified FiatTokenProxy‑style token project (0x60a3e35c...db1adb42)
    Contains FiatTokenProxy and 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.