Skip to content

RobotWallet contract

RobotWallet holds a robot’s money on Robinhood Chain. The robot’s key can pay anyone up to a daily limit; the owner controls everything else. One contract is deployed per robot, by its owner.

constructor(address token, address owner, address robot, uint256 dailyLimit)
Argument Meaning
token The token the limit is counted in. USDG on Robinhood Chain. Fixed at deployment.
owner The wallet that sets the rules.
robot The address of the robot’s key.
dailyLimit Most the robot can spend per UTC day, in the token’s smallest unit. 25000000 is 25 USDG.
function pay(address to, uint256 amount, string calldata memo) external

Sends amount of the token to to. Only the robot’s key can call it. Reverts if spending is paused, if amount is zero, or if it would take the day’s total over the limit.

Function Returns
token() The token address
owner() The owner
robot() The robot key’s address
dailyLimit() The daily limit
spentToday() Amount spent in the current UTC day
remainingToday() What the robot can spend right now: the limit left, capped by the balance. Zero when paused.
paused() Whether spending is paused
Function Effect
setDailyLimit(uint256) Change the limit
setPaused(bool) Pause or resume spending
setRobot(address) Replace the robot’s key
withdraw(address asset, address to, uint256 amount) Withdraw any token. Pass the zero address as asset to withdraw ETH.
transferOwnership(address) Hand the account to another owner
event Paid(address indexed to, uint256 amount, string memo, uint256 spentToday);
event LimitChanged(uint256 dailyLimit);
event RobotChanged(address indexed robot);
event PausedChanged(bool paused);
event Withdrawn(address indexed asset, address indexed to, uint256 amount);
event OwnerChanged(address indexed owner);
Error When
NotRobot() pay called by anyone but the robot’s key
NotOwner() An owner function called by anyone else
Paused() pay while paused
OverDailyLimit(uint256 remaining) The payment would pass the daily limit. remaining is what’s left today.
ZeroAddress() A zero address where one isn’t allowed
ZeroAmount() pay with an amount of zero
TransferFailed() The token transfer failed, usually because the balance is too low
  • A day is block.timestamp / 1 days, a UTC day.
  • The first payment of a new day resets the running total.
  • Lowering the limit below what’s already spent today blocks further spending until the next day.
  • Withdrawals by the owner don’t count toward the limit.

The contract has unit tests, a fuzz test that checks the robot never spends more than the limit in a day, and a test against real USDG on a fork of Robinhood Chain. Read the source and the tests.