StockPayDistributor
0x43dc3ecfe02aa4192f41143fee9c0b6267424935
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
18,643 bytes
ABI entries
87
44 read · 23 write
License
none
Contract information
- Address
- 0x43dc3ecfe02aa4192f41143fee9c0b6267424935
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0xEfD3921A17…aaF3e935f3
- Creation tx
- 0xb3bddd063d…639731798f
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (44)
BASE_BPS() → uint256
CARRY_CREATOR_BPS() → uint256
CARRY_PLATFORM_BPS() → uint256
ZEROX() → address
active(address) → bool
activeStrategies(uint256) → uint256
activeStrategyCount() → uint256
buyer() → address
carryCredits(address, uint256) → uint256
carryExempt(uint256) → bool
claimable(address, address) → uint256
cycler() → address
defaultStock() → address
defaultStrategyId() → uint32
holderCount() → uint256
holderList(uint256) → address
holders(address) → uint256, uint256, address, uint32, uint64, uint256, uint256, uint256, uint32
magBase(address) → uint256
magMatch(address) → uint256
magStratBase(uint256) → uint256
magStratMatch(uint256) → uint256
menu(uint256) → address
menuLength() → uint256
minBalanceForRewards() → uint256
onMenu(address) → bool
opsWallet() → address
outstanding(uint256) → uint256
owner() → address
previewCycle(uint256) → tuple[]
pushCursor() → uint256
stratListed(uint256) → bool
strategies() → address
tierCutoffs(uint256) → uint32
tierWeightOf(address) → uint256
tierWeights(uint256) → uint16
token() → address
totalBaseShares(address) → uint256
totalMatchShares(address) → uint256
totalStratBaseShares(uint256) → uint256
totalStratMatchShares(uint256) → uint256
unallocated(address) → uint256
vault(uint256, address) → uint256
vetted(address) → bool
withdrawable(address, address) → uint256
Events (10)
BuyerSetCarryAccruedClaimedClockResetCycleBuyDefaultStrategySetMenuUpdatedOwnershipTransferredPreferenceSetStrategyAdopted
ABI
[
{
"inputs": [
{
"internalType": "address",
"name": "token_",
"type": "address"
},
{
"internalType": "address",
"name": "defaultStock_",
"type": "address"
},
{
"internalType": "address",
"name": "ops_",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BadStrategy",
"type": "error"
},
{
"inputs": [],
"name": "NoBuyer",
"type": "error"
},
{
"inputs": [],
"name": "NotOnMenu",
"type": "error"
},
{
"inputs": [],
"name": "OnlyToken",
"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": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
}
],
"name": "BuyerSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "strategyId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "credits",
"type": "uint256"
}
],
"name": "CarryAccrued",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "holder",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Claimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "holder",
"type": "address"
}
],
"name": "ClockReset",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "bool",
"name": "strat",
"type": "bool"
},
{
"indexed": true,
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "sid",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethSpent",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "got",
"type": "uint256"
}
],
"name": "CycleBuy",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "strategyId",
"type": "uint256"
}
],
"name": "DefaultStrategySet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "active",
"type": "bool"
},
{
"indexed": false,
"internalType": "bool",
"name": "vetted",
"type": "bool"
}
],
"name": "MenuUpdated",
"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": "holder",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "stock",
"type": "address"
}
],
"name": "PreferenceSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "holder",
"type": "address"
},
{
"indexed": true,
"internalType": "uint256",
"name": "strategyId",
"type": "uint256"
}
],
"name": "StrategyAdopted",
"type": "event"
},
{
"inputs": [],
"name": "BASE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "CARRY_CREATOR_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "CARRY_PLATFORM_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "ZEROX",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "active",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "activeStrategies",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "activeStrategyCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "s",
"type": "address"
}
],
"name": "addStock",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "adoptStrategy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "buyer",
"outputs": [
{
"internalType": "contract IBuyer",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "carryCredits",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "carryExempt",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "sid",
"type": "uint256"
}
],
"name": "claimCarry",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "h",
"type": "address"
}
],
"name": "claimForSelf",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "h",
"type": "address"
},
{
"internalType": "address",
"name": "stock",
"type": "address"
}
],
"name": "claimable",
"outputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "cycler",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultStock",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultStrategyId",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "holderCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "holderList",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "holders",
"outputs": [
{
"internalType": "uint256",
"name": "shares",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "matchShares",
"type": "uint256"
},
{
"internalType": "address",
"name": "pref",
"type": "address"
},
{
"internalType": "uint32",
"name": "stratId",
"type": "uint32"
},
{
"internalType": "uint64",
"name": "vestStart",
"type": "uint64"
},
{
"internalType": "uint256",
"name": "idxBase",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "idxMatch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "storedCredits",
"type": "uint256"
},
{
"internalType": "uint32",
"name": "listPos",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "magBase",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "magMatch",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "magStratBase",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "magStratMatch",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "menu",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "menuLength",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "minBalanceForRewards",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "onMenu",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "opsWallet",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "outstanding",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxEthPerStock",
"type": "uint256"
}
],
"name": "previewCycle",
"outputs": [
{
"components": [
{
"internalType": "bool",
"name": "strat",
"type": "bool"
},
{
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"internalType": "uint256",
"name": "sid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "ethBase",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "ethMatch",
"type": "uint256"
}
],
"internalType": "struct StockPayDistributor.Leg[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxSteps",
"type": "uint256"
}
],
"name": "pruneStrategies",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "count",
"type": "uint256"
}
],
"name": "pushBatch",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "pushCursor",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "h",
"type": "address"
}
],
"name": "refresh",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "renounceOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "maxEthPerStock",
"type": "uint256"
}
],
"name": "runCycle",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "bool",
"name": "strat",
"type": "bool"
},
{
"internalType": "address",
"name": "stock",
"type": "address"
},
{
"internalType": "uint256",
"name": "sid",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "ethBase",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "ethMatch",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "minOut",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "data",
"type": "bytes"
}
],
"internalType": "struct StockPayDistributor.ZBuy[]",
"name": "buys",
"type": "tuple[]"
},
{
"internalType": "uint256",
"name": "maxEthPerStock",
"type": "uint256"
}
],
"name": "runCycleZeroX",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "s",
"type": "address"
},
{
"internalType": "bool",
"name": "a",
"type": "bool"
}
],
"name": "setActive",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "b",
"type": "address"
}
],
"name": "setBuyer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "sid",
"type": "uint256"
},
{
"internalType": "bool",
"name": "exempt",
"type": "bool"
}
],
"name": "setCarryExempt",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "c",
"type": "address"
}
],
"name": "setCycler",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"name": "setDefaultStrategy",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "v",
"type": "uint256"
}
],
"name": "setMinBalanceForRewards",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "o",
"type": "address"
}
],
"name": "setOpsWallet",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "stock",
"type": "address"
}
],
"name": "setPreference",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "s",
"type": "address"
}
],
"name": "setStrategies",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "s",
"type": "address"
},
{
"internalType": "bool",
"name": "v",
"type": "bool"
}
],
"name": "setVetted",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "stratListed",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "strategies",
"outputs": [
{
"internalType": "contract IStrategies",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "fromShares",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "toShares",
"type": "uint256"
}
],
"name": "syncShares",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "tierCutoffs",
"outputs": [
{
"internalType": "uint32",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "h",
"type": "address"
}
],
"name": "tierWeightOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "tierWeights",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "totalBaseShares",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "totalMatchShares",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "totalStratBaseShares",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "totalStratMatchShares",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "unallocated",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "vault",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "vetted",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "withdrawable",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
//
// QUANT
// https://quantrh.com
// https://x.com/Quantrhood
//
pragma solidity ^0.8.26;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
interface IBuyer {
function buy(address stock, uint256 ethAmount, uint256 minOut, address recipient) external payable returns (uint256);
}
interface IStrategies {
function creatorOf(uint256 id) external view returns (address);
function entries(uint256 id) external view returns (address[] memory stocks, uint16[] memory weightsBps);
function count() external view returns (uint256);
}
contract StockPayDistributor is Ownable, ReentrancyGuard {
using SafeERC20 for IERC20;
uint256 private constant MAG = 2 ** 128;
uint256 public constant BASE_BPS = 8000;
uint256 public constant CARRY_CREATOR_BPS = 400;
uint256 public constant CARRY_PLATFORM_BPS = 100;
uint16[5] public tierWeights = [0, 100, 125, 150, 200];
uint32[5] public tierCutoffs = [0, 3 days, 7 days, 14 days, 30 days];
uint256 private constant RESET_SELL_BPS = 2500;
address public constant ZEROX = 0x0000000000001fF3684f28c67538d4D072C22734;
uint256 private constant ALLOC_TOL = 105;
struct ZBuy { bool strat; address stock; uint256 sid; uint256 ethBase; uint256 ethMatch; uint256 minOut; bytes data; }
struct Leg { bool strat; address stock; uint256 sid; uint256 ethBase; uint256 ethMatch; }
address public immutable token;
address public opsWallet;
IBuyer public buyer;
address public cycler;
IStrategies public strategies;
address[] public menu;
mapping(address => bool) public onMenu;
mapping(address => bool) public active;
mapping(address => bool) public vetted;
address public defaultStock;
uint32 public defaultStrategyId;
uint256 public minBalanceForRewards = 10_000 ether;
mapping(address => uint256) public magBase;
mapping(address => uint256) public magMatch;
mapping(address => uint256) public totalBaseShares;
mapping(address => uint256) public totalMatchShares;
mapping(address => uint256) public unallocated;
mapping(uint256 => uint256) public magStratBase;
mapping(uint256 => uint256) public magStratMatch;
mapping(uint256 => uint256) public totalStratBaseShares;
mapping(uint256 => uint256) public totalStratMatchShares;
mapping(uint256 => mapping(address => uint256)) public vault;
mapping(uint256 => uint256) public outstanding;
uint256[] public activeStrategies;
mapping(uint256 => bool) public stratListed;
mapping(address => mapping(uint256 => uint256)) public carryCredits;
mapping(uint256 => bool) public carryExempt;
struct Holder {
uint256 shares;
uint256 matchShares;
address pref;
uint32 stratId;
uint64 vestStart;
uint256 idxBase;
uint256 idxMatch;
uint256 storedCredits;
uint32 listPos;
}
mapping(address => Holder) public holders;
mapping(address => mapping(address => uint256)) public withdrawable;
address[] public holderList;
uint256 public pushCursor;
event PreferenceSet(address indexed holder, address indexed stock);
event StrategyAdopted(address indexed holder, uint256 indexed strategyId);
event MenuUpdated(address indexed stock, bool active, bool vetted);
event DefaultStrategySet(uint256 indexed strategyId);
event Claimed(address indexed holder, address indexed stock, uint256 amount);
event CarryAccrued(address indexed recipient, uint256 indexed strategyId, uint256 credits);
event ClockReset(address indexed holder);
event CycleBuy(bool strat, address indexed stock, uint256 indexed sid, uint256 ethSpent, uint256 got);
event BuyerSet(address indexed buyer);
error OnlyToken();
error NotOnMenu();
error NoBuyer();
error BadStrategy();
modifier onlyToken() { if (msg.sender != token) revert OnlyToken(); _; }
constructor(address token_, address defaultStock_, address ops_) Ownable(msg.sender) {
require(token_ != address(0) && defaultStock_ != address(0) && ops_ != address(0), "zero addr");
token = token_;
opsWallet = ops_;
_addStock(defaultStock_);
_addStock(token_);
defaultStock = defaultStock_;
}
receive() external payable {}
function _addStock(address s) internal {
if (!onMenu[s]) { menu.push(s); onMenu[s] = true; }
active[s] = true;
vetted[s] = true;
emit MenuUpdated(s, true, true);
}
function addStock(address s) external onlyOwner { _addStock(s); }
function setActive(address s, bool a) external onlyOwner {
if (!onMenu[s]) revert NotOnMenu();
active[s] = a; emit MenuUpdated(s, a, vetted[s]);
}
function setVetted(address s, bool v) external onlyOwner {
if (!onMenu[s]) revert NotOnMenu();
vetted[s] = v; emit MenuUpdated(s, active[s], v);
}
function setBuyer(address b) external onlyOwner {
require(address(buyer) == address(0) && b != address(0), "set once");
buyer = IBuyer(b); emit BuyerSet(b);
}
function setCycler(address c) external onlyOwner { cycler = c; }
function setOpsWallet(address o) external onlyOwner { opsWallet = o; }
function setMinBalanceForRewards(uint256 v) external onlyOwner { minBalanceForRewards = v; }
function setStrategies(address s) external onlyOwner {
require(address(strategies) == address(0), "set once");
strategies = IStrategies(s);
}
function setDefaultStrategy(uint256 id) external onlyOwner {
require(id == 0 || (address(strategies) != address(0) && id > 0 && id < strategies.count()), "bad strategy");
defaultStrategyId = uint32(id);
if (id != 0) carryExempt[id] = true;
emit DefaultStrategySet(id);
}
function setCarryExempt(uint256 sid, bool exempt) external onlyOwner { carryExempt[sid] = exempt; }
function menuLength() external view returns (uint256) { return menu.length; }
function holderCount() external view returns (uint256) { return holderList.length; }
function activeStrategyCount() external view returns (uint256) { return activeStrategies.length; }
function tierWeightOf(address h) public view returns (uint256) {
Holder storage H = holders[h];
if (H.shares == 0) return 0;
uint256 e = block.timestamp - H.vestStart;
for (uint256 i = tierCutoffs.length; i > 0; i--) if (e >= tierCutoffs[i - 1]) return tierWeights[i - 1];
return 0;
}
function _settle(address h) internal {
Holder storage H = holders[h];
if (H.stratId != 0) {
uint256 sid = H.stratId;
uint256 pendBase = H.shares * (magStratBase[sid] - H.idxBase) / MAG;
uint256 pendMatch = H.matchShares * (magStratMatch[sid] - H.idxMatch) / MAG;
if (pendMatch > 0) {
address creator = strategies.creatorOf(sid);
if (creator != h && !carryExempt[sid]) {
uint256 cC = pendMatch * CARRY_CREATOR_BPS / 10_000;
uint256 cP = pendMatch * CARRY_PLATFORM_BPS / 10_000;
if (cC > 0) { carryCredits[creator][sid] += cC; emit CarryAccrued(creator, sid, cC); }
if (cP > 0) { carryCredits[opsWallet][sid] += cP; emit CarryAccrued(opsWallet, sid, cP); }
pendMatch -= cC + cP;
}
}
H.storedCredits += pendBase + pendMatch;
H.idxBase = magStratBase[sid];
H.idxMatch = magStratMatch[sid];
} else {
address p = H.pref;
if (p == address(0)) return;
uint256 pb = H.shares * (magBase[p] - H.idxBase) / MAG;
uint256 pm = H.matchShares * (magMatch[p] - H.idxMatch) / MAG;
if (pb + pm > 0) withdrawable[h][p] += pb + pm;
H.idxBase = magBase[p];
H.idxMatch = magMatch[p];
}
}
function _removeFromPools(address h) internal {
Holder storage H = holders[h];
if (H.stratId != 0) {
totalStratBaseShares[H.stratId] -= H.shares;
totalStratMatchShares[H.stratId] -= H.matchShares;
} else if (H.pref != address(0)) {
totalBaseShares[H.pref] -= H.shares;
totalMatchShares[H.pref] -= H.matchShares;
}
}
function _addToPools(address h) internal {
Holder storage H = holders[h];
if (H.stratId != 0) {
uint256 sid = H.stratId;
totalStratBaseShares[sid] += H.shares;
totalStratMatchShares[sid] += H.matchShares;
H.idxBase = magStratBase[sid];
H.idxMatch = magStratMatch[sid];
if (!stratListed[sid] && H.shares > 0) { stratListed[sid] = true; activeStrategies.push(sid); }
} else {
address p = H.pref;
totalBaseShares[p] += H.shares;
totalMatchShares[p] += H.matchShares;
H.idxBase = magBase[p];
H.idxMatch = magMatch[p];
}
}
function syncShares(address from, address to, uint256 fromShares, uint256 toShares) external onlyToken {
if (from != address(0)) _syncOne(from, fromShares, true);
if (to != address(0) && to != from) _syncOne(to, toShares, false);
}
function _syncOne(address h, uint256 newShares, bool isSender) internal {
if (newShares < minBalanceForRewards) newShares = 0;
Holder storage H = holders[h];
_settle(h);
_removeFromPools(h);
uint256 prev = H.shares;
if (isSender && newShares < prev) {
if (newShares == 0 || (prev - newShares) * 10_000 / prev > RESET_SELL_BPS) {
H.vestStart = uint64(block.timestamp);
} else {
H.vestStart = uint64(block.timestamp - ((block.timestamp - H.vestStart) * newShares / prev));
}
emit ClockReset(h);
} else if (newShares > prev) {
if (prev == 0) H.vestStart = uint64(block.timestamp);
else {
uint256 e = block.timestamp - H.vestStart;
H.vestStart = uint64(block.timestamp - (e * prev / newShares));
}
}
if (H.pref == address(0) && H.stratId == 0) {
if (defaultStrategyId != 0) H.stratId = defaultStrategyId;
else H.pref = defaultStock;
}
uint256 w;
if (newShares > 0) {
uint256 e2 = block.timestamp - H.vestStart;
for (uint256 i = tierCutoffs.length; i > 0; i--) if (e2 >= tierCutoffs[i - 1]) { w = tierWeights[i - 1]; break; }
}
H.shares = newShares;
H.matchShares = newShares * w / 100;
_addToPools(h);
if (newShares > 0 && H.listPos == 0) { holderList.push(h); H.listPos = uint32(holderList.length); }
}
function refresh(address h) public {
Holder storage H = holders[h];
if (H.shares == 0) return;
_settle(h);
_removeFromPools(h);
H.matchShares = H.shares * tierWeightOf(h) / 100;
_addToPools(h);
}
function _flushStrategy(address h) internal {
Holder storage H = holders[h];
if (H.stratId != 0 && H.storedCredits > 0) {
uint256 c = H.storedCredits;
H.storedCredits = 0;
_redeem(h, H.stratId, c, false);
}
}
function setPreference(address stock) external nonReentrant {
if (!onMenu[stock] || !active[stock]) revert NotOnMenu();
_settle(msg.sender);
_flushStrategy(msg.sender);
_removeFromPools(msg.sender);
Holder storage H = holders[msg.sender];
H.stratId = 0;
H.pref = stock;
H.matchShares = H.shares * tierWeightOf(msg.sender) / 100;
_addToPools(msg.sender);
emit PreferenceSet(msg.sender, stock);
}
function adoptStrategy(uint256 id) external nonReentrant {
if (address(strategies) == address(0) || id == 0 || id >= strategies.count()) revert BadStrategy();
_settle(msg.sender);
if (holders[msg.sender].stratId != uint32(id)) _flushStrategy(msg.sender);
_removeFromPools(msg.sender);
Holder storage H = holders[msg.sender];
H.stratId = uint32(id);
H.matchShares = H.shares * tierWeightOf(msg.sender) / 100;
_addToPools(msg.sender);
emit StrategyAdopted(msg.sender, id);
}
function _distributeSingle(address stock, uint256 baseGot, uint256 matchGot) internal {
uint256 tb = totalBaseShares[stock];
if (tb == 0) { unallocated[stock] += baseGot + matchGot; return; }
baseGot += unallocated[stock];
unallocated[stock] = 0;
magBase[stock] += baseGot * MAG / tb;
uint256 tm = totalMatchShares[stock];
if (tm > 0 && matchGot > 0) magMatch[stock] += matchGot * MAG / tm;
else if (matchGot > 0) magBase[stock] += matchGot * MAG / tb;
}
function _tryBuy(address s, uint256 eth, address to) internal returns (uint256 got) {
try buyer.buy{value: eth}(s, eth, 0, to) returns (uint256 g) { return g; } catch { return 0; }
}
function runCycle(uint256 maxEthPerStock) external nonReentrant {
require(msg.sender == cycler || msg.sender == owner(), "not cycler");
if (address(buyer) == address(0)) revert NoBuyer();
uint256 pot = address(this).balance;
if (pot == 0) return;
uint256 sumBase; uint256 sumMatch;
for (uint256 i = 0; i < menu.length; i++) {
if (!active[menu[i]]) continue;
sumBase += totalBaseShares[menu[i]];
sumMatch += totalMatchShares[menu[i]];
}
for (uint256 i = 0; i < activeStrategies.length; i++) {
uint256 sid = activeStrategies[i];
sumBase += totalStratBaseShares[sid];
sumMatch += totalStratMatchShares[sid];
}
if (sumBase == 0) return;
uint256 basePot = pot * BASE_BPS / 10_000;
uint256 matchPot = pot - basePot;
if (sumMatch == 0) { basePot = pot; matchPot = 0; }
for (uint256 i = 0; i < menu.length; i++) {
address s = menu[i];
if (!active[s]) continue;
uint256 ethBase = basePot * totalBaseShares[s] / sumBase;
uint256 ethMatch = sumMatch > 0 ? matchPot * totalMatchShares[s] / sumMatch : 0;
uint256 eth = ethBase + ethMatch;
if (eth == 0) continue;
if (eth > maxEthPerStock) { ethBase = ethBase * maxEthPerStock / eth; ethMatch = maxEthPerStock - ethBase; eth = maxEthPerStock; }
uint256 got = _tryBuy(s, eth, address(this));
if (got == 0) continue;
_distributeSingle(s, got * ethBase / eth, got - (got * ethBase / eth));
emit CycleBuy(false, s, 0, eth, got);
}
for (uint256 i = 0; i < activeStrategies.length; ) {
uint256 sid = activeStrategies[i];
uint256 tb = totalStratBaseShares[sid];
if (tb == 0) {
stratListed[sid] = false;
activeStrategies[i] = activeStrategies[activeStrategies.length - 1];
activeStrategies.pop();
continue;
}
uint256 tm = totalStratMatchShares[sid];
uint256 ethBase = basePot * tb / sumBase;
uint256 ethMatch = sumMatch > 0 ? matchPot * tm / sumMatch : 0;
uint256 ethTotal = ethBase + ethMatch;
if (ethTotal == 0) { unchecked { ++i; } continue; }
(address[] memory stk, uint16[] memory wt) = strategies.entries(sid);
uint256 used;
for (uint256 j = 0; j < stk.length; j++) {
if (!active[stk[j]] || !vetted[stk[j]]) continue;
uint256 legEth = ethTotal * wt[j] / 10_000;
if (legEth == 0) continue;
if (legEth > maxEthPerStock) legEth = maxEthPerStock;
uint256 got = _tryBuy(stk[j], legEth, address(this));
if (got > 0) { vault[sid][stk[j]] += got; used += legEth; emit CycleBuy(true, stk[j], sid, legEth, got); }
}
if (used == 0) { unchecked { ++i; } continue; }
uint256 uBase = ethBase * used / ethTotal;
uint256 uMatch = used - uBase;
magStratBase[sid] += uBase * MAG / tb;
if (tm > 0) magStratMatch[sid] += uMatch * MAG / tm;
else magStratBase[sid] += uMatch * MAG / tb;
outstanding[sid] += used;
unchecked { ++i; }
}
}
function _execZeroX(address stock, uint256 eth, uint256 minOut, bytes calldata data) internal returns (uint256 got) {
if (eth == 0 || eth > address(this).balance) return 0;
uint256 bal = IERC20(stock).balanceOf(address(this));
(bool ok, ) = ZEROX.call{value: eth}(data);
if (!ok) return 0;
got = IERC20(stock).balanceOf(address(this)) - bal;
require(minOut > 0 && got >= minOut, "0x: bad fill / misdeliver");
}
function runCycleZeroX(ZBuy[] calldata buys, uint256 maxEthPerStock) external nonReentrant {
require(msg.sender == cycler || msg.sender == owner(), "not cycler");
Leg[] memory canon = _computeLegs(maxEthPerStock);
bool[] memory used = new bool[](canon.length);
for (uint256 i = 0; i < buys.length; i++) {
ZBuy calldata b = buys[i];
if (!active[b.stock]) continue;
if (b.strat && !vetted[b.stock]) continue;
uint256 j = canon.length;
for (uint256 c = 0; c < canon.length; c++) {
if (!used[c] && canon[c].strat == b.strat && canon[c].stock == b.stock && canon[c].sid == b.sid) { j = c; break; }
}
if (j == canon.length) continue;
used[j] = true;
if (b.ethBase > canon[j].ethBase * ALLOC_TOL / 100) continue;
if (b.ethMatch > canon[j].ethMatch * ALLOC_TOL / 100) continue;
uint256 eth = b.ethBase + b.ethMatch;
if (!b.strat) {
uint256 got = _execZeroX(b.stock, eth, b.minOut, b.data);
if (got == 0) continue;
uint256 baseGot = got * b.ethBase / eth;
_distributeSingle(b.stock, baseGot, got - baseGot);
emit CycleBuy(false, b.stock, 0, eth, got);
} else {
uint256 sid = b.sid;
uint256 tb = totalStratBaseShares[sid];
if (tb == 0) continue;
uint256 got = _execZeroX(b.stock, eth, b.minOut, b.data);
if (got == 0) continue;
vault[sid][b.stock] += got;
uint256 tm = totalStratMatchShares[sid];
magStratBase[sid] += b.ethBase * MAG / tb;
if (tm > 0) magStratMatch[sid] += b.ethMatch * MAG / tm;
else magStratBase[sid] += b.ethMatch * MAG / tb;
outstanding[sid] += eth;
emit CycleBuy(true, b.stock, sid, eth, got);
}
}
}
function pruneStrategies(uint256 maxSteps) external {
uint256 i = 0; uint256 steps = 0;
while (i < activeStrategies.length && steps < maxSteps) {
uint256 sid = activeStrategies[i];
if (totalStratBaseShares[sid] == 0) {
stratListed[sid] = false;
activeStrategies[i] = activeStrategies[activeStrategies.length - 1];
activeStrategies.pop();
} else { i++; }
steps++;
}
}
function previewCycle(uint256 maxEthPerStock) external view returns (Leg[] memory) {
return _computeLegs(maxEthPerStock);
}
function _computeLegs(uint256 maxEthPerStock) internal view returns (Leg[] memory legs) {
uint256 pot = address(this).balance;
if (pot == 0) return new Leg[](0);
uint256 sumBase; uint256 sumMatch;
for (uint256 i = 0; i < menu.length; i++) {
if (!active[menu[i]]) continue;
sumBase += totalBaseShares[menu[i]];
sumMatch += totalMatchShares[menu[i]];
}
for (uint256 i = 0; i < activeStrategies.length; i++) {
sumBase += totalStratBaseShares[activeStrategies[i]];
sumMatch += totalStratMatchShares[activeStrategies[i]];
}
if (sumBase == 0) return new Leg[](0);
uint256 basePot = pot * BASE_BPS / 10_000;
uint256 matchPot = pot - basePot;
if (sumMatch == 0) { basePot = pot; matchPot = 0; }
uint256 cnt;
for (uint256 i = 0; i < menu.length; i++) {
address s = menu[i];
if (!active[s]) continue;
uint256 eb = basePot * totalBaseShares[s] / sumBase;
uint256 em = sumMatch > 0 ? matchPot * totalMatchShares[s] / sumMatch : 0;
if (eb + em > 0) cnt++;
}
for (uint256 i = 0; i < activeStrategies.length; i++) {
uint256 sid = activeStrategies[i];
uint256 tb = totalStratBaseShares[sid];
if (tb == 0) continue;
uint256 eb = basePot * tb / sumBase;
uint256 em = sumMatch > 0 ? matchPot * totalStratMatchShares[sid] / sumMatch : 0;
uint256 tot = eb + em;
if (tot == 0) continue;
(address[] memory stk, uint16[] memory wt) = strategies.entries(sid);
for (uint256 j = 0; j < stk.length; j++) {
if (!active[stk[j]] || !vetted[stk[j]]) continue;
if (eb * wt[j] / 10_000 + em * wt[j] / 10_000 > 0) cnt++;
}
}
legs = new Leg[](cnt);
uint256 k;
for (uint256 i = 0; i < menu.length; i++) {
address s = menu[i];
if (!active[s]) continue;
uint256 eb = basePot * totalBaseShares[s] / sumBase;
uint256 em = sumMatch > 0 ? matchPot * totalMatchShares[s] / sumMatch : 0;
uint256 eth = eb + em;
if (eth == 0) continue;
if (eth > maxEthPerStock) { eb = eb * maxEthPerStock / eth; em = maxEthPerStock - eb; }
legs[k++] = Leg(false, s, 0, eb, em);
}
for (uint256 i = 0; i < activeStrategies.length; i++) {
uint256 sid = activeStrategies[i];
uint256 tb = totalStratBaseShares[sid];
if (tb == 0) continue;
uint256 eb = basePot * tb / sumBase;
uint256 em = sumMatch > 0 ? matchPot * totalStratMatchShares[sid] / sumMatch : 0;
uint256 tot = eb + em;
if (tot == 0) continue;
(address[] memory stk, uint16[] memory wt) = strategies.entries(sid);
for (uint256 j = 0; j < stk.length; j++) {
if (!active[stk[j]] || !vetted[stk[j]]) continue;
uint256 lb = eb * wt[j] / 10_000;
uint256 lm = em * wt[j] / 10_000;
uint256 le = lb + lm;
if (le == 0) continue;
if (le > maxEthPerStock) { lb = lb * maxEthPerStock / le; lm = maxEthPerStock - lb; }
legs[k++] = Leg(true, stk[j], sid, lb, lm);
}
}
}
function claim() external nonReentrant { _claimFor(msg.sender); }
function _claimFor(address h) internal {
_settle(h);
Holder storage H = holders[h];
for (uint256 i = 0; i < menu.length; i++) {
address s = menu[i];
uint256 amt = withdrawable[h][s];
if (amt == 0) continue;
withdrawable[h][s] = 0;
IERC20(s).safeTransfer(h, amt);
emit Claimed(h, s, amt);
}
if (H.stratId != 0 && H.storedCredits > 0) {
uint256 c = H.storedCredits;
H.storedCredits = 0;
if (!_redeem(h, H.stratId, c, false)) H.storedCredits = c;
}
}
function _redeem(address to, uint256 sid, uint256 credits, bool toWithdrawable) internal returns (bool paidAny) {
uint256 out = outstanding[sid];
if (out == 0 || credits == 0) return false;
(address[] memory stk, ) = strategies.entries(sid);
uint256 n = stk.length;
uint256[] memory gives = new uint256[](n);
for (uint256 j = 0; j < n; j++) {
uint256 g = vault[sid][stk[j]] * credits / out;
if (g > 0) { gives[j] = g; paidAny = true; }
}
if (!paidAny) return false;
outstanding[sid] = out - credits;
for (uint256 j = 0; j < n; j++) {
if (gives[j] == 0) continue;
vault[sid][stk[j]] -= gives[j];
if (toWithdrawable) withdrawable[to][stk[j]] += gives[j];
else { IERC20(stk[j]).safeTransfer(to, gives[j]); emit Claimed(to, stk[j], gives[j]); }
}
return true;
}
function claimCarry(uint256 sid) external nonReentrant {
uint256 c = carryCredits[msg.sender][sid];
if (c == 0) return;
carryCredits[msg.sender][sid] = 0;
if (!_redeem(msg.sender, sid, c, false)) carryCredits[msg.sender][sid] = c;
}
function claimForSelf(address h) external {
require(msg.sender == address(this), "only self");
_claimFor(h);
}
function pushBatch(uint256 count) external nonReentrant {
uint256 n = holderList.length;
if (n == 0) return;
for (uint256 i = 0; i < count; i++) {
if (pushCursor >= n) pushCursor = 0;
try this.claimForSelf(holderList[pushCursor]) {} catch {}
pushCursor++;
}
}
function claimable(address h, address stock) external view returns (uint256 amount) {
amount = withdrawable[h][stock];
Holder storage H = holders[h];
if (H.stratId == 0) {
address p = H.pref;
if (p == stock) {
amount += H.shares * (magBase[stock] - H.idxBase) / MAG
+ H.matchShares * (magMatch[stock] - H.idxMatch) / MAG;
}
} else {
uint256 sid = H.stratId;
uint256 pendBase = H.shares * (magStratBase[sid] - H.idxBase) / MAG;
uint256 pendMatch = H.matchShares * (magStratMatch[sid] - H.idxMatch) / MAG;
if (pendMatch > 0 && strategies.creatorOf(sid) != h && !carryExempt[sid]) {
pendMatch -= pendMatch * (CARRY_CREATOR_BPS + CARRY_PLATFORM_BPS) / 10_000;
}
uint256 credits = H.storedCredits + pendBase + pendMatch;
uint256 out = outstanding[sid];
if (out > 0 && credits > 0) amount += vault[sid][stock] * credits / out;
}
}
}
Chain explorer4223msChain node73ms