MineableToken
0xd69be8d7c6d0e8216dc2c151834e4150313260cc
Verification
Verified
v0.8.26+commit.8a97fa7a
Type
Contract
16,792 bytes
ABI entries
89
49 read · 9 write
License
none
Contract information
- Address
- 0xd69be8d7c6d0e8216dc2c151834e4150313260cc
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.26+commit.8a97fa7a
- Optimization
- Enabled
- Creator
- 0x4e59b44847…26c0B4956C
- Creation tx
- 0xb9331e6043…07179587ce
Token
- Name
- HashWood
- Symbol
- HASH
- Decimals
- 18
- Holders
- 146
- Market
- View token page →
Read contract (49)
HALVING_INTERVAL() → uint256
INITIAL_REWARD() → uint256
INITIAL_SWAP_FEE_BPS() → uint256
MAXIMUM_TARGET() → uint256
MAX_ADJUSTMENT_FACTOR() → uint256
MAX_SUPPLY() → uint256
MINIMUM_TARGET() → uint256
MINT_FEE_BPS() → uint256
MINT_PRICE() → uint256
MIN_SWAP_FEE_BPS() → uint256
POST_SALE_SWAP_FEE_BPS() → uint256
READJUSTMENT_INTERVAL() → uint256
SWAP_FEE_DECAY_BPS() → uint256
afterAddLiquidity(address, tuple, tuple, int256, int256, bytes) → bytes4, int256
afterDonate(address, tuple, uint256, uint256, bytes) → bytes4
afterInitialize(address, tuple, uint160, int24) → bytes4
afterRemoveLiquidity(address, tuple, tuple, int256, int256, bytes) → bytes4, int256
allowance(address, address) → uint256
balanceOf(address) → uint256
beforeAddLiquidity(address, tuple, tuple, bytes) → bytes4
beforeDonate(address, tuple, uint256, uint256, bytes) → bytes4
beforeInitialize(address, tuple, uint160) → bytes4
beforeRemoveLiquidity(address, tuple, tuple, bytes) → bytes4
canonicalPoolKey() → tuple
challengeNumber() → bytes32
checkMintSolution(uint256, address) → bool
decimals() → uint8
epochCount() → uint256
feeRecipient() → address
getChallengeNumber() → bytes32
getMiningDifficulty() → uint256
getMiningReward() → uint256
getMiningTarget() → uint256
initSqrtPriceX96() → uint160
lastMintBlock() → uint256
lastRetargetTime() → uint256
miningTarget() → uint256
name() → string
onERC721Received(address, address, uint256, bytes) → bytes4
permit2() → address
polReserve() → uint256
poolManager() → address
positionManager() → address
seedThreshold() → uint256
swapFeeBps() → uint256
symbol() → string
targetMintTime() → uint256
tokensMinted() → uint256
totalSupply() → uint256
Events (7)
ApprovalDifficultyAdjustedFeeCollectedFeesClaimedLiquiditySeededMintTransfer
ABI
[
{
"inputs": [
{
"internalType": "string",
"name": "name_",
"type": "string"
},
{
"internalType": "string",
"name": "symbol_",
"type": "string"
},
{
"internalType": "uint256",
"name": "initialMiningTarget",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "targetMintTime_",
"type": "uint256"
},
{
"internalType": "contract IPoolManager",
"name": "poolManager_",
"type": "address"
},
{
"internalType": "address",
"name": "feeRecipient_",
"type": "address"
},
{
"internalType": "contract IPositionManager",
"name": "positionManager_",
"type": "address"
},
{
"internalType": "address",
"name": "permit2_",
"type": "address"
},
{
"internalType": "uint256",
"name": "mintPrice_",
"type": "uint256"
},
{
"internalType": "uint160",
"name": "initSqrtPriceX96_",
"type": "uint160"
},
{
"internalType": "uint256",
"name": "seedEveryNMints_",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "BlockAlreadyMined",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "allowance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientAllowance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "uint256",
"name": "balance",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "needed",
"type": "uint256"
}
],
"name": "ERC20InsufficientBalance",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "approver",
"type": "address"
}
],
"name": "ERC20InvalidApprover",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "receiver",
"type": "address"
}
],
"name": "ERC20InvalidReceiver",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
}
],
"name": "ERC20InvalidSender",
"type": "error"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "ERC20InvalidSpender",
"type": "error"
},
{
"inputs": [],
"name": "EthTransferFailed",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientGasForSeed",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientLiquidity",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientPayment",
"type": "error"
},
{
"inputs": [],
"name": "InvalidAction",
"type": "error"
},
{
"inputs": [],
"name": "InvalidDigest",
"type": "error"
},
{
"inputs": [],
"name": "MiningFinished",
"type": "error"
},
{
"inputs": [],
"name": "NotPoolManager",
"type": "error"
},
{
"inputs": [],
"name": "NothingToClaim",
"type": "error"
},
{
"inputs": [],
"name": "NothingToSeed",
"type": "error"
},
{
"inputs": [],
"name": "PoolNotInitialized",
"type": "error"
},
{
"inputs": [],
"name": "ReentrancyGuardReentrantCall",
"type": "error"
},
{
"inputs": [],
"name": "SolutionAboveTarget",
"type": "error"
},
{
"inputs": [],
"name": "UnsupportedSwapMode",
"type": "error"
},
{
"inputs": [],
"name": "WrongPool",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Approval",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "newMiningTarget",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "elapsedSeconds",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "expectedSeconds",
"type": "uint256"
}
],
"name": "DifficultyAdjusted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": false,
"internalType": "bool",
"name": "isBuy",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
}
],
"name": "FeeCollected",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "recipient",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
}
],
"name": "FeesClaimed",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "ethAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "tokenAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint128",
"name": "liquidity",
"type": "uint128"
}
],
"name": "LiquiditySeeded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "rewardAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "epochCount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bytes32",
"name": "newChallengeNumber",
"type": "bytes32"
}
],
"name": "Mint",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "to",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Transfer",
"type": "event"
},
{
"inputs": [],
"name": "HALVING_INTERVAL",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "INITIAL_REWARD",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "INITIAL_SWAP_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAXIMUM_TARGET",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_ADJUSTMENT_FACTOR",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MAX_SUPPLY",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MINIMUM_TARGET",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MINT_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MINT_PRICE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "MIN_SWAP_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "POST_SALE_SWAP_FEE_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "READJUSTMENT_INTERVAL",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "SWAP_FEE_DECAY_BPS",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "int256",
"name": "liquidityDelta",
"type": "int256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"internalType": "struct ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterAddLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterDonate",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"internalType": "uint160",
"name": "",
"type": "uint160"
},
{
"internalType": "int24",
"name": "",
"type": "int24"
}
],
"name": "afterInitialize",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "int256",
"name": "liquidityDelta",
"type": "int256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"internalType": "struct ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterRemoveLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "BalanceDelta",
"name": "",
"type": "int256"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "int256",
"name": "amountSpecified",
"type": "int256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct SwapParams",
"name": "params",
"type": "tuple"
},
{
"internalType": "BalanceDelta",
"name": "delta",
"type": "int256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "afterSwap",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "int128",
"name": "",
"type": "int128"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "spender",
"type": "address"
}
],
"name": "allowance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "spender",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "approve",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "autoSeed",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "account",
"type": "address"
}
],
"name": "balanceOf",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "int256",
"name": "liquidityDelta",
"type": "int256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"internalType": "struct ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeAddLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeDonate",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "key",
"type": "tuple"
},
{
"internalType": "uint160",
"name": "sqrtPriceX96",
"type": "uint160"
}
],
"name": "beforeInitialize",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "int24",
"name": "tickLower",
"type": "int24"
},
{
"internalType": "int24",
"name": "tickUpper",
"type": "int24"
},
{
"internalType": "int256",
"name": "liquidityDelta",
"type": "int256"
},
{
"internalType": "bytes32",
"name": "salt",
"type": "bytes32"
}
],
"internalType": "struct ModifyLiquidityParams",
"name": "",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeRemoveLiquidity",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
},
{
"components": [
{
"internalType": "bool",
"name": "zeroForOne",
"type": "bool"
},
{
"internalType": "int256",
"name": "amountSpecified",
"type": "int256"
},
{
"internalType": "uint160",
"name": "sqrtPriceLimitX96",
"type": "uint160"
}
],
"internalType": "struct SwapParams",
"name": "params",
"type": "tuple"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "beforeSwap",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
},
{
"internalType": "BeforeSwapDelta",
"name": "",
"type": "int256"
},
{
"internalType": "uint24",
"name": "",
"type": "uint24"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "canonicalPoolKey",
"outputs": [
{
"components": [
{
"internalType": "Currency",
"name": "currency0",
"type": "address"
},
{
"internalType": "Currency",
"name": "currency1",
"type": "address"
},
{
"internalType": "uint24",
"name": "fee",
"type": "uint24"
},
{
"internalType": "int24",
"name": "tickSpacing",
"type": "int24"
},
{
"internalType": "contract IHooks",
"name": "hooks",
"type": "address"
}
],
"internalType": "struct PoolKey",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "challengeNumber",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "address",
"name": "minter",
"type": "address"
}
],
"name": "checkMintSolution",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "claimFees",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "decimals",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "epochCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "feeRecipient",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getChallengeNumber",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getMiningDifficulty",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getMiningReward",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getMiningTarget",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "initSqrtPriceX96",
"outputs": [
{
"internalType": "uint160",
"name": "",
"type": "uint160"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastMintBlock",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "lastRetargetTime",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "miningTarget",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "challengeDigest",
"type": "bytes32"
}
],
"name": "mint",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "name",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "address",
"name": "",
"type": "address"
},
{
"internalType": "uint256",
"name": "",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "",
"type": "bytes"
}
],
"name": "onERC721Received",
"outputs": [
{
"internalType": "bytes4",
"name": "",
"type": "bytes4"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "permit2",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "polReserve",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "poolManager",
"outputs": [
{
"internalType": "contract IPoolManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "positionManager",
"outputs": [
{
"internalType": "contract IPositionManager",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "seedLiquidity",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "seedThreshold",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "swapFeeBps",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "symbol",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "targetMintTime",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "tokensMinted",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalSupply",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transfer",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "from",
"type": "address"
},
{
"internalType": "address",
"name": "to",
"type": "address"
},
{
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "transferFrom",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IHooks} from "v4-core/src/interfaces/IHooks.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {Hooks} from "v4-core/src/libraries/Hooks.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {Currency, CurrencyLibrary} from "v4-core/src/types/Currency.sol";
import {BalanceDelta} from "v4-core/src/types/BalanceDelta.sol";
import {
BeforeSwapDelta, BeforeSwapDeltaLibrary, toBeforeSwapDelta
} from "v4-core/src/types/BeforeSwapDelta.sol";
import {ModifyLiquidityParams, SwapParams} from "v4-core/src/types/PoolOperation.sol";
import {PoolId} from "v4-core/src/types/PoolId.sol";
import {StateLibrary} from "v4-core/src/libraries/StateLibrary.sol";
import {TickMath} from "v4-core/src/libraries/TickMath.sol";
import {LiquidityAmounts} from "v4-core/test/utils/LiquidityAmounts.sol";
interface IPositionManager {
function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable;
}
interface IAllowanceTransfer {
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
}
contract MineableToken is ERC20, ReentrancyGuard, IHooks {
uint256 public constant MAX_SUPPLY = 21_000_000e18;
uint256 public constant INITIAL_REWARD = 1_100e18;
uint256 public constant HALVING_INTERVAL = 11_000;
uint256 public constant MINT_FEE_BPS = 100;
uint256 public constant READJUSTMENT_INTERVAL = 128;
uint256 public constant MAXIMUM_TARGET = 2 ** 248;
uint256 public constant MINIMUM_TARGET = 2 ** 16;
uint256 public constant MAX_ADJUSTMENT_FACTOR = 4;
uint256 public immutable targetMintTime;
uint256 public constant INITIAL_SWAP_FEE_BPS = 400;
uint256 public constant SWAP_FEE_DECAY_BPS = 150;
uint256 public constant MIN_SWAP_FEE_BPS = 100;
uint256 public constant POST_SALE_SWAP_FEE_BPS = 50;
IPoolManager public immutable poolManager;
address public immutable feeRecipient;
uint256 public immutable MINT_PRICE;
IPositionManager public immutable positionManager;
address public immutable permit2;
uint24 internal constant LP_FEE = 0;
int24 internal constant TICK_SPACING = 60;
int24 internal constant TICK_LOWER = -887_220;
int24 internal constant TICK_UPPER = 887_220;
uint8 internal constant ACTION_MINT_POSITION = 0x02;
uint8 internal constant ACTION_SETTLE_PAIR = 0x0d;
uint8 internal constant ACTION_SWEEP = 0x14;
uint256 public polReserve;
bool internal approvalsDone;
uint256 public immutable seedThreshold;
uint256 internal constant SEED_GAS_FLOOR = 800_000;
uint160 public immutable initSqrtPriceX96;
uint256 public miningTarget;
bytes32 public challengeNumber;
uint256 public epochCount;
uint256 public tokensMinted;
uint256 public lastRetargetTime;
uint256 public lastMintBlock;
event Mint(address indexed from, uint256 rewardAmount, uint256 epochCount, bytes32 newChallengeNumber);
event DifficultyAdjusted(uint256 newMiningTarget, uint256 elapsedSeconds, uint256 expectedSeconds);
event FeeCollected(address indexed sender, bool isBuy, uint256 ethAmount);
event FeesClaimed(address indexed recipient, uint256 ethAmount);
event LiquiditySeeded(uint256 ethAmount, uint256 tokenAmount, uint128 liquidity);
error InvalidDigest();
error SolutionAboveTarget();
error MiningFinished();
error BlockAlreadyMined();
error InsufficientPayment();
error PoolNotInitialized();
error NothingToSeed();
error InsufficientLiquidity();
error InsufficientGasForSeed();
error NotPoolManager();
error WrongPool();
error UnsupportedSwapMode();
error InvalidAction();
error NothingToClaim();
error EthTransferFailed();
constructor(
string memory name_,
string memory symbol_,
uint256 initialMiningTarget,
uint256 targetMintTime_,
IPoolManager poolManager_,
address feeRecipient_,
IPositionManager positionManager_,
address permit2_,
uint256 mintPrice_,
uint160 initSqrtPriceX96_,
uint256 seedEveryNMints_
) ERC20(name_, symbol_) {
require(targetMintTime_ > 0, "target time = 0");
require(address(poolManager_) != address(0), "pool manager = 0");
require(feeRecipient_ != address(0), "fee recipient = 0");
require(address(positionManager_) != address(0), "position manager = 0");
require(permit2_ != address(0), "permit2 = 0");
require(initSqrtPriceX96_ > 0, "init price = 0");
require(seedEveryNMints_ > 0, "seed interval = 0");
miningTarget = _clampTarget(initialMiningTarget);
targetMintTime = targetMintTime_;
lastRetargetTime = block.timestamp;
poolManager = poolManager_;
feeRecipient = feeRecipient_;
positionManager = positionManager_;
permit2 = permit2_;
MINT_PRICE = mintPrice_;
seedThreshold = mintPrice_ * seedEveryNMints_;
initSqrtPriceX96 = initSqrtPriceX96_;
challengeNumber =
keccak256(abi.encodePacked(_prevBlockhash(), block.chainid, address(this), block.timestamp));
Hooks.validateHookPermissions(IHooks(address(this)), _permissions());
}
function _prevBlockhash() internal view returns (bytes32) {
return block.number > 0 ? blockhash(block.number - 1) : bytes32(0);
}
function mint(uint256 nonce, bytes32 challengeDigest) external payable nonReentrant returns (bool) {
bytes32 digest = keccak256(abi.encodePacked(challengeNumber, msg.sender, nonce));
if (digest != challengeDigest) revert InvalidDigest();
if (uint256(digest) > miningTarget) revert SolutionAboveTarget();
if (block.number == lastMintBlock) revert BlockAlreadyMined();
if (msg.value < MINT_PRICE) revert InsufficientPayment();
uint256 reward = getMiningReward();
if (reward == 0) revert MiningFinished();
if (tokensMinted + reward > MAX_SUPPLY) {
reward = MAX_SUPPLY - tokensMinted;
}
uint256 mintFee = (reward * MINT_FEE_BPS) / 10_000;
if (tokensMinted + reward + mintFee > MAX_SUPPLY) {
mintFee = MAX_SUPPLY - tokensMinted - reward;
}
polReserve += MINT_PRICE;
tokensMinted += reward + mintFee;
epochCount += 1;
lastMintBlock = block.number;
_mint(msg.sender, reward);
if (mintFee > 0) {
_mint(address(this), mintFee);
}
uint256 excess = msg.value - MINT_PRICE;
if (excess > 0) {
(bool ok,) = msg.sender.call{value: excess}("");
if (!ok) revert EthTransferFailed();
}
if (polReserve >= seedThreshold) {
if (gasleft() < SEED_GAS_FLOOR) revert InsufficientGasForSeed();
try this.autoSeed() {} catch {}
}
if (epochCount % READJUSTMENT_INTERVAL == 0) {
_adjustDifficulty();
}
challengeNumber = keccak256(abi.encodePacked(digest, _prevBlockhash(), epochCount));
emit Mint(msg.sender, reward, epochCount, challengeNumber);
return true;
}
function _adjustDifficulty() internal {
uint256 expected = READJUSTMENT_INTERVAL * targetMintTime;
uint256 elapsed = block.timestamp - lastRetargetTime;
if (elapsed < expected / MAX_ADJUSTMENT_FACTOR) elapsed = expected / MAX_ADJUSTMENT_FACTOR;
if (elapsed > expected * MAX_ADJUSTMENT_FACTOR) elapsed = expected * MAX_ADJUSTMENT_FACTOR;
uint256 newTarget = _clampTarget(Math.mulDiv(miningTarget, elapsed, expected));
miningTarget = newTarget;
lastRetargetTime = block.timestamp;
emit DifficultyAdjusted(newTarget, elapsed, expected);
}
function _clampTarget(uint256 target) internal pure returns (uint256) {
if (target > MAXIMUM_TARGET) return MAXIMUM_TARGET;
if (target < MINIMUM_TARGET) return MINIMUM_TARGET;
return target;
}
function getMiningReward() public view returns (uint256) {
uint256 era = epochCount / HALVING_INTERVAL;
if (era > 63) return 0;
uint256 reward = INITIAL_REWARD >> era;
uint256 remaining = MAX_SUPPLY - tokensMinted;
return reward < remaining ? reward : remaining;
}
function getMiningDifficulty() external view returns (uint256) {
return MAXIMUM_TARGET / miningTarget;
}
function getChallengeNumber() external view returns (bytes32) {
return challengeNumber;
}
function getMiningTarget() external view returns (uint256) {
return miningTarget;
}
function checkMintSolution(uint256 nonce, address minter) external view returns (bool) {
return uint256(keccak256(abi.encodePacked(challengeNumber, minter, nonce))) <= miningTarget;
}
function swapFeeBps() public view returns (uint256) {
if (tokensMinted >= MAX_SUPPLY) return POST_SALE_SWAP_FEE_BPS;
uint256 era = epochCount / HALVING_INTERVAL;
uint256 decay = era * SWAP_FEE_DECAY_BPS;
if (decay >= INITIAL_SWAP_FEE_BPS - MIN_SWAP_FEE_BPS) return MIN_SWAP_FEE_BPS;
return INITIAL_SWAP_FEE_BPS - decay;
}
function claimFees() external nonReentrant {
uint256 amount = address(this).balance - polReserve;
if (amount == 0) revert NothingToClaim();
(bool ok,) = feeRecipient.call{value: amount}("");
if (!ok) revert EthTransferFailed();
emit FeesClaimed(feeRecipient, amount);
}
receive() external payable {}
function seedLiquidity() external nonReentrant {
bool miningDone = tokensMinted >= MAX_SUPPLY;
if (polReserve < seedThreshold && !miningDone) revert NothingToSeed();
_seed();
}
function autoSeed() external {
if (msg.sender != address(this)) revert InvalidAction();
_seed();
}
function _seed() internal {
uint256 eth = polReserve;
uint256 tokens = balanceOf(address(this));
PoolKey memory key = canonicalPoolKey();
(uint160 sqrtPriceX96,,,) = StateLibrary.getSlot0(poolManager, key.toId());
if (sqrtPriceX96 == 0) revert PoolNotInitialized();
uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(
sqrtPriceX96,
TickMath.getSqrtPriceAtTick(TICK_LOWER),
TickMath.getSqrtPriceAtTick(TICK_UPPER),
eth,
tokens
);
if (liquidity == 0) revert InsufficientLiquidity();
if (!approvalsDone) {
approvalsDone = true;
_approve(address(this), permit2, type(uint256).max);
IAllowanceTransfer(permit2).approve(
address(this), address(positionManager), type(uint160).max, type(uint48).max
);
}
bytes memory actions =
abi.encodePacked(ACTION_MINT_POSITION, ACTION_SETTLE_PAIR, ACTION_SWEEP);
bytes[] memory params = new bytes[](3);
params[0] = abi.encode(key, TICK_LOWER, TICK_UPPER, liquidity, eth, tokens, address(this), bytes(""));
params[1] = abi.encode(key.currency0, key.currency1);
params[2] = abi.encode(key.currency0, address(this));
uint256 balBefore = address(this).balance;
positionManager.modifyLiquidities{value: eth}(abi.encode(actions, params), block.timestamp + 120);
uint256 ethUsed = balBefore - address(this).balance;
polReserve = eth - ethUsed;
emit LiquiditySeeded(ethUsed, tokens, liquidity);
}
function canonicalPoolKey() public view returns (PoolKey memory) {
return PoolKey({
currency0: CurrencyLibrary.ADDRESS_ZERO,
currency1: Currency.wrap(address(this)),
fee: LP_FEE,
tickSpacing: TICK_SPACING,
hooks: IHooks(address(this))
});
}
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
return this.onERC721Received.selector;
}
function _permissions() internal pure returns (Hooks.Permissions memory) {
return Hooks.Permissions({
beforeInitialize: true,
afterInitialize: false,
beforeAddLiquidity: false,
afterAddLiquidity: false,
beforeRemoveLiquidity: false,
afterRemoveLiquidity: false,
beforeSwap: true,
afterSwap: true,
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: true,
afterSwapReturnDelta: true,
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
function beforeInitialize(address, PoolKey calldata key, uint160 sqrtPriceX96) external view returns (bytes4) {
if (msg.sender != address(poolManager)) revert NotPoolManager();
if (Currency.unwrap(key.currency0) != address(0)) revert WrongPool();
if (Currency.unwrap(key.currency1) != address(this)) revert WrongPool();
if (key.fee == LP_FEE && key.tickSpacing == TICK_SPACING && sqrtPriceX96 != initSqrtPriceX96) {
revert WrongPool();
}
return IHooks.beforeInitialize.selector;
}
function beforeSwap(address sender, PoolKey calldata, SwapParams calldata params, bytes calldata)
external
nonReentrant
returns (bytes4, BeforeSwapDelta, uint24)
{
if (msg.sender != address(poolManager)) revert NotPoolManager();
bool isBuy = params.zeroForOne;
bool isExactInput = params.amountSpecified < 0;
if (!isBuy && !isExactInput) revert UnsupportedSwapMode();
if (!isBuy || !isExactInput) {
return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
}
uint256 ethIn = uint256(-params.amountSpecified);
uint256 fee = (ethIn * swapFeeBps()) / 10_000;
if (fee == 0) {
return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);
}
poolManager.take(CurrencyLibrary.ADDRESS_ZERO, address(this), fee);
emit FeeCollected(sender, true, fee);
return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);
}
function afterSwap(address sender, PoolKey calldata, SwapParams calldata params, BalanceDelta delta, bytes calldata)
external
returns (bytes4, int128)
{
if (msg.sender != address(poolManager)) revert NotPoolManager();
bool isBuy = params.zeroForOne;
bool isExactInput = params.amountSpecified < 0;
bool ethIsUnspecified = (isBuy && !isExactInput) || (!isBuy && isExactInput);
if (!ethIsUnspecified) {
return (IHooks.afterSwap.selector, 0);
}
int128 ethDelta = delta.amount0();
uint256 ethAmount = ethDelta < 0 ? uint256(int256(-ethDelta)) : uint256(int256(ethDelta));
if (ethAmount == 0) {
return (IHooks.afterSwap.selector, 0);
}
uint256 fee = (ethAmount * swapFeeBps()) / 10_000;
if (fee == 0) {
return (IHooks.afterSwap.selector, 0);
}
poolManager.take(CurrencyLibrary.ADDRESS_ZERO, address(this), fee);
emit FeeCollected(sender, isBuy, fee);
return (IHooks.afterSwap.selector, int128(int256(fee)));
}
function afterInitialize(address, PoolKey calldata, uint160, int24) external pure returns (bytes4) {
revert InvalidAction();
}
function beforeAddLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata)
external
pure
returns (bytes4)
{
revert InvalidAction();
}
function afterAddLiquidity(
address,
PoolKey calldata,
ModifyLiquidityParams calldata,
BalanceDelta,
BalanceDelta,
bytes calldata
) external pure returns (bytes4, BalanceDelta) {
revert InvalidAction();
}
function beforeRemoveLiquidity(address, PoolKey calldata, ModifyLiquidityParams calldata, bytes calldata)
external
pure
returns (bytes4)
{
revert InvalidAction();
}
function afterRemoveLiquidity(
address,
PoolKey calldata,
ModifyLiquidityParams calldata,
BalanceDelta,
BalanceDelta,
bytes calldata
) external pure returns (bytes4, BalanceDelta) {
revert InvalidAction();
}
function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
external
pure
returns (bytes4)
{
revert InvalidAction();
}
function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
external
pure
returns (bytes4)
{
revert InvalidAction();
}
}
Chain explorer4032msChain node81ms