io.github.andresdefi/zerodust
Recover stranded native gas: empty a chain's balance to exactly zero on 25 EVM chains.
Open source Repository Open in the app JSON README (API)
About
Recover stranded native gas: empty a chain's balance to exactly zero on 25 EVM chains.
Details
- Kind
- MCP servers
- Topic
- No topic detected
- Publisher
- andresdefi
- Origin
- official
- Category
- ferramentas
- Transport
- http
- Version
- 0.3.1
- Stars
- 2
- Last push
- 2026-07-25T20:21:46Z
- Repository state
- ativo
- Language
- TypeScript
- License
- MIT
- Added
- 2026-08-29 03:02:25
- Updated
- 2026-08-29 03:02:25
- Origin id
io.github.andresdefi/zerodust
README
# ZeroDust
**Exit a blockchain completely - transfer 100% of your native gas balance via EIP-7702**
ZeroDust is an intent-based exit system that enables users to sweep their entire native gas token balance to exactly zero via EIP-7702 sponsored execution.
## For AI agents
Agents accumulate dust as a byproduct of existing. Anything doing multi-chain
work — arbitrage, bridging, testing, deployment — ends up with stranded gas on
chains it will never touch again. A human notices and shrugs; an unattended
agent leaks capital indefinitely.
### Look first, no install, no key
The hosted MCP server needs nothing installed. Point any MCP client at:
```
https://api.zerodust.xyz/mcp
```
That is enough to find out whether an address has anything stranded and what
recovering it would cost. It is read-only, because it holds no keys.
### Then sweep, with the key wherever you keep it
```json
{
"mcpServers": {
"zerodust": {
"command": "npx",
"args": ["@zerodust/mcp-server"],
"env": {
"ZERODUST_ALLOW_EXECUTE": "true",
"ZERODUST_SIGNER_MODULE": "./my-signer.mjs"
}
}
}
}
```
Read-only by default. Sweeping needs the explicit opt-in above plus a signing
key, and there are four ways to supply one so a raw key never has to sit in a
config file:
| Variable | Key lives in |
|----------|--------------|
| `ZERODUST_SIGNER_MODULE` | your custody provider — any module returning a viem `LocalAccount`, which is what Turnkey, Privy and KMS adapters produce |
| `ZERODUST_KEYSTORE_FILE` | an encrypted V3 keystore, with the password in a separate file |
| `ZERODUST_PRIVATE_KEY_FILE` | a file on disk, not in the config |
| `ZERODUST_PRIVATE_KEY` | the config (simplest, least private) |
Funds can only go to the agent's own address unless
`ZERODUST_ALLOWED_DESTINATIONS` says otherwise — so a prompt-injected agent still
cannot send funds somewhere you never approved.
### Try it without risking anything
Every sweep tool and every SDK sweep accepts `dryRun`. It fetches a real quote,
produces all three real signatures, and stops before submitting. Nothing is
broadcast and no balance moves:
> "Do a dry run of sweeping my Arbitrum balance to Base"
There is deliberately no testnet mode: the API serves no testnet chains, so a
testnet flag would only return empty chain lists and failing quotes. `dryRun`
gives the same confidence against production.
### Agents can provision their own credentials
The read-only tools work with no credential at all. For higher limits an agent
can issue itself a key with no human in the loop, via the `zerodust_register_api_key`
tool or directly:
```bash
curl -X POST https://api.zerodust.xyz/agent/register \
-H "Content-Type: application/json" \
-d '{"name": "my-agent", "agentId": "my-agent-1"}'
# -> { "apiKey": "zd_...", "rateLimits": { "perMinute": 300, "daily": 1000 } }
```
| Package | Use |
|---------|-----|
| [`@zerodust/mcp-server`](https://www.npmjs.com/package/@zerodust/mcp-server) | MCP (Claude Code, Claude Desktop, any MCP client) |
| [`@zerodust/sdk`](https://www.npmjs.com/package/@zerodust/sdk) | TypeScript, direct — `createAgentFromPrivateKey` |
| [`@zerodust/langchain`](https://www.npmjs.com/package/@zerodust/langchain) | LangChain tools |
| [`@zerodust/ai-sdk`](https://www.npmjs.com/package/@zerodust/ai-sdk) | Vercel AI SDK tools |
**Verified on mainnet** (2026-07-21): Optimism → Base, source balance to exactly
0, delegation auto-revoked, 99.88% delivered, 23.2s end to end —
[`0x19456ea8…`](https://optimistic.etherscan.io/tx/0x19456ea86ed91097847ddad6d6b8cfd6a5240dedac3b100d1a06eb64c86def6c).
> **Note on wallets:** the browser UI needs the non-standard
> `wallet_signAuthorization` RPC, which no shipping wallet exposes yet
> ([MetaMask #7836](https://github.com/MetaMask/core/pull/7836),
> [Rabby #3411](https://github.com/RabbyHub/Rabby/pull/3411)). Agents are
> unaffected — they hold their own keys and sign locally.
## The Problem
When users want to fully exit a blockchain, they face an impossible situation:
```
User has: 0.0008 ETH on Arbitrum
User wants: 0 ETH on Arbitrum (transfer everything to Base)
The Problem:
├── To send ETH, you need ETH for gas
├── If you send all your ETH, you can't pay gas
├── If you keep gas, you can't send all your ETH
└── Result: Small amount always stranded
```
**ZeroDust is the only solution that enables complete chain exits for native gas tokens.**
## How It Works
1. User connects wallet to ZeroDust
2. User selects source chain and destination (same-chain or cross-chain)
3. User signs ONE authorization (no gas needed)
4. ZeroDust sponsor executes the sweep
5. User receives funds on destination
6. **Origin chain balance: EXACTLY ZERO**
## Supported Sweep Cases
| Case | Description | Example |
|------|-------------|---------|
| Cross-chain, same address | Exit to yourself on another chain | Arbitrum → Base (same wallet) |
| Cross-chain, different address | Exit to another wallet on another chain | Arbitrum → Base (different wallet) |
| Same-chain, different address | Consolidate to another wallet | Arbitrum → Arbitrum (different wallet) |
**Post-Condition (enforced on-chain):** Source balance = exactly 0 wei
## Supported Chains
**Contract Address (same on all chains):** `0x3732398281d0606aCB7EC1D490dFB0591BE4c4f2`
The contract is deployed on 26 mainnets. **25 of those are live in the API** —
Apechain (33139) is deployed but disabled, because it turned out not to support
EIP-7702.
| Chain | ID | Token | Chain | ID | Token |
|-------|---:|-------|-------|---:|-------|
| Ethereum | 1 | ETH | Mantle | 5000 | MNT |
| Optimism | 10 | ETH | Superseed | 5330 | ETH |
| BNB Chain | 56 | BNB | Base | 8453 | ETH |
| Gnosis | 100 | xDAI | Plasma | 9745 | XPL |
| Unichain | 130 | ETH | Mode | 34443 | ETH |
| Polygon | 137 | POL | Arbitrum | 42161 | ETH |
| Sonic | 146 | S | Celo | 42220 | CELO |
| X Layer | 196 | OKB | Ink | 57073 | ETH |
| Fraxtal | 252 | FRAX | BOB | 60808 | ETH |
| World Chain | 480 | ETH | Berachain | 80094 | BERA |
| Sei | 1329 | SEI | Scroll | 534352 | ETH |
| Story | 1514 | IP | Zora | 7777777 | ETH |
| Soneium | 1868 | ETH | | | |
This table is generated from the live API, which is the only authoritative
answer to what an integration can actually use:
```bash
node scripts/generate-chain-docs.mjs # regenerate
node scripts/generate-chain-docs.mjs --check # fail if a doc has drifted
curl https://api.zerodust.xyz/chains # the source of truth
```
Please do not hand-edit it. Earlier versions of this table claimed 26 live
chains and named 1514 "Astar zkEVM", 5330 "Kaia" and 57073 "Redstone" — three
chains that are not the ones deployed there. An agent that acts on a wrong chain
name gets an error and reasonably concludes the service is broken.
The contract is also on 46 testnets, but **the API serves no testnet chains**, so
there is no testnet environment to integrate against. Use the `dryRun` option in
the SDK or the MCP server to exercise the full flow without moving funds.
See [contracts/README.md](./contracts/README.md) for explorer links.
## Project Structure
```
zerodust/
├── contracts/ # Smart contracts (Foundry)
│ ├── src/
│ │ ├── ZeroDustSweepMainnet.sol # Production contract
│ │ └── ZeroDustSweepTEST.sol # Testnet contract
│ ├── script/
│ │ └── DeployMainnet.s.sol # Mainnet deployment (CREATE2)
│ └── broadcast/ # Deployment logs
└── docs/
```
## Architecture
### Contract Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ User's EOA │
│ (EIP-7702 delegated) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ ZeroDustSweepMainnet (bytecode) │ │
│ │ │ │
│ │ executeSweep(intent, sig) │ │
│ │ │ │ │
│ │ ┌────────────┴────────────┐ │ │
│ │ ▼ ▼ │ │
│ │ MODE_TRANSFER (0) MODE_CALL (1) │ │
│ │ Same-chain sweep Cross-chain sweep │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ Transfer to Call bridge target │ │
│ │ destination (callTarget + callData) │ │
│ │ │ │ │
│ └─────────────────────────────────────┼────────────────┘ │
│ │ │
└────────────────────────────────────────┼─────────────────────┘
│
▼
┌─────────────────────────┐
│ External Bridge │
│ (Gas.zip) │
│ │
│ Delivers funds on │
│ destination chain │
└─────────────────────────┘
```
### Security Model
- **No admin functions** - Immutable after deployment
- **No upgradability** - What you see is what you get
- **Unified SweepIntent** - Single signed structure for all sweep types
- **Zero balance enforcement** - Contract reverts if any balance remains
- **ERC-7201 storage** - Prevents slot collisions with other EIP-7702 apps
- **Immutable sponsors** - Stored in bytecode, not storage
## Fee Structure
**Service Fee:** 1% of swept value, with $0.05 minimum and $0.50 maximum.
```
Total Fee = Gas Reimbursement + Service Fee + Bridge Fee (if cross-chain)
Examples:
- $5 balance → $0.05 fee (1% = $0.05, at min) → User receives ~$4.95
- $10 balance → $0.10 fee (1%) → User receives ~$9.90
- $60 balance → $0.50 fee (max) → User receives ~$59.50
```
## Documentation
- [contracts/README.md](./contracts/README.md) - Contract details and deployment
- [contracts/SPECIFICATION.md](./contracts/SPECIFICATION.md) - Technical specification
- [contracts/DEPLOYMENT.md](./contracts/DEPLOYMENT.md) - Deployment guide
## Security
ZeroDust is designed with security as the top priority:
- **No fund custody** - All operations are atomic, single-transaction
- **User-controlled limits** - maxTotalFeeWei and minReceive signed by user
- **Mandatory simulation** - Every transaction simulated before execution
- **routeHash binding** - Signature bound to specific bridge route (cross-chain)
- **Internal security review** - 7 rounds, 16 issues identified and fixed
- **External audit** - Pending (required before full launch)
## Status
**Smart Contract:** Deployed on 26 mainnets + 46 testnets. **25 mainnets are
enabled in the API**; the API serves no testnets.
### Contract Versions
| Contract | Status | Features |
|---------|--------|----------|
| ZeroDustSweepMainnet | **Production** | Unified SweepIntent, granular fees, sponsor model |
| ZeroDustSweepTEST | Testnet | Same as mainnet, for testing |
### Verified Mainnet Sweeps
| Chain | Swept | TX |
|-------|-------|-----|
| Base | $3.46 → 0 | [View](https://basescan.org/tx/0x2f59a4598c7fcdce404c2330d361fda1cbab84b841e85bec82ca12164101b73d) |
| Arbitrum | $3.57 → 0 | [View](https://arbiscan.io/tx/0xffa0a26008157b0225a7c15c2263b80b6e386520dce69b58827320ced0dc5c62) |
| BSC | $2.25 → 0 | [View](https://bscscan.com/tx/0xc94f52c8689268118e3d42dd678916982b5479adb0e69227ddd1c3142ea52972) |
| Polygon | $7.55 → 0 | [View](https://polygonscan.com/tx/0xc21c4c29dbe1624c06a2a9a7692ac68409f3407f0c1960f01100ef39ceeb369f) |
See [contracts/README.md](./contracts/README.md) for full deployment list.
### Testnets NOT Supporting EIP-7702
The following testnets were tested and do not support EIP-7702:
Abstract, Lens, zkSync, Taiko, opBNB, Avalanche, Swell, Cyber, Boba, Metis, Fuse, Aurora, Flare, Vana, Corn, Rootstock, Apechain, IoTeX, Viction, XDC, Telos, Kava, EDU Chain, Gravity, Manta Pacific, Lightlink, Moonbase, Nibiru, Somnia, Rari, Blast, Xai, B3, Mezo, Chiliz, HashKey, Memecore
*Note: Mainnet support may differ from testnet.*
## Cross-Chain Bridging
ZeroDust supports cross-chain sweeps via the MODE_CALL pattern:
- **callTarget**: Bridge contract address
- **callData**: Bridge-specific transaction data
- **routeHash**: `keccak256(callData)` - binds signature to specific route
**Primary Bridge:** [Gas.zip](https://gas.zip) - 239+ chains, ~5 second delivery
## License
MIT License - see [LICENSE](./LICENSE)
---
**Live on 25 mainnet chains.** Contract: `0x3732398281d0606aCB7EC1D490dFB0591BE4c4f2`
(same address on every chain, via CREATE2).