How it works
The problem
Section titled “The problem”A deposit address holds your user’s tokens but no native coin to pay gas. The classic sweep first sends the address some ETH or BNB for gas, then transfers the tokens from it: two transactions per deposit, a wallet to run the top-ups from, and leftover gas dust on every address.
The idea
Section titled “The idea”EIP-7702 lets a plain address (an EOA) run a contract’s code while keeping its own key. The address signs an authorization: “on this chain, run the code at this address”. After that, a transaction from anyone can call into the deposit address, and it runs the code it delegated to.
Portuna Sweep gives each of your hot wallets a small contract, its delegate, that can do exactly one thing: move the address’s tokens or native coin to that hot wallet, when your own batcher contract asks. Each deposit address authorizes the delegate once. From then on one transaction from your gas wallet sweeps many deposit addresses at once, and none of them ever needs gas.
The parts
Section titled “The parts”| Part | What it is |
|---|---|
| Deposit address | A plain EOA. Your custody holds its key, and the key signs only the authorization. After its first sweep the address carries a delegation: its code is 0xef0100 followed by the delegate’s address. It still receives tokens and native coin like any address. |
| Hot wallet | The address the funds go to. Any address you control. You register it once per chain. |
| Delegate | The SweepDelegate contract of one hot wallet: the code your deposit addresses run. The hot wallet and your batcher are fixed in its bytecode, so it can send funds only to that hot wallet, and only when your batcher calls it. No owner, no storage, no upgrades. |
| Batcher | Your SweepBatcher contract. Only your gas wallet can call it. It sweeps a list of deposit addresses in one transaction; an address that fails does not stop the others. |
| Factory | The shared SweepFactory contract that deploys batchers and delegates. It has no owner, and whoever calls it becomes the gas wallet of what it deploys, so nobody can deploy contracts in your name. |
| Gas wallet | An address Portuna creates for you, the same on every chain. You fund it with the chain’s native coin; it pays for deploying your contracts and for every batch, and your fees accrue on it. |
Your batcher and delegates are yours alone: no other partner’s transaction can call them. All three contracts are
created with CREATE2, so their addresses follow from their code. You can compute them yourself and check that the
code on chain is the open code: see Verifying the delegate.
One sweep, step by step
Section titled “One sweep, step by step”- You request a sweep:
POST /v1/sweepswith the deposit address, the token and, for the address’s first sweep, its signed authorization. The API checks the authorization at once: the right chain, your hot wallet’s delegate, signed by the deposit address itself. - The service builds a batch. Sweeps of the same hot wallet that are queued by then go together, up to the chain’s batch size. It reads the deposit addresses’ code and nonces, simulates the whole batch and leaves out anything that would fail.
- One transaction goes on chain, from your gas wallet to your batcher. A first sweep carries its authorization in the same transaction: the chain sets the delegate on the address, then the batcher calls it. Repeat sweeps need no authorization.
- After the confirmations, each sweep is
swept,failedorrejected, with its share of the gas and its fee. Your webhook gets the outcome. See Statuses, confirmations and reorgs.
One hot wallet per address at a time
Section titled “One hot wallet per address at a time”An address runs one delegate at a time, so it sweeps into one hot wallet. To sweep it into another hot wallet, sign an authorization for that hot wallet’s delegate and send it with the next sweep; that sweep costs about as much as a first one. To make it a plain address again, revoke the delegation.
Chains and tokens
Section titled “Chains and tokens”You can sweep the tokens listed for a chain and its native coin. On the testnets:
| Chain | Chain ID | Native coin | Tokens |
|---|---|---|---|
| Sepolia | 11155111 | ETH | test USDT 0x5B74f75040584b133Ff1EB67eEe646B0da26e39C |
| BSC testnet | 97 | BNB | test USDT 0x5B74f75040584b133Ff1EB67eEe646B0da26e39C |
In a request, a token is its ticker (in any case), its contract address, or native for the chain’s coin. Any
other token gets unknown_token, and sweeps move only the requested token: airdrops and unknown tokens on an
address are left alone. Mainnet, Ethereum and BSC, is coming soon.