Skip to content
PortunaPortunaPortunaDocsTestnet

How it works

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.

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.

How a sweep goes from your backend to your hot walletYour backendAPI key · address keysPortuna SweepAPI · batching · webhooksYour batcherSweepBatcherREST APIwebhooksone transactiongas from your gas walletDeposit addressdelegate codeDeposit addressdelegate codeDeposit addressdelegate codesweep()Your hot wallettokens or coinEIP-7702 authorization, once per address
PartWhat it is
Deposit addressA 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 walletThe address the funds go to. Any address you control. You register it once per chain.
DelegateThe 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.
BatcherYour 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.
FactoryThe 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 walletAn 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.

  1. You request a sweep: POST /v1/sweeps with 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.
  2. 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.
  3. 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.
  4. After the confirmations, each sweep is swept, failed or rejected, with its share of the gas and its fee. Your webhook gets the outcome. See Statuses, confirmations and reorgs.

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.

You can sweep the tokens listed for a chain and its native coin. On the testnets:

ChainChain IDNative coinTokens
Sepolia11155111ETHtest USDT 0x5B74f75040584b133Ff1EB67eEe646B0da26e39C
BSC testnet97BNBtest 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.