Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Run one or more contract calls atomically from an EOA via an EIP-7702 sponsored transaction — the EOA ("authority") only signs an off-chain authorization while a separate "sponsor" account pays for and sends a single type-4 tx that executes the calls with `msg.sender == authority`, all-or-nothing. Use whenever you need to (a) batch several actions that must succeed or fail together as one EOA (e.g. approve+call, transfer+register, migrate+verify), or (b) get a transaction out of an EOA that CANN
.claude/skills/lifinance-eip7702-atomic-batch/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 36% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 50% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 51% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 111% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 65% | 0% |
Run a batch of calls as an EOA, atomically, with the gas paid by a different account. The engine is script/tasks/atomicBatch7702.ts; this document is the policy around it.
EIP-7702 lets an EOA temporarily adopt a contract's code via a signed authorization. We delegate the EOA to the canonical Multicall3 (0xcA11bde05977b3631167028862bE2a173976CA11, deployed at that address on every EVM chain) and call its aggregate3 — which issues each inner call with msg.sender == the EOA, reverting the whole tx if any call fails.
Because the sponsor signs and pays for the transaction while the authority (the EOA) only signs an off-chain authorization tuple:
That second property is what makes this the reliable way to extract a transaction from a compromised, actively-swept EOA: a sweeper bot only fires when the key receives native gas. With 7702 sponsorship no gas ever lands on the key, the bot stays idle, and the authority's nonce stays stable so the authorization can't be invalidated underneath you.
on arrival, or a key that's simply out of gas on a chain, needs to make exactly one privileged call (e.g. transferOwnership, renounceRole, approve). Sponsor it.
or not at all (approve-then-pull, transfer-then-register, migrate-then-verify).
interact-tron (no EIP-7702; different VM/address model).estimateGas surfaces this. Fall back to a normal tx (fund the EOA) or an MEV-protected bundle if a sweeper is the problem.
through a private 7702 tx to dodge the flow. This skill is for keys we hold directly.
bash# DRY-RUN (default): simulates every call, signs the auth, estimates the type-4 tx, sends nothing NODE_PATH=./node_modules bunx tsx script/tasks/atomicBatch7702.ts --config ./batch.json # execute NODE_PATH=./node_modules bunx tsx script/tasks/atomicBatch7702.ts --config ./batch.json --broadcast
Requires bun, the relevant keys in .env, and an RPC (resolves ETH_NODE_URI_<NETWORK> first, else networks.json rpcUrl).
Flags: --broadcast (send; otherwise dry-run), --config <path>, and --keep-delegation (skip the automatic undelegate — see the delegate section below).
json{ "network": "arbitrum", "sponsorKeyEnv": "PRIVATE_KEY_PRODUCTION", "authorityKeyEnv": "PRIVATE_KEY_PRODUCTION_OLD_V1", "delegate": "0xcA11bde05977b3631167028862bE2a173976CA11", "calls": [ { "target": "0x5741A7FfE7c39Ca175546a54985fA79211290b51", "function": "transferOwnership(address)", "args": ["0x156CeBba59DEB2cB23742F70dCb0a11cC775591F"] } ] }
network — key in config/networks.json.sponsorKeyEnv — env var of the key that pays (default PRIVATE_KEY_PRODUCTION).authorityKeyEnv — env var of the EOA whose behalf the calls run on. Omit it (or setit equal to sponsorKeyEnv) for a self-batch where one EOA batches its own calls; the engine then signs with executor: 'self'.
delegate — optional; defaults to the canonical Multicall3(0xcA11bde05977b3631167028862bE2a173976CA11, hardcoded in the engine). Only override with an audited delegate — see "Choosing the delegate" below.
calls[] — each is { target, function, args } (human-readable signature) or{ target, data } (raw calldata). Executed atomically in order.
Self-batch example (one wallet runs approve + deposit atomically — omit authorityKeyEnv, so the sponsor is the authority and signs with executor: 'self'):
json{ "network": "base", "sponsorKeyEnv": "PRIVATE_KEY_PRODUCTION", "calls": [ { "target": "0xUSDC", "function": "approve(address,uint256)", "args": ["0xVault", "1000000"] }, { "target": "0xVault", "function": "deposit(uint256)", "args": ["1000000"] } ] }
Always raise this before broadcasting — it's a security decision, not a default to assume. aggregate3 on Multicall3 has no access control: while an EOA is delegated to it, anyone can call the EOA's aggregate3 and execute arbitrary calls as that EOA. That is acceptable only for an account with nothing left to lose during the delegation window; it is dangerous for a funded/privileged one.
| | Multicall3 (default) | Custom restricted executor | |---|---|---| | Setup | none — deployed at the same address on every chain | must write + audit + deploy a contract | | Access control | none (open) | caller-checked (msg.sender == sponsor) or signature-verifying | | Safe to leave delegated? | No | Yes | | Best for | empty / throwaway / already-compromised keys, with auto-undelegate | funded/privileged accounts, or a delegation meant to persist |
Rule of thumb to confirm with the user:
auto-undelegate is sufficient (this was the EXSC-660 case).
Multicall3. Use a restricted executor (flag this as a follow-up: it's a new Solidity contract needing its own audit gate before use on production).
If the account is funded and a restricted executor isn't available yet, stop and surface the tradeoff rather than delegating it to open Multicall3.
aggregate3 batch with allowFailure: false (atomic).executor: 'self' only in the self-batch shape).aggregate3 as one call, with the delegation applied— aborts before spending if it reverts. Simulating each call separately against pre-batch state would wrongly fail dependent batches (e.g. approve→deposit), which only succeed once aggregate3 runs them together.
--broadcast: sends the single sponsor-paid type-4 tx, waits for the receipt, andreports success/revert. Re-running re-simulates first, but is not idempotent — a successful state-changing batch repeats its effects if you run it again.
delegation is applied before execution and is not rolled back on revert): sends a second sponsored tx with the authorization pointing at the zero address, resetting the authority's code to empty, and verifies getCode == 0x. Pass --keep-delegation to skip (only when using a restricted delegate you intend to persist).
(e.g. OZ single-step transferOwnership is final). Review the batch; rely on the dry-run simulation.
A signed 7702 authorization binds the authority, delegate, nonce, and signature — but not the sponsor. If you --broadcast into a public mempool, another actor can lift the authorization from your pending tx and land their own higher-fee sponsored tx first, applying the delegation and — with the open Multicall3 delegate — executing arbitrary calls as the authority before your batch runs. For an empty authority (the EXSC-660 case) there's nothing to steal, so it's safe. For a funded or privileged authority, do not --broadcast to a public mempool: use protected orderflow / a private bundle, and/or a restricted executor instead of open aggregate3 (see "Choosing the delegate").
(authorization → zero address) after any mined batch — success or revert, since the delegation isn't rolled back on revert — so no open delegate is left behind. Only pass --keep-delegation when the delegate is access-restricted and meant to persist. Note the brief window between the batch tx and the undelegate tx where an open Multicall3 delegation is drivable by anyone — another reason Multicall3 is only for accounts that hold nothing during that window.
whoever holds the key can re-delegate it. The durable protection is that the account no longer owns/holds anything (e.g. ownership moved to a safe wallet).
delegate at an unaudited or attacker-controlled contract..env and never printed. The authority key for a compromisedwallet is used only to sign the off-chain authorization.
The production ERC20Proxy on arbitrum/bsc/polygon was owned by a compromised legacy deployer (_OLD_V1) whose native gas is swept by a bot on arrival, so the normal fund-then-transferOwnership flow lost the race every time (even on Arbitrum, whose sequencer feed lets the bot react). The sponsored 7702 batch above transfers ownership to refundWallet in one deployer-paid tx with no fundable balance on _OLD_V1 — the sweeper never fires. See EXSC-660.
0xcA11bde05977b3631167028862bE2a173976CA11 on all supportedchains (verify with cast code before use on an unfamiliar chain).
manage-wallet-funds (fund/rebalance a wallet the normal way),interact-tron (Tron equivalent), sweep-wallet-funds (native sweep on rotation).
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-18 | pass→pass | 12,955 | 6,152 | -53% | 1 | 1 | 0% | 1,940 | 3,931 | +103% | 0 | 0 | — |
case-01 | fail→pass | 18,351 | 5,810 | -68% | 1 | 1 | 0% | 3,137 | 4,258 | +36% | 0 | 0 | — |
case-02 | fail→fail | 17,879 | 7,870 | -56% | 1 | 1 | 0% | 3,154 | 4,500 | +43% | 0 | 0 | — |
case-03 | fail→pass | 21,451 | 11,654 | -46% | 1 | 1 | 0% | 3,329 | 4,992 | +50% | 0 | 0 | — |
case-04 | fail→pass | 14,951 | 3,933 | -74% | 1 | 1 | 0% | 2,391 | 3,608 | +51% | 0 | 0 | — |
case-05 | fail→pass | 48,078 | 13,024 | -73% | 1 | 1 | 0% | 1,857 | 3,909 | +111% | 0 | 0 | — |
case-06 | pass→pass | 10,869 | 5,264 | -52% | 1 | 1 | 0% | 1,897 | 3,862 | +104% | 0 | 0 | — |
case-07 | fail→pass | 12,172 | 2,095 | -83% | 1 | 1 | 0% | 2,071 | 3,414 | +65% | 0 | 0 | — |
case-08 | fail→pass | 15,667 | 2,488 | -84% | 1 | 1 | 0% | 2,698 | 3,436 | +27% | 0 | 0 | — |
case-19 | fail→pass | 9,972 | 3,058 | -69% | 1 | 1 | 0% | 1,526 | 3,533 | +132% | 0 | 0 | — |
case-09 | pass→pass | 15,373 | 6,290 | -59% | 1 | 1 | 0% | 2,509 | 4,103 | +64% | 0 | 0 | — |
case-10 | pass→pass | 13,862 | 2,762 | -80% | 1 | 1 | 0% | 2,425 | 3,563 | +47% | 0 | 0 | — |
case-11 | fail→pass | 15,122 | 5,200 | -66% | 1 | 1 | 0% | 2,464 | 3,844 | +56% | 0 | 0 | — |
case-12 | pass→pass | 12,868 | 3,967 | -69% | 1 | 1 | 0% | 1,910 | 3,599 | +88% | 0 | 0 | — |
case-13 | fail→pass | 19,700 | 10,649 | -46% | 1 | 1 | 0% | 2,949 | 4,628 | +57% | 0 | 0 | — |
case-20 | fail→pass | 16,159 | 3,277 | -80% | 1 | 1 | 0% | 2,326 | 3,544 | +52% | 0 | 0 | — |
case-14 | fail→pass | 12,352 | 4,341 | -65% | 1 | 1 | 0% | 2,030 | 3,750 | +85% | 0 | 0 | — |
case-15 | fail→pass | 11,246 | 2,108 | -81% | 1 | 1 | 0% | 1,657 | 3,350 | +102% | 0 | 0 | — |
case-16 | fail→pass | 12,799 | 1,824 | -86% | 1 | 1 | 0% | 1,970 | 3,343 | +70% | 0 | 0 | — |
case-17 | fail→pass | 11,935 | 2,047 | -83% | 1 | 1 | 0% | 2,119 | 3,306 | +56% | 0 | 0 | — |
case-21 | fail→pass | 17,035 | 2,986 | -82% | 1 | 1 | 0% | 2,641 | 3,481 | +32% | 0 | 0 | — |
case-22 | fail→fail | 14,251 | 7,681 | -46% | 1 | 1 | 0% | 2,129 | 4,217 | +98% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +68 percentage points is the difference between those two pass rates over the 21 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.