TrancheEscrow
0xcf6a256e504a5a62717a2d4379894143b1083450
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
4,614 bytes
ABI entries
36
8 read · 7 write
License
none
Contract information
- Address
- 0xcf6a256e504a5a62717a2d4379894143b1083450
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x9008C9eE6e…13083680c6
- Creation tx
- 0x3578ea6545…4882cf17cc
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (8)
SWEEP_DELAY() → uint256
factory() → address
getProgram(address) → tuple
keeper() → address
owner() → address
pendingOwner() → address
programs(address) → uint64
renounceOwnership()
Events (7)
FactorySetKeeperChangedOwnershipTransferStartedOwnershipTransferredRegisteredSweptTrancheDistributed
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "_keeper",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AlreadyRegistered",
"type": "error"
},
{
"inputs": [],
"name": "CapExceeded",
"type": "error"
},
{
"inputs": [],
"name": "FactoryAlreadySet",
"type": "error"
},
{
"inputs": [],
"name": "LengthMismatch",
"type": "error"
},
{
"inputs": [],
"name": "NotFactory",
"type": "error"
},
{
"inputs": [],
"name": "NotKeeper",
"type": "error"
},
{
"inputs": [],
"name": "NotRegistered",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [],
"name": "RenounceDisabled",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "SweepTooEarly",
"type": "error"
},
{
"inputs": [],
"name": "TrancheLocked",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "factory",
"type": "address"
}
],
"name": "FactorySet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newKeeper",
"type": "address"
}
],
"name": "KeeperChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferStarted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint64",
"name": "launchTime",
"type": "uint64"
},
{
"indexed": false,
"internalType": "uint128[4]",
"name": "caps",
"type": "uint128[4]"
},
{
"indexed": false,
"internalType": "uint32[4]",
"name": "delays",
"type": "uint32[4]"
}
],
"name": "Registered",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Swept",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": true,
"internalType": "uint8",
"name": "trancheId",
"type": "uint8"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "TrancheDistributed",
"type": "event"
},
{
"inputs": [],
"name": "SWEEP_DELAY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint8",
"name": "trancheId",
"type": "uint8"
},
{
"internalType": "address[]",
"name": "to",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "amounts",
"type": "uint256[]"
}
],
"name": "distributeTranche",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "factory",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "getProgram",
"outputs": [
{
"components": [
{
"internalType": "uint64",
"name": "launchTime",
"type": "uint64"
},
{
"internalType": "uint128[4]",
"name": "caps",
"type": "uint128[4]"
},
{
"internalType": "uint128[4]",
"name": "spent",
"type": "uint128[4]"
},
{
"internalType": "uint32[4]",
"name": "delays",
"type": "uint32[4]"
}
],
"internalType": "struct TrancheEscrow.Program",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "keeper",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "programs",
"outputs": [
{
"internalType": "uint64",
"name": "launchTime",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint64",
"name": "launchTime",
"type": "uint64"
},
{
"internalType": "uint128[4]",
"name": "caps",
"type": "uint128[4]"
},
{
"internalType": "uint32[4]",
"name": "delays",
"type": "uint32[4]"
}
],
"name": "register",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "f",
"type": "address"
}
],
"name": "setFactory",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "a",
"type": "address"
}
],
"name": "setKeeper",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
}
],
"name": "sweepExpired",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IERC20 as IERC20OZ} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
contract TrancheEscrow is Ownable2Step {
using SafeERC20 for IERC20OZ;
uint256 public constant SWEEP_DELAY = 30 days;
address public factory;
address public keeper;
struct Program {
uint64 launchTime;
uint128[4] caps;
uint128[4] spent;
uint32[4] delays;
}
mapping(address => Program) public programs;
event Registered(address indexed token, uint64 launchTime, uint128[4] caps, uint32[4] delays);
event TrancheDistributed(address indexed token, uint8 indexed trancheId, address indexed to, uint256 amount);
event Swept(address indexed token, address to, uint256 amount);
event KeeperChanged(address indexed newKeeper);
event FactorySet(address indexed factory);
error NotFactory();
error NotKeeper();
error FactoryAlreadySet();
error AlreadyRegistered();
error NotRegistered();
error TrancheLocked();
error CapExceeded();
error LengthMismatch();
error SweepTooEarly();
error RenounceDisabled();
constructor(address _keeper) Ownable(msg.sender) {
keeper = _keeper;
emit KeeperChanged(_keeper);
}
function setFactory(address f) external onlyOwner {
if (factory != address(0)) revert FactoryAlreadySet();
factory = f;
emit FactorySet(f);
}
function setKeeper(address a) external onlyOwner {
keeper = a;
emit KeeperChanged(a);
}
function getProgram(address token) external view returns (Program memory) {
return programs[token];
}
function register(address token, uint64 launchTime, uint128[4] calldata caps, uint32[4] calldata delays)
external
{
if (msg.sender != factory) revert NotFactory();
if (programs[token].launchTime != 0) revert AlreadyRegistered();
programs[token] = Program(launchTime, caps, [uint128(0), 0, 0, 0], delays);
emit Registered(token, launchTime, caps, delays);
}
function distributeTranche(address token, uint8 trancheId, address[] calldata to, uint256[] calldata amounts)
external
{
if (msg.sender != keeper) revert NotKeeper();
if (to.length != amounts.length) revert LengthMismatch();
Program storage p = programs[token];
if (p.launchTime == 0) revert NotRegistered();
if (block.timestamp < uint256(p.launchTime) + p.delays[trancheId]) revert TrancheLocked();
uint256 total;
for (uint256 i; i < amounts.length; ++i) total += amounts[i];
if (uint256(p.spent[trancheId]) + total > p.caps[trancheId]) revert CapExceeded();
p.spent[trancheId] += uint128(total);
for (uint256 i; i < to.length; ++i) {
IERC20OZ(token).safeTransfer(to[i], amounts[i]);
emit TrancheDistributed(token, trancheId, to[i], amounts[i]);
}
}
function sweepExpired(address token, address to) external onlyOwner {
Program storage p = programs[token];
if (p.launchTime == 0) revert NotRegistered();
if (block.timestamp < uint256(p.launchTime) + SWEEP_DELAY) revert SweepTooEarly();
uint256 bal = IERC20OZ(token).balanceOf(address(this));
IERC20OZ(token).safeTransfer(to, bal);
emit Swept(token, to, bal);
}
function renounceOwnership() public view override onlyOwner {
revert RenounceDisabled();
}
}
Chain explorer3635msChain nodeok