Automating stablecoin payouts without taking custody
Most automated payment products take custody of user funds in order to schedule transfers. We didn't want to — and the contract design that makes that possible turned out to be the most interesting engineering problem we solved.
The ERC-20 allowance trick
Veltro's VeltroBatchPayout contract calls USDC.transferFrom(yourWallet, recipient, amount) for every recipient in a batch. The user pre-approves a generous USDC allowance to the contract once; every subsequent payout is a single transaction the user signs themselves. The contract never holds a balance.
OFAC screening must be synchronous
Our first architecture ran OFAC screening asynchronously after the batch was prepared. That was wrong. Screening must block the entire preparation step — no payout should be presentable for signature if any recipient hasn't been cleared. We moved screening into the preparation flow itself.
Zero custody simplifies the UX
Because Veltro never holds funds, the UX is: prepare → show summary → user signs → done. There's no "pending" state where funds are in transit through us. The only state that matters is "signed" or "not signed."
Single-transaction follow-ups
After the initial allowance, every subsequent batch payout is a single executeBatch call. The dashboard checks allowance() live and shows the user "Send payout (1 transaction)" — no extra signatures, no re-approval flow.