StockDistributor
0x33b0095333e64bf375952ef197b6fdc3437dc014
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
2,080 bytes
ABI entries
16
5 read · 4 write
License
none
Contract information
- Address
- 0x33b0095333e64bf375952ef197b6fdc3437dc014
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x89562Eb897…A7C47a39cb
- Creation tx
- 0x1547b73fec…1a8cb5a282
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (5)
indexToken() → address
interval() → uint256
nextDistribution() → uint256
owner() → address
treasury() → address
Events (2)
DistributedOwnershipTransferred
ABI
[
{
"inputs": [
{
"internalType": "contract ReflectionToken",
"name": "_indexToken",
"type": "address"
},
{
"internalType": "contract StockTreasury",
"name": "_treasury",
"type": "address"
},
{
"internalType": "address",
"name": "owner_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
}
],
"name": "OwnableInvalidOwner",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "OwnableUnauthorizedAccount",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "TooEarly",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "holders",
"type": "uint256"
}
],
"name": "Distributed",
"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"
},
{
"inputs": [],
"name": "distribute",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "indexToken",
"outputs": [
{
"internalType": "contract ReflectionToken",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "interval",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "nextDistribution",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "v",
"type": "uint256"
}
],
"name": "setInterval",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "contract StockTreasury",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ReflectionToken} from "./ReflectionToken.sol";
import {StockTreasury} from "./StockTreasury.sol";
/// Every `interval` (15 min) pushes this contract's balance of each registered stock token
/// pro-rata to all Index holders in the token's registry (>= minShareBalance; LP/treasury/
/// distributor excluded there). Permissionless poke — anyone/a keeper can call distribute().
/// ponytail: single-tx O(stocks x holders) push, ~thousands of holders on this cheap L2;
/// switch to batched cycles or a dividend-per-share accumulator if the registry outgrows gas.
contract StockDistributor is Ownable {
using SafeERC20 for IERC20;
ReflectionToken public immutable indexToken;
StockTreasury public immutable treasury;
uint256 public interval = 15 minutes;
uint256 public nextDistribution;
event Distributed(address indexed stock, uint256 amount, uint256 holders);
error TooEarly();
constructor(ReflectionToken _indexToken, StockTreasury _treasury, address owner_) Ownable(owner_) {
indexToken = _indexToken;
treasury = _treasury;
}
function setInterval(uint256 v) external onlyOwner { interval = v; }
function distribute() external {
if (block.timestamp < nextDistribution) revert TooEarly();
nextDistribution = block.timestamp + interval;
uint256 n = indexToken.holderCount();
if (n == 0) return;
address[] memory holders = new address[](n);
uint256[] memory bals = new uint256[](n);
uint256 eligible;
for (uint256 i; i < n; ++i) {
address h = indexToken.holderAt(i);
uint256 b = indexToken.balanceOf(h);
holders[i] = h;
bals[i] = b;
eligible += b;
}
if (eligible == 0) return;
uint256 m = treasury.stocksLength();
for (uint256 k; k < m; ++k) {
IERC20 stock = IERC20(treasury.stockTokenAt(k));
uint256 pot = stock.balanceOf(address(this));
if (pot == 0) continue;
for (uint256 i; i < n; ++i) {
uint256 amt = (pot * bals[i]) / eligible; // dust stays for the next cycle
if (amt != 0) stock.safeTransfer(holders[i], amt);
}
emit Distributed(address(stock), pot, n);
}
}
}
Chain explorer3751msChain node79ms