TenbaggerEdition
0xd1065f7eeec3565644b9e754abfc04b0dec6f062
Verification
Verified
v0.8.30+commit.73712a01
Type
Contract
18,001 bytes
ABI entries
143
58 read · 22 write
License
none
Contract information
- Address
- 0xd1065f7eeec3565644b9e754abfc04b0dec6f062
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.30+commit.73712a01
- Optimization
- Enabled
- Creator
- 0x068A09E093…6e36cda584
- Creation tx
- 0x2739a4a4aa…011fb73537
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (58)
CONFIGURATOR_ROLE() → bytes32
DEFAULT_ADMIN_ROLE() → bytes32
MAX_CONFIG_BATCH() → uint8
MAX_STOCK_TOKENS() → uint8
PAUSER_ROLE() → bytes32
PURCHASE_AUTHORIZATION_TYPEHASH() → bytes32
STOCKS_PER_PACK() → uint8
STOCK_UNIT() → uint256
TIER_COUNT() → uint8
assignedInventory(uint256) → uint256
authorizationDigest(tuple) → bytes32
claimedInventory(uint256) → uint256
configuredPacks() → uint16
configuredTierCounts(uint256) → uint16
defaultAdmin() → address
defaultAdminDelay() → uint48
defaultAdminDelayIncreaseWait() → uint48
editionName() → string
editionState() → uint8
eip712Domain() → bytes1, string, string, uint256, address, bytes32, uint256[]
eligibilitySigner() → address
expectedTierCounts(uint256) → uint16
getRoleAdmin(bytes32) → bytes32
hasRole(bytes32, address) → bool
inventorySolvent() → bool
manifestHash() → bytes32
nextPurchaseId() → uint256
owner() → address
packContents(uint16) → uint8, address[3], uint256[3], uint24
packPrice() → uint256
packedPack(uint16) → uint256
packsByBuyer(address) → uint16
paused() → bool
pendingDefaultAdmin() → address, uint48
pendingDefaultAdminDelay() → uint48, uint48
pendingPurchases() → uint16
perWalletLimit() → uint8
purchaseForRandomness(bytes32) → uint256
purchases(uint256) → address, uint64, uint16, uint8
randomnessAdapter() → address
randomnessTimeout() → uint64
referenceInventoryCents() → uint256
remainingSupply() → uint16
remainingTierCounts(uint256) → uint16
requiredInventory(uint256) → uint256
reservedPacks() → uint16
saleDuration() → uint64
saleEndsAt() → uint64
settledProceeds() → uint256
stockToken(uint256) → address
stockTokenCount() → uint256
supportsInterface(bytes4) → bool
tierValueCents(uint256) → uint24
totalSupply() → uint16
treasury() → address
usdg() → address
usedAuthorization(bytes32) → bool
withdrawnProceeds() → uint256
Events (23)
DefaultAdminDelayChangeCanceledDefaultAdminDelayChangeScheduledDefaultAdminTransferCanceledDefaultAdminTransferScheduledEIP712DomainChangedEditionActivatedEditionFinalizedEligibilitySignerUpdatedPackAssignedPackClaimedPackConfiguredPackDeliveryDeferredPackPurchasedPackRefundedPausedProceedsWithdrawnRandomnessAdapterSetRandomnessIgnoredRoleAdminChangedRoleGrantedRoleRevokedUnpausedUnsoldInventoryWithdrawn
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "editionName_",
"type": "string"
},
{
"internalType": "address",
"name": "admin_",
"type": "address"
},
{
"internalType": "address",
"name": "treasury_",
"type": "address"
},
{
"internalType": "address",
"name": "usdg_",
"type": "address"
},
{
"internalType": "address",
"name": "eligibilitySigner_",
"type": "address"
},
{
"internalType": "uint256",
"name": "packPrice_",
"type": "uint256"
},
{
"internalType": "uint16",
"name": "totalSupply_",
"type": "uint16"
},
{
"internalType": "uint8",
"name": "perWalletLimit_",
"type": "uint8"
},
{
"internalType": "uint64",
"name": "saleDuration_",
"type": "uint64"
},
{
"internalType": "uint64",
"name": "randomnessTimeout_",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "manifestHash_",
"type": "bytes32"
},
{
"internalType": "uint16[4]",
"name": "expectedTierCounts_",
"type": "uint16[4]"
},
{
"internalType": "uint24[4]",
"name": "tierValueCents_",
"type": "uint24[4]"
},
{
"internalType": "address[]",
"name": "stockTokens_",
"type": "address[]"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "AccessControlBadConfirmation",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint48",
"name": "schedule",
"type": "uint48"
}
],
"name": "AccessControlEnforcedDefaultAdminDelay",
"type": "error"
},
{
"inputs": [],
"name": "AccessControlEnforcedDefaultAdminRules",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "defaultAdmin",
"type": "address"
}
],
"name": "AccessControlInvalidDefaultAdmin",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
},
{
"internalType": "bytes32",
"name": "neededRole",
"type": "bytes32"
}
],
"name": "AccessControlUnauthorizedAccount",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "packId",
"type": "uint16"
}
],
"name": "AlreadyConfigured",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint64",
"name": "expiresAt",
"type": "uint64"
}
],
"name": "AuthorizationExpired",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
}
],
"name": "AuthorizationUsed",
"type": "error"
},
{
"inputs": [],
"name": "BadAuthorization",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "packId",
"type": "uint16"
}
],
"name": "BadPackEncoding",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint8",
"name": "tier",
"type": "uint8"
},
{
"internalType": "uint16",
"name": "configured",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "expected",
"type": "uint16"
}
],
"name": "BadTierCount",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "configured",
"type": "uint16"
},
{
"internalType": "uint16",
"name": "expected",
"type": "uint16"
}
],
"name": "ConfigIncomplete",
"type": "error"
},
{
"inputs": [],
"name": "EditionNotActive",
"type": "error"
},
{
"inputs": [],
"name": "EditionNotConfiguring",
"type": "error"
},
{
"inputs": [],
"name": "EnforcedPause",
"type": "error"
},
{
"inputs": [],
"name": "ExpectedPause",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "supplied",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "required",
"type": "uint256"
}
],
"name": "FeeMismatch",
"type": "error"
},
{
"inputs": [],
"name": "InvalidConfiguration",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "adapter",
"type": "address"
}
],
"name": "InvalidRandomnessAdapter",
"type": "error"
},
{
"inputs": [],
"name": "InvalidShortString",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"internalType": "enum TenbaggerEdition.PurchaseStatus",
"name": "status",
"type": "uint8"
}
],
"name": "InvalidStatus",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "required",
"type": "uint256"
}
],
"name": "InventoryInsolvent",
"type": "error"
},
{
"inputs": [],
"name": "NoProceeds",
"type": "error"
},
{
"inputs": [],
"name": "OnlySelf",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "OraclePaused",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
}
],
"name": "PackLimitReached",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint64",
"name": "availableAt",
"type": "uint64"
}
],
"name": "RefundNotReady",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint8",
"name": "bits",
"type": "uint8"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "SafeCastOverflowedUintDowncast",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "SafeERC20FailedOperation",
"type": "error"
},
{
"inputs": [],
"name": "SaleClosed",
"type": "error"
},
{
"inputs": [],
"name": "SaleNotEnded",
"type": "error"
},
{
"inputs": [],
"name": "SoldOut",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint8",
"name": "decimals",
"type": "uint8"
}
],
"name": "StockDecimals",
"type": "error"
},
{
"inputs": [
{
"internalType": "string",
"name": "str",
"type": "string"
}
],
"name": "StringTooLong",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "TokenPaused",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
}
],
"name": "UnknownRandomness",
"type": "error"
},
{
"inputs": [
{
"internalType": "uint8",
"name": "decimals",
"type": "uint8"
}
],
"name": "UsdgDecimals",
"type": "error"
},
{
"inputs": [],
"name": "ZeroAddress",
"type": "error"
},
{
"anonymous": false,
"inputs": [],
"name": "DefaultAdminDelayChangeCanceled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint48",
"name": "newDelay",
"type": "uint48"
},
{
"indexed": false,
"internalType": "uint48",
"name": "effectSchedule",
"type": "uint48"
}
],
"name": "DefaultAdminDelayChangeScheduled",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "DefaultAdminTransferCanceled",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "newAdmin",
"type": "address"
},
{
"indexed": false,
"internalType": "uint48",
"name": "acceptSchedule",
"type": "uint48"
}
],
"name": "DefaultAdminTransferScheduled",
"type": "event"
},
{
"anonymous": false,
"inputs": [],
"name": "EIP712DomainChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint64",
"name": "saleEndsAt",
"type": "uint64"
},
{
"indexed": false,
"internalType": "uint16",
"name": "totalSupply",
"type": "uint16"
}
],
"name": "EditionActivated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint16",
"name": "assignedPacks",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint16",
"name": "unsoldPacks",
"type": "uint16"
}
],
"name": "EditionFinalized",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousSigner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newSigner",
"type": "address"
}
],
"name": "EligibilitySignerUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"indexed": true,
"internalType": "uint16",
"name": "packId",
"type": "uint16"
},
{
"indexed": false,
"internalType": "uint8",
"name": "tier",
"type": "uint8"
},
{
"indexed": false,
"internalType": "uint24",
"name": "referenceValueCents",
"type": "uint24"
}
],
"name": "PackAssigned",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"indexed": true,
"internalType": "uint16",
"name": "packId",
"type": "uint16"
}
],
"name": "PackClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint16",
"name": "packId",
"type": "uint16"
},
{
"indexed": true,
"internalType": "uint8",
"name": "tier",
"type": "uint8"
}
],
"name": "PackConfigured",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
}
],
"name": "PackDeliveryDeferred",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
}
],
"name": "PackPurchased",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"indexed": true,
"internalType": "address",
"name": "buyer",
"type": "address"
}
],
"name": "PackRefunded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Paused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "treasury",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "ProceedsWithdrawn",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "adapter",
"type": "address"
}
],
"name": "RandomnessAdapterSet",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"name": "RandomnessIgnored",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "previousAdminRole",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "bytes32",
"name": "newAdminRole",
"type": "bytes32"
}
],
"name": "RoleAdminChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleGranted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"indexed": true,
"internalType": "address",
"name": "account",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "RoleRevoked",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "Unpaused",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "UnsoldInventoryWithdrawn",
"type": "event"
},
{
"inputs": [],
"name": "CONFIGURATOR_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "DEFAULT_ADMIN_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_CONFIG_BATCH",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_STOCK_TOKENS",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PAUSER_ROLE",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "PURCHASE_AUTHORIZATION_TYPEHASH",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "STOCKS_PER_PACK",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "STOCK_UNIT",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "TIER_COUNT",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "acceptDefaultAdminTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "activate",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "assignedInventory",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"internalType": "uint64",
"name": "expiresAt",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
}
],
"internalType": "struct TenbaggerEdition.PurchaseAuthorization",
"name": "authorization",
"type": "tuple"
}
],
"name": "authorizationDigest",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newAdmin",
"type": "address"
}
],
"name": "beginDefaultAdminTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"internalType": "uint64",
"name": "expiresAt",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
}
],
"internalType": "struct TenbaggerEdition.PurchaseAuthorization",
"name": "authorization",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "userEntropy",
"type": "bytes32"
}
],
"name": "buy",
"outputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"internalType": "uint64",
"name": "expiresAt",
"type": "uint64"
},
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
}
],
"internalType": "struct TenbaggerEdition.PurchaseAuthorization",
"name": "authorization",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "signature",
"type": "bytes"
},
{
"internalType": "bytes32",
"name": "userEntropy",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "permitDeadline",
"type": "uint256"
},
{
"internalType": "uint8",
"name": "permitV",
"type": "uint8"
},
{
"internalType": "bytes32",
"name": "permitR",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "permitS",
"type": "bytes32"
}
],
"name": "buyWithPermit",
"outputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "cancelDefaultAdminTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint48",
"name": "newDelay",
"type": "uint48"
}
],
"name": "changeDefaultAdminDelay",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"name": "claim",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "claimedInventory",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256[]",
"name": "packedPacks",
"type": "uint256[]"
}
],
"name": "configurePacks",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "configuredPacks",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "configuredTierCounts",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultAdmin",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultAdminDelay",
"outputs": [
{
"internalType": "uint48",
"name": "",
"type": "uint48"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "defaultAdminDelayIncreaseWait",
"outputs": [
{
"internalType": "uint48",
"name": "",
"type": "uint48"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"name": "deliverFromCallback",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "editionName",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "editionState",
"outputs": [
{
"internalType": "enum TenbaggerEdition.EditionState",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "eip712Domain",
"outputs": [
{
"internalType": "bytes1",
"name": "fields",
"type": "bytes1"
},
{
"internalType": "string",
"name": "name",
"type": "string"
},
{
"internalType": "string",
"name": "version",
"type": "string"
},
{
"internalType": "uint256",
"name": "chainId",
"type": "uint256"
},
{
"internalType": "address",
"name": "verifyingContract",
"type": "address"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
},
{
"internalType": "uint256[]",
"name": "extensions",
"type": "uint256[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "eligibilitySigner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "expectedTierCounts",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "finalizeSale",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "randomWord",
"type": "uint256"
}
],
"name": "fulfillRandomness",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
}
],
"name": "getRoleAdmin",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "grantRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "hasRole",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "inventorySolvent",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "manifestHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "nextPurchaseId",
"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": "uint16",
"name": "packId",
"type": "uint16"
}
],
"name": "packContents",
"outputs": [
{
"internalType": "uint8",
"name": "tier",
"type": "uint8"
},
{
"internalType": "address[3]",
"name": "tokens",
"type": "address[3]"
},
{
"internalType": "uint256[3]",
"name": "amounts",
"type": "uint256[3]"
},
{
"internalType": "uint24",
"name": "referenceValueCents",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "packPrice",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint16",
"name": "packId",
"type": "uint16"
}
],
"name": "packedPack",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
}
],
"name": "packsByBuyer",
"outputs": [
{
"internalType": "uint16",
"name": "count",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pausePurchases",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "paused",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingDefaultAdmin",
"outputs": [
{
"internalType": "address",
"name": "newAdmin",
"type": "address"
},
{
"internalType": "uint48",
"name": "schedule",
"type": "uint48"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingDefaultAdminDelay",
"outputs": [
{
"internalType": "uint48",
"name": "newDelay",
"type": "uint48"
},
{
"internalType": "uint48",
"name": "schedule",
"type": "uint48"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "pendingPurchases",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "perWalletLimit",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "requestId",
"type": "bytes32"
}
],
"name": "purchaseForRandomness",
"outputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"name": "purchases",
"outputs": [
{
"internalType": "address",
"name": "buyer",
"type": "address"
},
{
"internalType": "uint64",
"name": "requestedAt",
"type": "uint64"
},
{
"internalType": "uint16",
"name": "packId",
"type": "uint16"
},
{
"internalType": "enum TenbaggerEdition.PurchaseStatus",
"name": "status",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "randomnessAdapter",
"outputs": [
{
"internalType": "contract IRandomnessAdapter",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "randomnessTimeout",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "referenceInventoryCents",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "purchaseId",
"type": "uint256"
}
],
"name": "refundExpired",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "remainingSupply",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "remainingTierCounts",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "renounceRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "requiredInventory",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "reservedPacks",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "role",
"type": "bytes32"
},
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "revokeRole",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "rollbackDefaultAdminDelay",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "saleDuration",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "saleEndsAt",
"outputs": [
{
"internalType": "uint64",
"name": "",
"type": "uint64"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "signer",
"type": "address"
}
],
"name": "setEligibilitySigner",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "adapter",
"type": "address"
}
],
"name": "setRandomnessAdapter",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "settledProceeds",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "stockToken",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "stockTokenCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes4",
"name": "interfaceId",
"type": "bytes4"
}
],
"name": "supportsInterface",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"name": "tierValueCents",
"outputs": [
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint16",
"name": "",
"type": "uint16"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "treasury",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "unpausePurchases",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "usdg",
"outputs": [
{
"internalType": "contract IERC20",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes32",
"name": "nonce",
"type": "bytes32"
}
],
"name": "usedAuthorization",
"outputs": [
{
"internalType": "bool",
"name": "used",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "withdrawProceeds",
"outputs": [
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "withdrawnProceeds",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {
AccessControlDefaultAdminRules
} from "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import { SignatureChecker } from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import { Pausable } from "@openzeppelin/contracts/utils/Pausable.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IRandomnessAdapter, IRandomnessConsumer } from "./interfaces/IRandomnessAdapter.sol";
/// @title Tenbagger Edition
/// @notice A prefunded, finite stock-token pack edition with exact onchain inventory and
/// without-replacement randomness.
/// @dev The contract intentionally combines sale escrow and stock vault. Fewer trust boundaries
/// make solvency and refunds easier to verify. Claims and refunds remain available while
/// purchases are paused.
contract TenbaggerEdition is
AccessControlDefaultAdminRules,
EIP712,
Pausable,
ReentrancyGuard,
IRandomnessConsumer
{
using SafeERC20 for IERC20;
enum EditionState {
Configuring,
Active,
Finalized
}
enum PurchaseStatus {
None,
Pending,
Assigned,
Claimed,
Refunded
}
struct PurchaseAuthorization {
address buyer;
uint64 expiresAt;
bytes32 nonce;
}
struct Purchase {
address buyer;
uint64 requestedAt;
uint16 packId;
PurchaseStatus status;
}
error AlreadyConfigured(uint16 packId);
error AuthorizationExpired(uint64 expiresAt);
error AuthorizationUsed(bytes32 nonce);
error BadAuthorization();
error BadPackEncoding(uint16 packId);
error BadTierCount(uint8 tier, uint16 configured, uint16 expected);
error ConfigIncomplete(uint16 configured, uint16 expected);
error EditionNotActive();
error EditionNotConfiguring();
error FeeMismatch(uint256 supplied, uint256 required);
error InventoryInsolvent(address token, uint256 balance, uint256 required);
error InvalidConfiguration();
error InvalidRandomnessAdapter(address adapter);
error InvalidStatus(uint256 purchaseId, PurchaseStatus status);
error NoProceeds();
error OnlySelf();
error OraclePaused(address token);
error PackLimitReached(address buyer);
error RefundNotReady(uint64 availableAt);
error SaleClosed();
error SaleNotEnded();
error SoldOut();
error StockDecimals(address token, uint8 decimals);
error TokenPaused(address token);
error UnknownRandomness(bytes32 requestId);
error UsdgDecimals(uint8 decimals);
error ZeroAddress();
bytes32 public constant CONFIGURATOR_ROLE = keccak256("CONFIGURATOR_ROLE");
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant PURCHASE_AUTHORIZATION_TYPEHASH =
keccak256("PurchaseAuthorization(address buyer,uint64 expiresAt,bytes32 nonce)");
uint256 public constant STOCK_UNIT = 1e12;
uint8 public constant TIER_COUNT = 4;
uint8 public constant STOCKS_PER_PACK = 3;
uint8 public constant MAX_STOCK_TOKENS = 32;
uint8 public constant MAX_CONFIG_BATCH = 25;
string public editionName;
IERC20 public immutable usdg;
address public immutable treasury;
uint256 public immutable packPrice;
uint16 public immutable totalSupply;
uint8 public immutable perWalletLimit;
uint64 public immutable saleDuration;
uint64 public immutable randomnessTimeout;
bytes32 public immutable manifestHash;
uint256 public immutable referenceInventoryCents;
EditionState public editionState;
address public eligibilitySigner;
IRandomnessAdapter public randomnessAdapter;
uint64 public saleEndsAt;
uint16 public configuredPacks;
uint16 public remainingSupply;
uint16 public reservedPacks;
uint16 public pendingPurchases;
uint256 public nextPurchaseId = 1;
uint256 public settledProceeds;
uint256 public withdrawnProceeds;
uint16[4] public expectedTierCounts;
uint16[4] public configuredTierCounts;
uint16[4] public remainingTierCounts;
uint24[4] public tierValueCents;
address[] private _stockTokens;
uint256[] public requiredInventory;
uint256[] public assignedInventory;
uint256[] public claimedInventory;
mapping(uint16 packId => uint256 packedData) private _packData;
mapping(uint16 drawIndex => uint16 packIdPlusOne) private _drawSwaps;
mapping(uint256 purchaseId => Purchase purchase) public purchases;
mapping(bytes32 requestId => uint256 purchaseId) public purchaseForRandomness;
mapping(address buyer => uint16 count) public packsByBuyer;
mapping(bytes32 nonce => bool used) public usedAuthorization;
event EditionActivated(uint64 saleEndsAt, uint16 totalSupply);
event EditionFinalized(uint16 assignedPacks, uint16 unsoldPacks);
event EligibilitySignerUpdated(address indexed previousSigner, address indexed newSigner);
event PackAssigned(
uint256 indexed purchaseId,
address indexed buyer,
uint16 indexed packId,
uint8 tier,
uint24 referenceValueCents
);
event PackClaimed(uint256 indexed purchaseId, address indexed buyer, uint16 indexed packId);
event PackConfigured(uint16 indexed packId, uint8 indexed tier);
event PackPurchased(
uint256 indexed purchaseId, address indexed buyer, bytes32 indexed requestId
);
event PackDeliveryDeferred(uint256 indexed purchaseId, address indexed buyer);
event PackRefunded(uint256 indexed purchaseId, address indexed buyer);
event ProceedsWithdrawn(address indexed treasury, uint256 amount);
event RandomnessAdapterSet(address indexed adapter);
event RandomnessIgnored(bytes32 indexed requestId, uint256 indexed purchaseId);
event UnsoldInventoryWithdrawn(address indexed token, uint256 amount);
constructor(
string memory editionName_,
address admin_,
address treasury_,
address usdg_,
address eligibilitySigner_,
uint256 packPrice_,
uint16 totalSupply_,
uint8 perWalletLimit_,
uint64 saleDuration_,
uint64 randomnessTimeout_,
bytes32 manifestHash_,
uint16[4] memory expectedTierCounts_,
uint24[4] memory tierValueCents_,
address[] memory stockTokens_
) AccessControlDefaultAdminRules(2 days, admin_) EIP712("Tenbagger", "1") {
if (
admin_ == address(0) || treasury_ == address(0) || usdg_ == address(0)
|| eligibilitySigner_ == address(0)
) revert ZeroAddress();
if (
bytes(editionName_).length == 0 || packPrice_ == 0 || totalSupply_ == 0
|| perWalletLimit_ == 0 || saleDuration_ == 0 || randomnessTimeout_ == 0
|| manifestHash_ == bytes32(0) || stockTokens_.length < STOCKS_PER_PACK
|| stockTokens_.length > MAX_STOCK_TOKENS
) revert InvalidConfiguration();
uint8 usdgDecimals = IERC20Metadata(usdg_).decimals();
if (usdgDecimals != 6) revert UsdgDecimals(usdgDecimals);
uint256 tierSupply = 0;
uint256 referenceValue = 0;
for (uint8 tier; tier < TIER_COUNT; ++tier) {
if (tierValueCents_[tier] == 0) revert InvalidConfiguration();
tierSupply += expectedTierCounts_[tier];
referenceValue += uint256(expectedTierCounts_[tier]) * uint256(tierValueCents_[tier]);
}
if (tierSupply != totalSupply_) revert InvalidConfiguration();
for (uint256 i; i < stockTokens_.length; ++i) {
address token = stockTokens_[i];
if (token == address(0)) revert ZeroAddress();
for (uint256 j; j < i; ++j) {
if (stockTokens_[j] == token) revert InvalidConfiguration();
}
uint8 tokenDecimals = IERC20Metadata(token).decimals();
if (tokenDecimals != 18) revert StockDecimals(token, tokenDecimals);
_stockTokens.push(token);
requiredInventory.push(0);
assignedInventory.push(0);
claimedInventory.push(0);
}
editionName = editionName_;
treasury = treasury_;
usdg = IERC20(usdg_);
eligibilitySigner = eligibilitySigner_;
packPrice = packPrice_;
totalSupply = totalSupply_;
perWalletLimit = perWalletLimit_;
saleDuration = saleDuration_;
randomnessTimeout = randomnessTimeout_;
manifestHash = manifestHash_;
referenceInventoryCents = referenceValue;
expectedTierCounts = expectedTierCounts_;
tierValueCents = tierValueCents_;
_grantRole(CONFIGURATOR_ROLE, admin_);
_grantRole(PAUSER_ROLE, admin_);
}
function configurePacks(uint256[] calldata packedPacks) external onlyRole(CONFIGURATOR_ROLE) {
if (editionState != EditionState.Configuring) revert EditionNotConfiguring();
if (packedPacks.length == 0 || packedPacks.length > MAX_CONFIG_BATCH) {
revert InvalidConfiguration();
}
if (configuredPacks + packedPacks.length > totalSupply) revert InvalidConfiguration();
for (uint256 i; i < packedPacks.length; ++i) {
uint16 packId = configuredPacks;
uint256 packed = packedPacks[i];
if (_packData[packId] != 0) revert AlreadyConfigured(packId);
(uint8 tier, uint8[3] memory tokenIds, uint64[3] memory amountUnits) =
_decodePacked(packed);
if (
tier >= TIER_COUNT || packed >> 224 != 0 || tokenIds[0] == tokenIds[1]
|| tokenIds[0] == tokenIds[2] || tokenIds[1] == tokenIds[2]
) revert BadPackEncoding(packId);
for (uint8 item; item < STOCKS_PER_PACK; ++item) {
if (tokenIds[item] >= _stockTokens.length || amountUnits[item] == 0) {
revert BadPackEncoding(packId);
}
requiredInventory[tokenIds[item]] += uint256(amountUnits[item]) * STOCK_UNIT;
}
_packData[packId] = packed;
++configuredTierCounts[tier];
++configuredPacks;
emit PackConfigured(packId, tier);
}
}
function setRandomnessAdapter(address adapter) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (editionState != EditionState.Configuring) revert EditionNotConfiguring();
if (adapter.code.length == 0) revert InvalidRandomnessAdapter(adapter);
randomnessAdapter = IRandomnessAdapter(adapter);
emit RandomnessAdapterSet(adapter);
}
function setEligibilitySigner(address signer) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (signer == address(0)) revert ZeroAddress();
address previous = eligibilitySigner;
eligibilitySigner = signer;
emit EligibilitySignerUpdated(previous, signer);
}
function activate() external onlyRole(DEFAULT_ADMIN_ROLE) {
if (editionState != EditionState.Configuring) revert EditionNotConfiguring();
if (configuredPacks != totalSupply) {
revert ConfigIncomplete(configuredPacks, totalSupply);
}
if (address(randomnessAdapter).code.length == 0) {
revert InvalidRandomnessAdapter(address(randomnessAdapter));
}
for (uint8 tier; tier < TIER_COUNT; ++tier) {
if (configuredTierCounts[tier] != expectedTierCounts[tier]) {
revert BadTierCount(tier, configuredTierCounts[tier], expectedTierCounts[tier]);
}
remainingTierCounts[tier] = expectedTierCounts[tier];
}
uint256 stockCount = _stockTokens.length;
for (uint256 i; i < stockCount; ++i) {
address token = _stockTokens[i];
uint256 balance = IERC20(token).balanceOf(address(this));
uint256 required = requiredInventory[i];
if (balance < required) revert InventoryInsolvent(token, balance, required);
if (_flagTrue(token, "oraclePaused()")) revert OraclePaused(token);
if (_flagTrue(token, "tokenPaused()") || _flagTrue(token, "paused()")) {
revert TokenPaused(token);
}
}
remainingSupply = totalSupply;
saleEndsAt = uint64(block.timestamp) + saleDuration;
editionState = EditionState.Active;
emit EditionActivated(saleEndsAt, totalSupply);
}
function buy(
PurchaseAuthorization calldata authorization,
bytes calldata signature,
bytes32 userEntropy
) external payable nonReentrant whenNotPaused returns (uint256 purchaseId, bytes32 requestId) {
return _buy(msg.sender, authorization, signature, userEntropy);
}
/// @notice Buys a pack through a relayer using an EIP-2612 USDG permit.
/// @dev The permit and eligibility authorization both bind the purchase to the buyer. The
/// relayer can pay gas and randomness fees but can never receive the pack contents.
function buyWithPermit(
PurchaseAuthorization calldata authorization,
bytes calldata signature,
bytes32 userEntropy,
uint256 permitDeadline,
uint8 permitV,
bytes32 permitR,
bytes32 permitS
) external payable nonReentrant whenNotPaused returns (uint256 purchaseId, bytes32 requestId) {
IERC20Permit(address(usdg))
.permit(
authorization.buyer,
address(this),
packPrice,
permitDeadline,
permitV,
permitR,
permitS
);
return _buy(authorization.buyer, authorization, signature, userEntropy);
}
function _buy(
address buyer,
PurchaseAuthorization calldata authorization,
bytes calldata signature,
bytes32 userEntropy
) private returns (uint256 purchaseId, bytes32 requestId) {
if (editionState != EditionState.Active) revert EditionNotActive();
if (block.timestamp >= saleEndsAt) revert SaleClosed();
if (reservedPacks >= totalSupply) revert SoldOut();
if (packsByBuyer[buyer] >= perWalletLimit) revert PackLimitReached(buyer);
if (authorization.buyer != buyer) revert BadAuthorization();
if (authorization.expiresAt < block.timestamp) {
revert AuthorizationExpired(authorization.expiresAt);
}
if (usedAuthorization[authorization.nonce]) {
revert AuthorizationUsed(authorization.nonce);
}
bytes32 digest = authorizationDigest(authorization);
if (!SignatureChecker.isValidSignatureNow(eligibilitySigner, digest, signature)) {
revert BadAuthorization();
}
uint256 randomnessFee = randomnessAdapter.quoteFee();
if (msg.value != randomnessFee) revert FeeMismatch(msg.value, randomnessFee);
usedAuthorization[authorization.nonce] = true;
purchaseId = nextPurchaseId++;
purchases[purchaseId] = Purchase({
buyer: buyer,
requestedAt: uint64(block.timestamp),
packId: 0,
status: PurchaseStatus.Pending
});
++packsByBuyer[buyer];
++reservedPacks;
++pendingPurchases;
usdg.safeTransferFrom(buyer, address(this), packPrice);
bytes32 requestEntropy =
keccak256(abi.encode(block.chainid, address(this), purchaseId, buyer, userEntropy));
requestId = keccak256(abi.encode(address(this), purchaseId, requestEntropy));
purchaseForRandomness[requestId] = purchaseId;
randomnessAdapter.requestRandomness{ value: randomnessFee }(requestId, requestEntropy);
emit PackPurchased(purchaseId, buyer, requestId);
}
function fulfillRandomness(bytes32 requestId, uint256 randomWord) external nonReentrant {
if (msg.sender != address(randomnessAdapter)) {
revert InvalidRandomnessAdapter(msg.sender);
}
uint256 purchaseId = purchaseForRandomness[requestId];
if (purchaseId == 0) revert UnknownRandomness(requestId);
Purchase storage purchase = purchases[purchaseId];
if (purchase.status != PurchaseStatus.Pending) {
emit RandomnessIgnored(requestId, purchaseId);
return;
}
uint16 packId = _drawPack(uint256(keccak256(abi.encode(randomWord, purchaseId))));
uint8 tier = uint8(_packData[packId]);
purchase.packId = packId;
purchase.status = PurchaseStatus.Assigned;
--pendingPurchases;
--remainingTierCounts[tier];
settledProceeds += packPrice;
(, uint8[3] memory tokenIds, uint64[3] memory amountUnits) =
_decodePacked(_packData[packId]);
for (uint8 item; item < STOCKS_PER_PACK; ++item) {
assignedInventory[tokenIds[item]] += uint256(amountUnits[item]) * STOCK_UNIT;
}
emit PackAssigned(purchaseId, purchase.buyer, packId, tier, tierValueCents[tier]);
try this.deliverFromCallback(purchaseId) { }
catch {
emit PackDeliveryDeferred(purchaseId, purchase.buyer);
}
}
/// @notice Retries a deferred delivery. Anyone may pay the gas, but tokens always go to the
/// recorded buyer.
function claim(uint256 purchaseId) external nonReentrant {
_deliver(purchaseId);
}
/// @dev External self-call keeps the three token transfers atomic while allowing the
/// randomness callback to complete if a token temporarily rejects transfers.
function deliverFromCallback(uint256 purchaseId) external {
if (msg.sender != address(this)) revert OnlySelf();
_deliver(purchaseId);
}
function _deliver(uint256 purchaseId) private {
Purchase storage purchase = purchases[purchaseId];
if (purchase.status != PurchaseStatus.Assigned) {
revert InvalidStatus(purchaseId, purchase.status);
}
purchase.status = PurchaseStatus.Claimed;
(, uint8[3] memory tokenIds, uint64[3] memory amountUnits) =
_decodePacked(_packData[purchase.packId]);
for (uint8 item; item < STOCKS_PER_PACK; ++item) {
uint256 amount = uint256(amountUnits[item]) * STOCK_UNIT;
claimedInventory[tokenIds[item]] += amount;
IERC20(_stockTokens[tokenIds[item]]).safeTransfer(purchase.buyer, amount);
}
emit PackClaimed(purchaseId, purchase.buyer, purchase.packId);
}
function refundExpired(uint256 purchaseId) external nonReentrant {
Purchase storage purchase = purchases[purchaseId];
if (purchase.status != PurchaseStatus.Pending) {
revert InvalidStatus(purchaseId, purchase.status);
}
uint64 availableAt = purchase.requestedAt + randomnessTimeout;
if (block.timestamp < availableAt) revert RefundNotReady(availableAt);
purchase.status = PurchaseStatus.Refunded;
--pendingPurchases;
--reservedPacks;
--packsByBuyer[purchase.buyer];
usdg.safeTransfer(purchase.buyer, packPrice);
emit PackRefunded(purchaseId, purchase.buyer);
}
function withdrawProceeds() external nonReentrant returns (uint256 amount) {
amount = settledProceeds - withdrawnProceeds;
if (amount == 0) revert NoProceeds();
withdrawnProceeds = settledProceeds;
usdg.safeTransfer(treasury, amount);
emit ProceedsWithdrawn(treasury, amount);
}
function finalizeSale() external onlyRole(DEFAULT_ADMIN_ROLE) nonReentrant {
if (editionState != EditionState.Active) revert EditionNotActive();
if (block.timestamp < saleEndsAt) revert SaleNotEnded();
if (pendingPurchases != 0) revert InvalidConfiguration();
editionState = EditionState.Finalized;
uint16 assignedPacks = totalSupply - remainingSupply;
uint256 stockCount = _stockTokens.length;
for (uint256 i; i < stockCount; ++i) {
uint256 unsoldAmount = requiredInventory[i] - assignedInventory[i];
if (unsoldAmount != 0) {
IERC20(_stockTokens[i]).safeTransfer(treasury, unsoldAmount);
emit UnsoldInventoryWithdrawn(_stockTokens[i], unsoldAmount);
}
}
emit EditionFinalized(assignedPacks, remainingSupply);
}
function pausePurchases() external onlyRole(PAUSER_ROLE) {
_pause();
}
function unpausePurchases() external onlyRole(PAUSER_ROLE) {
_unpause();
}
function authorizationDigest(PurchaseAuthorization calldata authorization)
public
view
returns (bytes32)
{
bytes32 structHash = keccak256(
abi.encode(
PURCHASE_AUTHORIZATION_TYPEHASH,
authorization.buyer,
authorization.expiresAt,
authorization.nonce
)
);
return _hashTypedDataV4(structHash);
}
function stockTokenCount() external view returns (uint256) {
return _stockTokens.length;
}
function stockToken(uint256 index) external view returns (address) {
return _stockTokens[index];
}
function packedPack(uint16 packId) external view returns (uint256) {
return _packData[packId];
}
function packContents(uint16 packId)
external
view
returns (
uint8 tier,
address[3] memory tokens,
uint256[3] memory amounts,
uint24 referenceValueCents
)
{
uint8[3] memory tokenIds;
uint64[3] memory amountUnits;
(tier, tokenIds, amountUnits) = _decodePacked(_packData[packId]);
referenceValueCents = tierValueCents[tier];
for (uint8 item; item < STOCKS_PER_PACK; ++item) {
tokens[item] = _stockTokens[tokenIds[item]];
amounts[item] = uint256(amountUnits[item]) * STOCK_UNIT;
}
}
function inventorySolvent() external view returns (bool) {
uint256 stockCount = _stockTokens.length;
for (uint256 i; i < stockCount; ++i) {
uint256 obligation = editionState == EditionState.Finalized
? assignedInventory[i] - claimedInventory[i]
: requiredInventory[i] - claimedInventory[i];
if (IERC20(_stockTokens[i]).balanceOf(address(this)) < obligation) return false;
}
return true;
}
function _drawPack(uint256 randomWord) private returns (uint16 selectedPackId) {
uint16 count = remainingSupply;
if (count == 0) revert SoldOut();
uint16 drawIndex = SafeCast.toUint16(randomWord % count);
uint16 lastIndex = count - 1;
uint16 selectedPlusOne = _drawSwaps[drawIndex];
if (selectedPlusOne == 0) selectedPlusOne = drawIndex + 1;
uint16 lastPlusOne = _drawSwaps[lastIndex];
if (lastPlusOne == 0) lastPlusOne = lastIndex + 1;
if (drawIndex != lastIndex) _drawSwaps[drawIndex] = lastPlusOne;
delete _drawSwaps[lastIndex];
remainingSupply = lastIndex;
selectedPackId = selectedPlusOne - 1;
}
function _decodePacked(uint256 packed)
private
pure
returns (uint8 tier, uint8[3] memory tokenIds, uint64[3] memory amountUnits)
{
tier = SafeCast.toUint8(packed & type(uint8).max);
uint256 offset = 8;
for (uint8 item; item < STOCKS_PER_PACK; ++item) {
tokenIds[item] = SafeCast.toUint8((packed >> offset) & type(uint8).max);
offset += 8;
amountUnits[item] = SafeCast.toUint64((packed >> offset) & type(uint64).max);
offset += 64;
}
}
function _flagTrue(address token, string memory signature) private view returns (bool) {
(bool success, bytes memory result) = token.staticcall(abi.encodeWithSignature(signature));
return success && result.length >= 32 && abi.decode(result, (bool));
}
}
Chain explorer3612msChain node88ms