Skip to content
PortunaPortunaPortunaDocsTestnet

Contracts

Three small contracts, with no owner, no admin functions and no upgrade path. Their code is the same for everyone; what makes a batcher or a delegate yours is the addresses built into it.

SweepFactory is shared: one per chain, at the same address on every chain, deployed through the standard CREATE2 deployer with a zero salt.

ChainFactoryDeployed
BSC testnet 97 Testnet0x66f63A7246dF0eD76098dD95ad0F53d8e7bed456transaction not recorded
Sepolia 11155111 Testnet0x66f63A7246dF0eD76098dD95ad0F53d8e7bed456block 11876757
Compilersolc 0.8.33+commit.64118f21, EVM prague, optimizer 200 runs
CREATE2deployer 0x4e59b44847b379578588920cA78FbF26c0B4956C, salt 0x0000000000000000000000000000000000000000000000000000000000000000
Code hashesinit code 0x8978addd4a3a80d6acb85369e765f8200edf5f2ae440241994bf3b2b11021320
runtime code 0xa805b3e8e2915685aaa97e94e623be43255d68ab0ce7886c272242d082cc475d
commit97eb1efe5c30bb96de87da6e726601602d30d360

The factory deploys your contracts when you register a hot wallet, from your gas wallet: whoever calls it becomes the gas wallet of what it deploys, so nobody can deploy contracts in your name. Its functions are idempotent.

FunctionWhat it does
deployBatcher()Deploys the caller’s batcher, or returns it.
deployDelegate(hotWallet)Deploys the delegate of hotWallet for the caller’s batcher, or returns it.
onboard(hotWallet)Both at once.
predictBatcher(gasWallet)The address of a gas wallet’s batcher, deployed or not.
predictDelegate(hotWallet, gasWallet)The address of a hot wallet’s delegate, deployed or not.

SweepBatcher(gasWallet): one per gas wallet, and only that gas wallet may call it. sweepMany sweeps a list of deposit addresses in one transaction, giving each at most 300,000 gas, so that one failing address cannot stop the rest. It emits Swept(account, token, amount) for each success and SweepFailed(account, token, reason) with the error selector for each failure.

SweepDelegate(hotWallet, batcher): one per hot wallet, the code your deposit addresses run. Its single function, sweep(token, amount), sends the address’s token, or its native coin for the zero address, to the hot wallet, and only when your batcher calls; amount 0 means the whole balance. It supports tokens that return nothing from transfer, such as USDT on Ethereum, and reads at most 32 bytes of a token’s answer. It keeps the address receiving native coin and calls like a plain address.

ErrorMeaning
Unauthorized()The caller is not your batcher, or not your gas wallet.
NothingToSweep()The balance to sweep is 0.
TokenTransferFailed()The token refused the transfer.
NativeTransferFailed()The hot wallet refused the native coin.
ZeroAddress()A delegate cannot be built for the zero address.

Every address above follows from the contract code: compute them from the code and compare. The SDK’s verify-delegate does it in one command; Verifying the delegate shows it and the manual way with Foundry. The compiler settings and code hashes above are those of the deployed factory.

On mainnet, the source of the factory and of each batcher and delegate will also be verified on Etherscan and Sourcify. Testnet deployments are not verified in explorers: check them by their addresses. For the contract source, ask support@portuna.io.