diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..140ea9e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/openzeppelin-contracts"] + path = lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/Cargo.toml b/Cargo.toml index ad7d45a..9890df3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ homepage = "https://maidsafe.net" license = "GPL-3.0" name = "evmlib" repository = "https://github.com/WithAutonomi/evmlib" -version = "0.5.0" +version = "0.6.0" [features] external-signer = [] diff --git a/abi/IMerklePaymentVault.json b/abi/IPaymentVault.json similarity index 51% rename from abi/IMerklePaymentVault.json rename to abi/IPaymentVault.json index 43ddf76..f46218d 100644 --- a/abi/IMerklePaymentVault.json +++ b/abi/IPaymentVault.json @@ -1,4 +1,20 @@ [ + { + "type": "constructor", + "inputs": [ + { + "name": "_antToken", + "type": "address", + "internalType": "contract IERC20" + }, + { + "name": "_batchLimit", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "nonpayable" + }, { "type": "function", "name": "CANDIDATES_PER_POOL", @@ -40,101 +56,80 @@ }, { "type": "function", - "name": "estimateMerkleTreeCost", + "name": "batchLimit", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "completedMerklePayments", "inputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ { "name": "depth", "type": "uint8", "internalType": "uint8" }, - { - "name": "poolCommitments", - "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.PoolCommitment[]", - "components": [ - { - "name": "poolHash", - "type": "bytes32", - "internalType": "bytes32" - }, - { - "name": "candidates", - "type": "tuple[16]", - "internalType": "struct IMerklePaymentVault.CandidateNode[16]", - "components": [ - { - "name": "rewardsAddress", - "type": "address", - "internalType": "address" - }, - { - "name": "metrics", - "type": "tuple", - "internalType": "struct IMerklePaymentVault.QuotingMetrics", - "components": [ - { - "name": "dataType", - "type": "uint8", - "internalType": "enum IMerklePaymentVault.DataType" - }, - { - "name": "closeRecordsStored", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "recordsPerType", - "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.Record[]", - "components": [ - { - "name": "dataType", - "type": "uint8", - "internalType": "enum IMerklePaymentVault.DataType" - }, - { - "name": "records", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ] - } - ] - } - ] - }, { "name": "merklePaymentTimestamp", "type": "uint64", "internalType": "uint64" } ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "completedPayments", + "inputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], "outputs": [ { - "name": "totalAmount", - "type": "uint256", - "internalType": "uint256" + "name": "rewardsAddress", + "type": "bytes16", + "internalType": "bytes16" + }, + { + "name": "amount", + "type": "uint128", + "internalType": "uint128" } ], "stateMutability": "view" }, { "type": "function", - "name": "getPaymentInfo", + "name": "getCompletedMerklePayment", "inputs": [ { - "name": "winnerPoolHash", + "name": "winnerHash", "type": "bytes32", "internalType": "bytes32" } ], "outputs": [ { - "name": "info", + "name": "", "type": "tuple", - "internalType": "struct IMerklePaymentVault.PaymentInfo", + "internalType": "struct CompletedMerklePayment", "components": [ { "name": "depth", @@ -149,7 +144,7 @@ { "name": "paidNodeAddresses", "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.PaidNode[]", + "internalType": "struct PaidNode[]", "components": [ { "name": "rewardsAddress", @@ -160,6 +155,11 @@ "name": "poolIndex", "type": "uint8", "internalType": "uint8" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" } ] } @@ -180,7 +180,7 @@ { "name": "poolCommitments", "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.PoolCommitment[]", + "internalType": "struct PoolCommitment[]", "components": [ { "name": "poolHash", @@ -190,7 +190,7 @@ { "name": "candidates", "type": "tuple[16]", - "internalType": "struct IMerklePaymentVault.CandidateNode[16]", + "internalType": "struct CandidateNode[16]", "components": [ { "name": "rewardsAddress", @@ -198,38 +198,9 @@ "internalType": "address" }, { - "name": "metrics", - "type": "tuple", - "internalType": "struct IMerklePaymentVault.QuotingMetrics", - "components": [ - { - "name": "dataType", - "type": "uint8", - "internalType": "enum IMerklePaymentVault.DataType" - }, - { - "name": "closeRecordsStored", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "recordsPerType", - "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.Record[]", - "components": [ - { - "name": "dataType", - "type": "uint8", - "internalType": "enum IMerklePaymentVault.DataType" - }, - { - "name": "records", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ] + "name": "amount", + "type": "uint256", + "internalType": "uint256" } ] } @@ -257,82 +228,83 @@ }, { "type": "function", - "name": "payForMerkleTree2", + "name": "payForQuotes", "inputs": [ { - "name": "depth", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "poolCommitments", + "name": "_payments", "type": "tuple[]", - "internalType": "struct IMerklePaymentVault.PoolCommitmentPacked[]", + "internalType": "struct DataPayment[]", "components": [ { - "name": "poolHash", - "type": "bytes32", - "internalType": "bytes32" + "name": "rewardsAddress", + "type": "address", + "internalType": "address" }, { - "name": "candidates", - "type": "tuple[16]", - "internalType": "struct IMerklePaymentVault.CandidateNodePacked[16]", - "components": [ - { - "name": "rewardsAddress", - "type": "address", - "internalType": "address" - }, - { - "name": "dataTypeAndTotalCostUnit", - "type": "uint256", - "internalType": "uint256" - } - ] + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "quoteHash", + "type": "bytes32", + "internalType": "bytes32" } ] - }, - { - "name": "merklePaymentTimestamp", - "type": "uint64", - "internalType": "uint64" - } - ], - "outputs": [ - { - "name": "winnerPoolHash", - "type": "bytes32", - "internalType": "bytes32" - }, - { - "name": "totalAmount", - "type": "uint256", - "internalType": "uint256" } ], + "outputs": [], "stateMutability": "nonpayable" }, { "type": "function", - "name": "payments", + "name": "verifyPayment", "inputs": [ { - "name": "winnerPoolHash", - "type": "bytes32", - "internalType": "bytes32" + "name": "_payments", + "type": "tuple[]", + "internalType": "struct DataPayment[]", + "components": [ + { + "name": "rewardsAddress", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "quoteHash", + "type": "bytes32", + "internalType": "bytes32" + } + ] } ], "outputs": [ { - "name": "depth", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "merklePaymentTimestamp", - "type": "uint64", - "internalType": "uint64" + "name": "", + "type": "tuple[]", + "internalType": "struct PaymentVerificationResult[]", + "components": [ + { + "name": "quoteHash", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "amountPaid", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "isValid", + "type": "bool", + "internalType": "bool" + } + ] } ], "stateMutability": "view" @@ -342,22 +314,22 @@ "name": "DataPaymentMade", "inputs": [ { - "name": "root", - "type": "bytes32", + "name": "rewardsAddress", + "type": "address", "indexed": true, - "internalType": "bytes32" + "internalType": "address" }, { - "name": "treeDepth", + "name": "amount", "type": "uint256", "indexed": true, "internalType": "uint256" }, { - "name": "amount", - "type": "uint256", + "name": "quoteHash", + "type": "bytes32", "indexed": true, - "internalType": "uint256" + "internalType": "bytes32" } ], "anonymous": false @@ -413,69 +385,23 @@ "internalType": "uint8" }, { - "name": "max", + "name": "maxDepth", "type": "uint8", "internalType": "uint8" } ] }, - { - "type": "error", - "name": "GracePeriodNotOver", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidAmount", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidChainlinkPrice", - "inputs": [] - }, { "type": "error", "name": "InvalidInputLength", "inputs": [] }, - { - "type": "error", - "name": "InvalidQuoteHash", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidRecipientsCount", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidRoot", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidTreeDepth", - "inputs": [] - }, { "type": "error", "name": "PaymentAlreadyExists", "inputs": [ { - "name": "poolHash", - "type": "bytes32", - "internalType": "bytes32" - } - ] - }, - { - "type": "error", - "name": "PaymentNotFound", - "inputs": [ - { - "name": "poolHash", + "name": "winnerPoolHash", "type": "bytes32", "internalType": "bytes32" } @@ -483,37 +409,12 @@ }, { "type": "error", - "name": "PriceFeedNull", - "inputs": [] - }, - { - "type": "error", - "name": "RootAlreadyPaid", - "inputs": [] - }, - { - "type": "error", - "name": "SequencerDown", - "inputs": [] - }, - { - "type": "error", - "name": "WrongCandidateCount", + "name": "SafeERC20FailedOperation", "inputs": [ { - "name": "poolIdx", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "expected", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "got", - "type": "uint256", - "internalType": "uint256" + "name": "token", + "type": "address", + "internalType": "address" } ] }, @@ -527,10 +428,10 @@ "internalType": "uint256" }, { - "name": "got", + "name": "actual", "type": "uint256", "internalType": "uint256" } ] } -] +] \ No newline at end of file diff --git a/abi/IPaymentVaultV6.json b/abi/IPaymentVaultV6.json deleted file mode 100644 index e825b12..0000000 --- a/abi/IPaymentVaultV6.json +++ /dev/null @@ -1,374 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "_antToken", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AntTokenNull", - "type": "error" - }, - { - "inputs": [], - "name": "BatchLimitExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInputLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "name": "DataPaymentMade", - "type": "event" - }, - { - "inputs": [], - "name": "antToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "batchLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "completedPayments", - "outputs": [ - { - "internalType": "bytes16", - "name": "rewardsAddress", - "type": "bytes16" - }, - { - "internalType": "uint128", - "name": "amount", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "dataSize", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "closeRecordsStored", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "records", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.Record[]", - "name": "recordsPerType", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "maxRecords", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "receivedPaymentCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liveTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkDensity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkSize", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.QuotingMetrics[]", - "name": "_metrics", - "type": "tuple[]" - } - ], - "name": "getQuote", - "outputs": [ - { - "internalType": "uint256[]", - "name": "prices", - "type": "uint256[]" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "internalType": "struct IPaymentVault.DataPayment[]", - "name": "_payments", - "type": "tuple[]" - } - ], - "name": "payForQuotes", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "requiredPaymentVerificationLength", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "dataSize", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "closeRecordsStored", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "records", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.Record[]", - "name": "recordsPerType", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "maxRecords", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "receivedPaymentCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liveTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkDensity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkSize", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.QuotingMetrics", - "name": "metrics", - "type": "tuple" - }, - { - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "internalType": "struct IPaymentVault.PaymentVerification[]", - "name": "_payments", - "type": "tuple[]" - } - ], - "name": "verifyPayment", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amountPaid", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isValid", - "type": "bool" - } - ], - "internalType": "struct IPaymentVault.PaymentVerificationResult[3]", - "name": "verificationResults", - "type": "tuple[3]" - } - ], - "stateMutability": "view", - "type": "function" - } -] \ No newline at end of file diff --git a/artifacts/MerklePaymentVault.json b/artifacts/MerklePaymentVault.json deleted file mode 100644 index 80cae40..0000000 --- a/artifacts/MerklePaymentVault.json +++ /dev/null @@ -1 +0,0 @@ -{"abi":[{"type":"constructor","inputs":[{"name":"_antToken","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"ANT_PRICE","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"CANDIDATES_PER_POOL","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"DEFAULT_COST_UNIT","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"MAX_MERKLE_DEPTH","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"PRECISION","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"antToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"type":"function","name":"costUnitPerDataType","inputs":[{"name":"","type":"uint8","internalType":"enum IMerklePaymentVault.DataType"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"estimateMerkleTreeCost","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"poolCommitments","type":"tuple[]","internalType":"struct IMerklePaymentVault.PoolCommitment[]","components":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"},{"name":"candidates","type":"tuple[16]","internalType":"struct IMerklePaymentVault.CandidateNode[16]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"metrics","type":"tuple","internalType":"struct IMerklePaymentVault.QuotingMetrics","components":[{"name":"dataType","type":"uint8","internalType":"enum IMerklePaymentVault.DataType"},{"name":"closeRecordsStored","type":"uint256","internalType":"uint256"},{"name":"recordsPerType","type":"tuple[]","internalType":"struct IMerklePaymentVault.Record[]","components":[{"name":"dataType","type":"uint8","internalType":"enum IMerklePaymentVault.DataType"},{"name":"records","type":"uint256","internalType":"uint256"}]}]}]}]},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"totalAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getPaymentInfo","inputs":[{"name":"winnerPoolHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"info","type":"tuple","internalType":"struct IMerklePaymentVault.PaymentInfo","components":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"},{"name":"paidNodeAddresses","type":"tuple[]","internalType":"struct IMerklePaymentVault.PaidNode[]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"poolIndex","type":"uint8","internalType":"uint8"}]}]}],"stateMutability":"view"},{"type":"function","name":"maxCostUnit","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"minPrice","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"payForMerkleTree","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"poolCommitments","type":"tuple[]","internalType":"struct IMerklePaymentVault.PoolCommitment[]","components":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"},{"name":"candidates","type":"tuple[16]","internalType":"struct IMerklePaymentVault.CandidateNode[16]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"metrics","type":"tuple","internalType":"struct IMerklePaymentVault.QuotingMetrics","components":[{"name":"dataType","type":"uint8","internalType":"enum IMerklePaymentVault.DataType"},{"name":"closeRecordsStored","type":"uint256","internalType":"uint256"},{"name":"recordsPerType","type":"tuple[]","internalType":"struct IMerklePaymentVault.Record[]","components":[{"name":"dataType","type":"uint8","internalType":"enum IMerklePaymentVault.DataType"},{"name":"records","type":"uint256","internalType":"uint256"}]}]}]}]},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"winnerPoolHash","type":"bytes32","internalType":"bytes32"},{"name":"totalAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"payForMerkleTree2","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"poolCommitments","type":"tuple[]","internalType":"struct IMerklePaymentVault.PoolCommitmentPacked[]","components":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"},{"name":"candidates","type":"tuple[16]","internalType":"struct IMerklePaymentVault.CandidateNodePacked[16]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"dataTypeAndTotalCostUnit","type":"uint256","internalType":"uint256"}]}]},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"winnerPoolHash","type":"bytes32","internalType":"bytes32"},{"name":"totalAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"payments","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"scalingFactor","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"event","name":"DataPaymentMade","inputs":[{"name":"root","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"treeDepth","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"amount","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"MerklePaymentMade","inputs":[{"name":"winnerPoolHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"depth","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"totalAmount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"merklePaymentTimestamp","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"error","name":"AntTokenNull","inputs":[]},{"type":"error","name":"BatchLimitExceeded","inputs":[]},{"type":"error","name":"DepthTooLarge","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"max","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"GracePeriodNotOver","inputs":[]},{"type":"error","name":"InvalidAmount","inputs":[]},{"type":"error","name":"InvalidChainlinkPrice","inputs":[]},{"type":"error","name":"InvalidInputLength","inputs":[]},{"type":"error","name":"InvalidQuoteHash","inputs":[]},{"type":"error","name":"InvalidRecipientsCount","inputs":[]},{"type":"error","name":"InvalidRoot","inputs":[]},{"type":"error","name":"InvalidTreeDepth","inputs":[]},{"type":"error","name":"PRBMath_SD59x18_Log_InputTooSmall","inputs":[{"name":"x","type":"int256","internalType":"SD59x18"}]},{"type":"error","name":"PaymentAlreadyExists","inputs":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"PaymentNotFound","inputs":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"PriceFeedNull","inputs":[]},{"type":"error","name":"RootAlreadyPaid","inputs":[]},{"type":"error","name":"SequencerDown","inputs":[]},{"type":"error","name":"WrongCandidateCount","inputs":[{"name":"poolIdx","type":"uint256","internalType":"uint256"},{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"got","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"WrongPoolCount","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"got","type":"uint256","internalType":"uint256"}]}],"bytecode":{"object":"0x60a03461018457601f61160838819003918201601f19168301916001600160401b038311848410176101885780849260209460405283398101031261018457516001600160a01b0381169081900361018457670de0b6b3a7640000600155600360025569d3c21bcecceda1000000600355801561013f576080526004602052670de0b6b3a76400007f91da3fd0782e51c6b3986e9e672fd566868e71f3dbc2d6c2cd6fbb3e361af2a78190557f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec8190557fabd6e7cb50984ff9c2f3e18a2660c3353dadf4e3291deeb275dae2cd1e44fe0581905560035f527f2e174c10e159ea99b867ce3205125c24a42d128804e4070ed6fcc8cc98166aa05560405161146b908161019d82396080518181816102720152818161075e01526108da0152f35b60405162461bcd60e51b815260206004820152601560248201527f496e76616c696420746f6b656e206164647265737300000000000000000000006044820152606490fd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c908163043b7587146109ae575080630716326d146109725780630e19c53914610941578063137978c31461092657806335815564146109095780634ec42e8e146108c55780636ecfa702146100ff578063937c07b7146106835780639e9dfde1146105bb578063a75e9b1914610445578063a960bd4914610104578063aaf5eb68146100ff578063be29e3d0146100ff578063e45be8eb146100e25763ed3437f8146100c1575f80fd5b346100de575f3660031901126100de576020600154604051908152f35b5f80fd5b346100de575f3660031901126100de576020600254604051908152f35b6109f2565b346100de5760603660031901126100de5760043560ff8116908181036100de57602435916001600160401b0383116100de57366023840112156100de578260040135926001600160401b0384116100de573660246104208602830101116100de57604435936001600160401b03851685036100de57600c831161042d5761018a84610c58565b808203610418575061019d853383610c73565b9081101561040457610420020191602483013592835f525f60205260ff60405f2054166103f15760440193610200604051906101d98183610b9b565b3682375f5b601081106103c75750610208816101f7610215936110e1565b61010060e082015191015190610ce0565b60011c6001851b90610acc565b92610221828685610d6b565b5f868152602081905260409020805468ffffffffffffffff0019841668ffffffffffffffffff1990911617600885901b68ffffffffffffffff001617815590969061026c8387610af3565b906001017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165f5b8581106102ff576040805160ff8a168152602081018b90526001600160401b038916818301528b908b9082907f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec9080606081015b0390a282519182526020820152f35b60ff61030b828d610b11565b51169061032061031b8388610c47565b610b3d565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201526044810187905292906020846064815f895af19182156103bc5760019461038a93610390575b506040519161037683610b51565b858060a01b03168252602082015285610bd4565b0161029c565b6103b09060203d81116103b5575b6103a88183610b9b565b810190610bbc565b610368565b503d61039e565b6040513d5f823e3d90fd5b806103e060206103d96001948b610c47565b0135611184565b6103ea8285610ccf565b52016101de565b83639d8c19ed60e01b5f5260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6347fe11b560e11b5f5260045260245260445ffd5b8263164b5f0360e11b5f52600452600c60245260445ffd5b346100de5760203660031901126100de5760043560606040805161046881610b80565b5f81525f60208201520152805f525f60205260405f20906040519061048c82610b80565b600183549360ff851684526001600160401b03602085019560081c168552019081546104b781610c30565b926104c56040519485610b9b565b81845260208401905f5260205f205f915b83831061058557505050506040830191825260ff83511615610573576001600160401b038484846040519384936020855260ff6080860194511660208601525116604084015251906060808401528151809152602060a084019201905f5b818110610542575050500390f35b825180516001600160a01b0316855260209081015160ff168186015286955060409094019390920191600101610534565b63272f9fc160e01b5f5260045260245ffd5b60016020819260405161059781610b51565b60ff8654858060a01b038116835260a01c16838201528152019201920191906104d6565b346100de576105c936610a14565b60ff841693600c851161066b576105df90610c58565b80830361065457505f5b82811061063257602061062a86600161062261061d6106148a8a61060e8b3383610c73565b91610a9c565b86810190610ab6565b610ced565b911b90610acc565b604051908152f35b8061064d6106436001938688610a9c565b6020810190610ab6565b50016105e9565b90506347fe11b560e11b5f5260045260245260445ffd5b8463164b5f0360e11b5f52600452600c60245260445ffd5b346100de5761069136610a14565b909160ff841692600c84116108ad576106a985610c58565b80820361041857505f5b8181106108955750906106cb9161060e843383610c73565b90813592835f525f60205260ff60405f2054166103f157602083016107006106f661061d8387610ab6565b6001841b90610acc565b9361070c848789610d6b565b5f878152602081905260409020805468ffffffffffffffff0019861668ffffffffffffffffff1990911617600887901b68ffffffffffffffff00161781556107548588610af3565b92600191909101917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691905f5b8781106107e45750506040805160ff8c168152602081018a90526001600160401b038916818301528a908a9082907f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec9080606081016102f0565b60ff6107f08284610b11565b51169061080961031b836108048b88610ab6565b610b25565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201526044810189905292906020846064815f8b5af19182156103bc5760019461087393610879575b506040519161085f83610b51565b858060a01b03168252602082015287610bd4565b0161078a565b6108909060203d81116103b5576103a88183610b9b565b610851565b806108a66106436001938587610a9c565b50016106b3565b8363164b5f0360e11b5f52600452600c60245260445ffd5b346100de575f3660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de575f3660031901126100de576020600354604051908152f35b346100de575f3660031901126100de57602060405160108152f35b346100de5760203660031901126100de5760043560048110156100de576109696020916109c7565b54604051908152f35b346100de5760203660031901126100de576004355f525f6020526040805f20546001600160401b0382519160ff8116835260081c166020820152f35b346100de575f3660031901126100de5780600c60209252f35b60048110156109de575f52600460205260405f2090565b634e487b7160e01b5f52602160045260245ffd5b346100de575f3660031901126100de576020604051670de0b6b3a76400008152f35b60606003198201126100de5760043560ff811681036100de57916024356001600160401b0381116100de57826023820112156100de578060040135926001600160401b0384116100de5760248460051b830101116100de5760240191906044356001600160401b03811681036100de5790565b903590603e19813603018212156100de570190565b9082101561040457610ab39160051b810190610a87565b90565b9035906101fe19813603018212156100de570190565b81810292918115918404141715610adf57565b634e487b7160e01b5f52601160045260245ffd5b8115610afd570490565b634e487b7160e01b5f52601260045260245ffd5b80518210156104045760209160051b010190565b601082101561040457610ab39160051b810190610a87565b356001600160a01b03811681036100de5790565b604081019081106001600160401b03821117610b6c57604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117610b6c57604052565b90601f801991011681019081106001600160401b03821117610b6c57604052565b908160209103126100de575180151581036100de5790565b80549068010000000000000000821015610b6c5760018201808255821015610404575f5260205f20019060018060a01b03815116825491602060ff60a01b91015160a01b16916affffffffffffffffffffff60a81b1617179055565b6001600160401b038111610b6c5760051b60200190565b9060108110156104045760061b0190565b60ff600191160160ff8111610adf57607f600191821c161b90565b916040519060208201924484524260408401526bffffffffffffffffffffffff199060601b1660608301526001600160401b0360c01b9060c01b166074820152605c8152610cc2607c82610b9b565b5190208115610afd570690565b9060108110156104045760051b0190565b91908201809211610adf57565b60405190610200610cfe8184610b9b565b3683375f5b60108110610d20575050806101f7610d1a926110e1565b60011c90565b610d2a8183610b25565b90602082013591605e19813603018312156100de57600192610d4c9101610f38565b610d568286610ccf565b5201610d03565b5f198114610adf5760010190565b60ff90939291931691610d7d83610c30565b93610d8b6040519586610b9b565b838552601f19610d9a85610c30565b0136602087013760405192610200610db28186610b9b565b36853760405190602082019244845260408301526001600160401b0360c01b9060c01b16606082015260488152610dea606882610b9b565b519020935f915f955b85841080610ec9575b15610e76576040516020810191825287604082015260408152610e20606082610b9b565b51902095600f8716610e328187610ccf565b5115610e49575b50610e4390610d5d565b95610df3565b610e439194816001610e5e610e6f948a610ccf565b52610e698287610b11565b52610d5d565b9390610e39565b50945092915003610e845790565b60405162461bcd60e51b815260206004820152601f60248201527f4661696c656420746f2073656c65637420656e6f7567682077696e6e657273006044820152606490fd5b5060648710610dfc565b81810392915f138015828513169184121617610adf57565b600160ff1b8114610adf575f0390565b81810292915f8212600160ff1b821416610adf578184051490151715610adf57565b9190915f8382019384129112908015821691151617610adf57565b905f5f9260408101915b610f4c83836111ae565b9050851015610fa357610f5f83836111ae565b861015610404578560061b0180359160048310156100de57610f95610f9b926020610f8b6001966109c7565b5491013590610acc565b90610ce0565b940193610f42565b9150919250610fb1816111e3565b913560048110156100de57610fd891610fcc610fd3926109c7565b5490610ce0565b6111e3565b9081811480156110c3575b80156110b2575b6110aa57610ff78261121c565b906110018161121c565b91801580156110a2575b6110985761104b6ec097ce7bc90715b34b9f1000000000926110456110869561103f61103961105c9661127a565b9161127a565b90610ed3565b9561125e565b93611057600154610eeb565b610efb565b0561108160025493670de0b6b3a7640000806110788784610acc565b04910492610f1d565b610ed3565b905f821315611093575090565b905090565b5050505060025490565b50821561100b565b505060025490565b50670de0b6b3a76400008214610fea565b50670de0b6b3a76400008114610fe3565b91908203918211610adf57565b906001915b601083106110f357509050565b6110fd8382610ccf565b5191835b8015801580611166575b1561114b575f19820191808311610adf576111316111298487610ccf565b519186610ccf565b521561110157634e487b7160e01b5f52601160045260245ffd5b509261115d6001939495929585610ccf565b520191906110e6565b505f198201828111610adf5761117d869186610ccf565b511161110b565b60ff81169060048210156109de57610fd3610fd89160081c610fcc6111a8826111e3565b946109c7565b903590601e19813603018212156100de57018035906001600160401b0382116100de57602001918160061b360383136100de57565b600354801561121657670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610adf57610ab391610af3565b50505f90565b670de0b6b3a764000081101561124857670de0b6b3a764000003670de0b6b3a76400008111610adf5790565b670de0b6b3a763ffff198101908111610adf5790565b90808210156112715790610ab3916110d4565b610ab3916110d4565b80156112b857611289906112ef565b670de0b6b3a7640000810290808205670de0b6b3a76400001490151715610adf576714057b7ef767814f900590565b60405162461bcd60e51b815260206004820152600f60248201526e1b1b8a0c0a481d5b9919599a5b9959608a1b6044820152606490fd5b805f81131561142357670de0b6b3a7640000811261140357506001905b670de0b6b3a764000081056fffffffffffffffffffffffffffffffff811160071b90811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c9060ff821160031b91821c92600f841160021b93841c94600160038711811b96871c119617171717171717670de0b6b3a7640000810291811d90670de0b6b3a764000082146113f057506706f05b59d3b20000905b5f82136113ba5750500290565b80670de0b6b3a764000091020590671bc16d674ec800008212156113e2575b60011d906113ad565b809192019160011d906113d9565b9050670de0b6b3a7640000929150020290565b5f1991508015610afd576ec097ce7bc90715b34b9f10000000000561130c565b63059b101b60e01b5f5260045260245ffdfea264697066735822122053c5f9ea3f0ebdda81c7470200eb8738e4cd84cfb631695332961f220efb33c764736f6c634300081c0033","sourceMap":"421:19562:1:-:0;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;1306:4;;421:19562;1409:1;;421:19562;1497:4;1409:1;421:19562;1805:23;;421:19562;;1864:28;;1950:19;421:19562;1605:4;1306;1605;421:19562;;;1605:4;421:19562;;;1605:4;421:19562;;;1409:1;-1:-1:-1;1605:4:1;;421:19562;;;;;;;;;1864:28;421:19562;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;421:19562:1;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;421:19562:1;;;;;;-1:-1:-1;421:19562:1;;;;;-1:-1:-1;421:19562:1","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c908163043b7587146109ae575080630716326d146109725780630e19c53914610941578063137978c31461092657806335815564146109095780634ec42e8e146108c55780636ecfa702146100ff578063937c07b7146106835780639e9dfde1146105bb578063a75e9b1914610445578063a960bd4914610104578063aaf5eb68146100ff578063be29e3d0146100ff578063e45be8eb146100e25763ed3437f8146100c1575f80fd5b346100de575f3660031901126100de576020600154604051908152f35b5f80fd5b346100de575f3660031901126100de576020600254604051908152f35b6109f2565b346100de5760603660031901126100de5760043560ff8116908181036100de57602435916001600160401b0383116100de57366023840112156100de578260040135926001600160401b0384116100de573660246104208602830101116100de57604435936001600160401b03851685036100de57600c831161042d5761018a84610c58565b808203610418575061019d853383610c73565b9081101561040457610420020191602483013592835f525f60205260ff60405f2054166103f15760440193610200604051906101d98183610b9b565b3682375f5b601081106103c75750610208816101f7610215936110e1565b61010060e082015191015190610ce0565b60011c6001851b90610acc565b92610221828685610d6b565b5f868152602081905260409020805468ffffffffffffffff0019841668ffffffffffffffffff1990911617600885901b68ffffffffffffffff001617815590969061026c8387610af3565b906001017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165f5b8581106102ff576040805160ff8a168152602081018b90526001600160401b038916818301528b908b9082907f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec9080606081015b0390a282519182526020820152f35b60ff61030b828d610b11565b51169061032061031b8388610c47565b610b3d565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201526044810187905292906020846064815f895af19182156103bc5760019461038a93610390575b506040519161037683610b51565b858060a01b03168252602082015285610bd4565b0161029c565b6103b09060203d81116103b5575b6103a88183610b9b565b810190610bbc565b610368565b503d61039e565b6040513d5f823e3d90fd5b806103e060206103d96001948b610c47565b0135611184565b6103ea8285610ccf565b52016101de565b83639d8c19ed60e01b5f5260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b6347fe11b560e11b5f5260045260245260445ffd5b8263164b5f0360e11b5f52600452600c60245260445ffd5b346100de5760203660031901126100de5760043560606040805161046881610b80565b5f81525f60208201520152805f525f60205260405f20906040519061048c82610b80565b600183549360ff851684526001600160401b03602085019560081c168552019081546104b781610c30565b926104c56040519485610b9b565b81845260208401905f5260205f205f915b83831061058557505050506040830191825260ff83511615610573576001600160401b038484846040519384936020855260ff6080860194511660208601525116604084015251906060808401528151809152602060a084019201905f5b818110610542575050500390f35b825180516001600160a01b0316855260209081015160ff168186015286955060409094019390920191600101610534565b63272f9fc160e01b5f5260045260245ffd5b60016020819260405161059781610b51565b60ff8654858060a01b038116835260a01c16838201528152019201920191906104d6565b346100de576105c936610a14565b60ff841693600c851161066b576105df90610c58565b80830361065457505f5b82811061063257602061062a86600161062261061d6106148a8a61060e8b3383610c73565b91610a9c565b86810190610ab6565b610ced565b911b90610acc565b604051908152f35b8061064d6106436001938688610a9c565b6020810190610ab6565b50016105e9565b90506347fe11b560e11b5f5260045260245260445ffd5b8463164b5f0360e11b5f52600452600c60245260445ffd5b346100de5761069136610a14565b909160ff841692600c84116108ad576106a985610c58565b80820361041857505f5b8181106108955750906106cb9161060e843383610c73565b90813592835f525f60205260ff60405f2054166103f157602083016107006106f661061d8387610ab6565b6001841b90610acc565b9361070c848789610d6b565b5f878152602081905260409020805468ffffffffffffffff0019861668ffffffffffffffffff1990911617600887901b68ffffffffffffffff00161781556107548588610af3565b92600191909101917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691905f5b8781106107e45750506040805160ff8c168152602081018a90526001600160401b038916818301528a908a9082907f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec9080606081016102f0565b60ff6107f08284610b11565b51169061080961031b836108048b88610ab6565b610b25565b6040516323b872dd60e01b81523360048201526001600160a01b03821660248201526044810189905292906020846064815f8b5af19182156103bc5760019461087393610879575b506040519161085f83610b51565b858060a01b03168252602082015287610bd4565b0161078a565b6108909060203d81116103b5576103a88183610b9b565b610851565b806108a66106436001938587610a9c565b50016106b3565b8363164b5f0360e11b5f52600452600c60245260445ffd5b346100de575f3660031901126100de576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b346100de575f3660031901126100de576020600354604051908152f35b346100de575f3660031901126100de57602060405160108152f35b346100de5760203660031901126100de5760043560048110156100de576109696020916109c7565b54604051908152f35b346100de5760203660031901126100de576004355f525f6020526040805f20546001600160401b0382519160ff8116835260081c166020820152f35b346100de575f3660031901126100de5780600c60209252f35b60048110156109de575f52600460205260405f2090565b634e487b7160e01b5f52602160045260245ffd5b346100de575f3660031901126100de576020604051670de0b6b3a76400008152f35b60606003198201126100de5760043560ff811681036100de57916024356001600160401b0381116100de57826023820112156100de578060040135926001600160401b0384116100de5760248460051b830101116100de5760240191906044356001600160401b03811681036100de5790565b903590603e19813603018212156100de570190565b9082101561040457610ab39160051b810190610a87565b90565b9035906101fe19813603018212156100de570190565b81810292918115918404141715610adf57565b634e487b7160e01b5f52601160045260245ffd5b8115610afd570490565b634e487b7160e01b5f52601260045260245ffd5b80518210156104045760209160051b010190565b601082101561040457610ab39160051b810190610a87565b356001600160a01b03811681036100de5790565b604081019081106001600160401b03821117610b6c57604052565b634e487b7160e01b5f52604160045260245ffd5b606081019081106001600160401b03821117610b6c57604052565b90601f801991011681019081106001600160401b03821117610b6c57604052565b908160209103126100de575180151581036100de5790565b80549068010000000000000000821015610b6c5760018201808255821015610404575f5260205f20019060018060a01b03815116825491602060ff60a01b91015160a01b16916affffffffffffffffffffff60a81b1617179055565b6001600160401b038111610b6c5760051b60200190565b9060108110156104045760061b0190565b60ff600191160160ff8111610adf57607f600191821c161b90565b916040519060208201924484524260408401526bffffffffffffffffffffffff199060601b1660608301526001600160401b0360c01b9060c01b166074820152605c8152610cc2607c82610b9b565b5190208115610afd570690565b9060108110156104045760051b0190565b91908201809211610adf57565b60405190610200610cfe8184610b9b565b3683375f5b60108110610d20575050806101f7610d1a926110e1565b60011c90565b610d2a8183610b25565b90602082013591605e19813603018312156100de57600192610d4c9101610f38565b610d568286610ccf565b5201610d03565b5f198114610adf5760010190565b60ff90939291931691610d7d83610c30565b93610d8b6040519586610b9b565b838552601f19610d9a85610c30565b0136602087013760405192610200610db28186610b9b565b36853760405190602082019244845260408301526001600160401b0360c01b9060c01b16606082015260488152610dea606882610b9b565b519020935f915f955b85841080610ec9575b15610e76576040516020810191825287604082015260408152610e20606082610b9b565b51902095600f8716610e328187610ccf565b5115610e49575b50610e4390610d5d565b95610df3565b610e439194816001610e5e610e6f948a610ccf565b52610e698287610b11565b52610d5d565b9390610e39565b50945092915003610e845790565b60405162461bcd60e51b815260206004820152601f60248201527f4661696c656420746f2073656c65637420656e6f7567682077696e6e657273006044820152606490fd5b5060648710610dfc565b81810392915f138015828513169184121617610adf57565b600160ff1b8114610adf575f0390565b81810292915f8212600160ff1b821416610adf578184051490151715610adf57565b9190915f8382019384129112908015821691151617610adf57565b905f5f9260408101915b610f4c83836111ae565b9050851015610fa357610f5f83836111ae565b861015610404578560061b0180359160048310156100de57610f95610f9b926020610f8b6001966109c7565b5491013590610acc565b90610ce0565b940193610f42565b9150919250610fb1816111e3565b913560048110156100de57610fd891610fcc610fd3926109c7565b5490610ce0565b6111e3565b9081811480156110c3575b80156110b2575b6110aa57610ff78261121c565b906110018161121c565b91801580156110a2575b6110985761104b6ec097ce7bc90715b34b9f1000000000926110456110869561103f61103961105c9661127a565b9161127a565b90610ed3565b9561125e565b93611057600154610eeb565b610efb565b0561108160025493670de0b6b3a7640000806110788784610acc565b04910492610f1d565b610ed3565b905f821315611093575090565b905090565b5050505060025490565b50821561100b565b505060025490565b50670de0b6b3a76400008214610fea565b50670de0b6b3a76400008114610fe3565b91908203918211610adf57565b906001915b601083106110f357509050565b6110fd8382610ccf565b5191835b8015801580611166575b1561114b575f19820191808311610adf576111316111298487610ccf565b519186610ccf565b521561110157634e487b7160e01b5f52601160045260245ffd5b509261115d6001939495929585610ccf565b520191906110e6565b505f198201828111610adf5761117d869186610ccf565b511161110b565b60ff81169060048210156109de57610fd3610fd89160081c610fcc6111a8826111e3565b946109c7565b903590601e19813603018212156100de57018035906001600160401b0382116100de57602001918160061b360383136100de57565b600354801561121657670de0b6b3a7640000820291808304670de0b6b3a76400001490151715610adf57610ab391610af3565b50505f90565b670de0b6b3a764000081101561124857670de0b6b3a764000003670de0b6b3a76400008111610adf5790565b670de0b6b3a763ffff198101908111610adf5790565b90808210156112715790610ab3916110d4565b610ab3916110d4565b80156112b857611289906112ef565b670de0b6b3a7640000810290808205670de0b6b3a76400001490151715610adf576714057b7ef767814f900590565b60405162461bcd60e51b815260206004820152600f60248201526e1b1b8a0c0a481d5b9919599a5b9959608a1b6044820152606490fd5b805f81131561142357670de0b6b3a7640000811261140357506001905b670de0b6b3a764000081056fffffffffffffffffffffffffffffffff811160071b90811c6001600160401b03811160061b90811c63ffffffff811160051b90811c61ffff811160041b90811c9060ff821160031b91821c92600f841160021b93841c94600160038711811b96871c119617171717171717670de0b6b3a7640000810291811d90670de0b6b3a764000082146113f057506706f05b59d3b20000905b5f82136113ba5750500290565b80670de0b6b3a764000091020590671bc16d674ec800008212156113e2575b60011d906113ad565b809192019160011d906113d9565b9050670de0b6b3a7640000929150020290565b5f1991508015610afd576ec097ce7bc90715b34b9f10000000000561130c565b63059b101b60e01b5f5260045260245ffdfea264697066735822122053c5f9ea3f0ebdda81c7470200eb8738e4cd84cfb631695332961f220efb33c764736f6c634300081c0033","sourceMap":"421:19562:1:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;9795:29;421:19562;9795:29;;;421:19562;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;1275:35;421:19562;;;;;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;1383:27;421:19562;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;799:2;6280:24;;6276:100;;6458:27;;;:::i;:::-;6499:39;;;6495:130;;6749:10;6707:77;6749:10;;6707:77;;:::i;:::-;421:19562;;;;;;;;;;;;;;;;;;;;;;;;;;;6981:109;;421:19562;7231:21;421:19562;;;;;;;;;:::i;:::-;;;;;11978:6;421:19562;11978:6;;;;12128;12237:21;12128:6;;7354:23;12128:6;;:::i;:::-;421:19562;;;;;;;;12237:21;;:::i;:::-;421:19562;;;;;7354:23;;:::i;:::-;7466:65;;;;;;:::i;:::-;421:19562;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;;;;7907:19;;;;:::i;:::-;7941:13;421:19562;8273:22;8158:8;-1:-1:-1;;;;;421:19562:1;;7956:9;;;;;;421:19562;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;8390:77;;421:19562;;;;8390:77;;;;421:19562;;;;;;;;;;7967:3;421:19562;8002:16;;;;:::i;:::-;421:19562;;8057:30;:45;:30;;;;:::i;:::-;:45;:::i;:::-;421:19562;;-1:-1:-1;;;8158:64:1;;6749:10;421:19562;8158:64;;421:19562;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;-1:-1:-1;8158:64:1;;;;;;;;421:19562;8158:64;8273:91;8158:64;;;7967:3;421:19562;;;;;;;:::i;:::-;;;;;;;;;;8301:62;;421:19562;8273:91;;:::i;:::-;421:19562;7941:13;;8158:64;;;421:19562;8158:64;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;421:19562;;;;;;;;;11986:3;12033:13;12017:55;421:19562;12033:13;421:19562;12033:13;;;:::i;:::-;:38;421:19562;12017:55;:::i;:::-;12005:67;;;;:::i;:::-;421:19562;;11963:13;;6981:109;3982:36;;;;421:19562;7043:36;421:19562;;;;7043:36;421:19562;;;;;;;;;;;;6495:130;3160:53;;;421:19562;6561:53;421:19562;;;;;;6561:53;6276:100;2926:38;;;;421:19562;6327:38;421:19562;;799:2;421:19562;;;;6327:38;421:19562;;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10909:15;10905:84;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;-1:-1:-1;421:19562:1;;;;;;;;;;;;;10905:84;10947:31;;;421:19562;10947:31;421:19562;;;;10947:31;421:19562;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;9296:24;799:2;9296:24;;9292:100;;9474:27;;;:::i;:::-;9515:39;;;9511:130;;9729:13;421:19562;9744:26;;;;;;9795:29;10508:23;10144:10;421:19562;10363:44;10385:21;10226:30;10144:10;;10102:77;10144:10;;10102:77;;:::i;:::-;10226:30;;:::i;:::-;10385:21;;;;;:::i;:::-;10363:44;:::i;:::-;421:19562;;10508:23;;:::i;:::-;421:19562;;;;;;9772:3;9795:18;:29;:18;421:19562;9795:18;;;;:::i;:::-;:29;;;;;:::i;:::-;;421:19562;9729:13;;9511:130;3160:53;;;;;421:19562;9577:53;421:19562;;;;;;9577:53;9292:100;2926:38;;;;421:19562;9343:38;421:19562;;799:2;421:19562;;;;9343:38;421:19562;;;;;;;:::i;:::-;;;;;;2879:24;799:2;2879:24;;2875:100;;3057:27;;;:::i;:::-;3098:39;;;3094:130;;3312:13;421:19562;3327:26;;;;;;3694:10;;3776:30;3694:10;3652:77;3694:10;;3652:77;;:::i;3776:30::-;421:19562;;;;;;;;3378:29;421:19562;;;;;;;3920:109;;3378:29;4157:21;;4280:23;4135:44;4157:21;;;;:::i;4135:44::-;421:19562;;;4280:23;;:::i;:::-;4392:65;;;;;;:::i;:::-;421:19562;;;;3378:29;421:19562;;;;;;;;-1:-1:-1;;421:19562:1;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;4833:19;;;;:::i;:::-;4867:13;421:19562;5199:22;;;;;5084:8;-1:-1:-1;;;;;421:19562:1;;4867:13;421:19562;4882:9;;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;5316:77;;421:19562;;;;5316:77;421:19562;4893:3;421:19562;4928:16;;;;:::i;:::-;421:19562;;4983:21;:45;:30;:21;;;;;:::i;:::-;:30;:::i;:45::-;421:19562;;-1:-1:-1;;;5084:64:1;;3694:10;421:19562;5084:64;;421:19562;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;3378:29;421:19562;;;-1:-1:-1;5084:64:1;;;;;;;;421:19562;5084:64;5199:91;5084:64;;;4893:3;421:19562;;;;;;;:::i;:::-;;;;;;;;;3378:29;5227:62;;421:19562;5199:91;;:::i;:::-;421:19562;4867:13;;5084:64;;;3378:29;5084:64;;;;;;;;;:::i;:::-;;;3355:3;3378:18;:29;:18;421:19562;3378:18;;;;:::i;:29::-;;421:19562;3312:13;;2875:100;2926:38;;;;421:19562;2926:38;421:19562;;799:2;421:19562;;;;2926:38;421:19562;;;;;;-1:-1:-1;;421:19562:1;;;;;;545:41;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;1468:33;421:19562;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;;;907:2;421:19562;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;1648:55;421:19562;1648:55;;:::i;:::-;421:19562;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;421:19562:1;;;;;799:2;421:19562;;;;;;;;;;;-1:-1:-1;421:19562:1;;;;;-1:-1:-1;421:19562:1;;:::o;:::-;;;;-1:-1:-1;421:19562:1;;;;;-1:-1:-1;421:19562:1;;;;;;;-1:-1:-1;;421:19562:1;;;;;;;1605:4;421:19562;;;;;-1:-1:-1;;421:19562:1;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::i;:::-;;-1:-1:-1;;;;;421:19562:1;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;:::o;:::-;;;;-1:-1:-1;421:19562:1;;;;;-1:-1:-1;421:19562:1;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;:::o;:::-;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;421:19562:1;;-1:-1:-1;421:19562:1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;;;421:19562:1;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::o;11142:188::-;421:19562;11254:1;11142:188;421:19562;;;;;;;;11254:1;421:19562;;;;;11142:188;:::o;11401:267::-;;421:19562;;11548:70;;;;11565:16;;421:19562;;11583:15;421:19562;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;11548:70;;;;;;;:::i;:::-;421:19562;11538:81;;421:19562;;;;;11401:267;:::o;421:19562::-;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;14565:476::-;421:19562;;;;;;;;:::i;:::-;;;;-1:-1:-1;14772:6:1;421:19562;14772:6;;;;14899;;;;15008:21;14899:6;;:::i;15008:21::-;421:19562;;14565:476;:::o;14780:3::-;14821:13;;;;:::i;:::-;:21;;;;421:19562;;;;;;;;;;;;;;;14811:32;421:19562;;14811:32;:::i;:::-;14799:44;;;;:::i;:::-;421:19562;;14757:13;;421:19562;-1:-1:-1;;421:19562:1;;;;;;;:::o;19081:900::-;421:19562;19081:900;;;;;421:19562;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;421:19562:1;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;19336:55;421:19562;19336:55;;19353:16;;421:19562;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;19336:55;;;;;;;:::i;:::-;421:19562;19326:66;;19403:25;-1:-1:-1;19438:20:1;-1:-1:-1;19509:364:1;;19516:21;;;:39;;;19509:364;19516:39;;;421:19562;;;19588:32;;421:19562;;;;;;;;;19588:32;;;421:19562;19588:32;;:::i;:::-;421:19562;19578:43;;421:19562;;;;19692:13;;;;:::i;:::-;421:19562;;19687:152;;19509:364;19852:10;;;;:::i;:::-;19509:364;;;19687:152;19852:10;19725:20;;;19741:4;19725:20;19809:15;19725:20;;;:::i;:::-;421:19562;19763:28;;;;:::i;:::-;421:19562;19809:15;:::i;:::-;19687:152;;;;19516:39;;;;;;;19891:22;421:19562;;19081:900;:::o;421:19562::-;;;-1:-1:-1;;;421:19562:1;;;;;;;;;;;;;;;;;;;;19516:39;19541:14;19552:3;19541:14;;19516:39;;421:19562;;;;;;;-1:-1:-1;421:19562:1;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;;421:19562:1;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;;;421:19562:1;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;15499:1825::-;;421:19562;;17530:22;;;;17506:211;17561:3;17530:22;;;;:::i;:::-;17526:33;;;;;;;17605:22;;;;:::i;:::-;421:19562;;;;;;;;;;;;;;;;;;17653:53;17644:62;17653:36;17692:14;17653:36;421:19562;17653:36;;:::i;:::-;421:19562;17692:14;;421:19562;17653:53;;:::i;:::-;17644:62;;:::i;:::-;17561:3;421:19562;17511:13;;;17526:33;;;;;;15674:24;;;:::i;:::-;421:19562;;;;;;;;15729:64;15755:37;;15739:53;15755:37;;:::i;:::-;421:19562;15739:53;;:::i;:::-;15729:64;:::i;:::-;15884:24;;;;:51;;;;17506:211;15884:78;;;;17506:211;15880:124;;16099:31;;;:::i;:::-;16160;;;;:::i;:::-;16268:14;;;:32;;;;17506:211;16264:78;;16640:32;421:19562;16438:23;16539:19;17127:45;16438:23;16489;16438;16863:32;16438:23;;:::i;:::-;16489;;:::i;:::-;16539:19;;:::i;:::-;16640:32;;:::i;:::-;421:19562;16863:22;421:19562;;16863:22;:::i;:::-;:32;:::i;:::-;421:19562;17127:25;16970:8;421:19562;16957:21;1605:4;16957:21;;;;;:::i;:::-;421:19562;;;17127:25;;:::i;:::-;:45;:::i;:::-;17235:10;421:19562;17235:10;;;17231:56;;17296:21;15499:1825;:::o;17231:56::-;17261:15;;;:::o;16264:78::-;421:19562;;;;16323:8;421:19562;16316:15;:::o;16268:32::-;16286:14;;;16268:32;;15880:124;421:19562;;15985:8;421:19562;15978:15;:::o;15884:78::-;15939:23;1605:4;15939:23;;15884:78;;:51;15912:23;1605:4;15912:23;;15884:51;;421:19562;;;;;;;;;;:::o;18669:344::-;;18758:1;18741:266;18761:6;18765:2;18761:6;;;;18669:344;;;:::o;18769:3::-;18802:9;;;;:::i;:::-;421:19562;18825:13;;18852:116;18859:5;;;;:28;;;18852:116;18859:28;;;-1:-1:-1;;421:19562:1;;;;;;;;18907:25;18919:13;;;;:::i;:::-;421:19562;18907:25;;;:::i;:::-;421:19562;;18852:116;421:19562;;;;18863:1;421:19562;;;;;18863:1;421:19562;18859:28;;;18981:15;18758:1;18859:28;;;;;18981:15;;:::i;:::-;421:19562;;18746:13;;;;18859:28;-1:-1:-1;;;421:19562:1;;;;;;;18868:13;;;;;:::i;:::-;421:19562;18868:19;18859:28;;12507:1979;421:19562;12716:31;;421:19562;;;;;;;12909:45;12899:56;421:19562;12811:1;421:19562;12925:29;12844:24;;;:::i;:::-;12925:29;;:::i;421:19562::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;421:19562:1;;;;;;;;;;;;;;;;:::o;17802:187::-;17882:11;421:19562;17882:16;;17878:55;;1605:4;421:19562;;;;;;1605:4;421:19562;;;;;;;17949:33;;;:::i;17878:55::-;17914:8;;421:19562;17914:8;:::o;18052:159::-;1605:4;18138:6;;;18134:49;;1605:4;421:19562;1605:4;421:19562;;;;18052:159;:::o;18134:49::-;-1:-1:-1;;421:19562:1;;;;;;;18160:12;:::o;18052:159::-;;18138:6;;;;18134:49;;18199:5;;;;:::i;18134:49::-;18167:5;;;:::i;18268:296::-;18346:6;;18342:62;;11916:7:18;;;:::i;:::-;2515:4:14;421:19562:1;;;;;;2515:4:14;421:19562:1;;;;;;;1300:20:14;11916:34:18;421:19562:1;18268:296;:::o;18342:62::-;421:19562;;-1:-1:-1;;;18368:25:1;;421:19562;18368:25;;;421:19562;;;;;;-1:-1:-1;;;421:19562:1;;;;18368:25;;;20268:1697:18;20329:24;20371:1;20363:9;;;20359:82;;2515:4:14;20492:13:18;;2515:4:14;;20521:8:18;20528:1;20488:194;;2515:4:14;2632;;12755:169:3;;;;;;;;-1:-1:-1;;;;;12941:153:3;;;;;;;13111:145;;;;;;;;13273:141;;;;;;;;13430:139;;;;;;;;;13585:138;;;;;;;;;13739;;13430:139;13739:138;;;;;;;13929:102;12941:153;;13111:145;13273:141;13430:139;13585:138;13739;13929:102;2515:4:14;2632;;;;;21179:10:18;2515:4:14;21179:10:18;;21175:70;;21475:25;1024:6:14;21470:426:18;21502:9;20371:1;21502:9;;;;2632:4:14;;;20268:1697:18;:::o;21513:11::-;2632:4:14;2515;2632;;;21642:16:18;21456:4;21642:16;;;21638:248;;21513:11;13739:138:3;2632:4:14;21475:25:18;;;21638:248;1024:6:14;;;;2632:4;13739:138:3;2632:4:14;21638:248:18;;;21175:70;2632:4:14;;2515;2632;;;;;21205:29:18;:::o;20488:194::-;-1:-1:-1;;421:19562:1;-1:-1:-1;2632:4:14;;;;;;20488:194:18;;20359:82;20391:43;;;20371:1;20391:43;;421:19562:1;;20371:1:18;20391:43","linkReferences":{},"immutableReferences":{"287":[{"start":626,"length":32},{"start":1886,"length":32},{"start":2266,"length":32}]}},"methodIdentifiers":{"ANT_PRICE()":"be29e3d0","CANDIDATES_PER_POOL()":"137978c3","DEFAULT_COST_UNIT()":"6ecfa702","MAX_MERKLE_DEPTH()":"043b7587","PRECISION()":"aaf5eb68","antToken()":"4ec42e8e","costUnitPerDataType(uint8)":"0e19c539","estimateMerkleTreeCost(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":"9e9dfde1","getPaymentInfo(bytes32)":"a75e9b19","maxCostUnit()":"35815564","minPrice()":"e45be8eb","payForMerkleTree(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":"937c07b7","payForMerkleTree2(uint8,(bytes32,(address,uint256)[16])[],uint64)":"a960bd49","payments(bytes32)":"0716326d","scalingFactor()":"ed3437f8"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_antToken\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AntTokenNull\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchLimitExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"max\",\"type\":\"uint8\"}],\"name\":\"DepthTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"GracePeriodNotOver\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidAmount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidChainlinkPrice\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInputLength\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidQuoteHash\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRecipientsCount\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidRoot\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidTreeDepth\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"SD59x18\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"PRBMath_SD59x18_Log_InputTooSmall\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"}],\"name\":\"PaymentAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"}],\"name\":\"PaymentNotFound\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"PriceFeedNull\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RootAlreadyPaid\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"SequencerDown\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"poolIdx\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"WrongCandidateCount\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"got\",\"type\":\"uint256\"}],\"name\":\"WrongPoolCount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"root\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"treeDepth\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"DataPaymentMade\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"MerklePaymentMade\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"ANT_PRICE\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"CANDIDATES_PER_POOL\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"DEFAULT_COST_UNIT\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_MERKLE_DEPTH\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PRECISION\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"antToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enum IMerklePaymentVault.DataType\",\"name\":\"\",\"type\":\"uint8\"}],\"name\":\"costUnitPerDataType\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"enum IMerklePaymentVault.DataType\",\"name\":\"dataType\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"closeRecordsStored\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"enum IMerklePaymentVault.DataType\",\"name\":\"dataType\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"records\",\"type\":\"uint256\"}],\"internalType\":\"struct IMerklePaymentVault.Record[]\",\"name\":\"recordsPerType\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IMerklePaymentVault.QuotingMetrics\",\"name\":\"metrics\",\"type\":\"tuple\"}],\"internalType\":\"struct IMerklePaymentVault.CandidateNode[16]\",\"name\":\"candidates\",\"type\":\"tuple[16]\"}],\"internalType\":\"struct IMerklePaymentVault.PoolCommitment[]\",\"name\":\"poolCommitments\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"estimateMerkleTreeCost\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"}],\"name\":\"getPaymentInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"poolIndex\",\"type\":\"uint8\"}],\"internalType\":\"struct IMerklePaymentVault.PaidNode[]\",\"name\":\"paidNodeAddresses\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IMerklePaymentVault.PaymentInfo\",\"name\":\"info\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxCostUnit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minPrice\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"enum IMerklePaymentVault.DataType\",\"name\":\"dataType\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"closeRecordsStored\",\"type\":\"uint256\"},{\"components\":[{\"internalType\":\"enum IMerklePaymentVault.DataType\",\"name\":\"dataType\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"records\",\"type\":\"uint256\"}],\"internalType\":\"struct IMerklePaymentVault.Record[]\",\"name\":\"recordsPerType\",\"type\":\"tuple[]\"}],\"internalType\":\"struct IMerklePaymentVault.QuotingMetrics\",\"name\":\"metrics\",\"type\":\"tuple\"}],\"internalType\":\"struct IMerklePaymentVault.CandidateNode[16]\",\"name\":\"candidates\",\"type\":\"tuple[16]\"}],\"internalType\":\"struct IMerklePaymentVault.PoolCommitment[]\",\"name\":\"poolCommitments\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"payForMerkleTree\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"dataTypeAndTotalCostUnit\",\"type\":\"uint256\"}],\"internalType\":\"struct IMerklePaymentVault.CandidateNodePacked[16]\",\"name\":\"candidates\",\"type\":\"tuple[16]\"}],\"internalType\":\"struct IMerklePaymentVault.PoolCommitmentPacked[]\",\"name\":\"poolCommitments\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"payForMerkleTree2\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"payments\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"scalingFactor\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"events\":{\"DataPaymentMade(bytes32,uint256,uint256)\":{\"params\":{\"amount\":\"Payment amount\",\"root\":\"Merkle root\",\"treeDepth\":\"Tree depth\"}},\"MerklePaymentMade(bytes32,uint8,uint256,uint64)\":{\"params\":{\"depth\":\"Tree depth\",\"merklePaymentTimestamp\":\"Client-provided timestamp\",\"totalAmount\":\"Total tokens paid to winners\",\"winnerPoolHash\":\"Hash of the selected winner pool\"}}},\"kind\":\"dev\",\"methods\":{\"estimateMerkleTreeCost(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)\":{\"params\":{\"depth\":\"Tree depth (determines number of nodes paid)\",\"merklePaymentTimestamp\":\"Client-provided timestamp\",\"poolCommitments\":\"Array of pool commitments (2^ceil(depth/2))\"},\"returns\":{\"totalAmount\":\"Estimated total tokens that would be paid\"}},\"getPaymentInfo(bytes32)\":{\"params\":{\"winnerPoolHash\":\"Hash returned from payForMerkleTree\"},\"returns\":{\"info\":\"Payment information stored on-chain\"}},\"payForMerkleTree(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)\":{\"params\":{\"depth\":\"Tree depth (determines number of nodes paid)\",\"merklePaymentTimestamp\":\"Client-provided timestamp\",\"poolCommitments\":\"Array of pool commitments (2^ceil(depth/2))\"},\"returns\":{\"totalAmount\":\"Total tokens paid to winners\",\"winnerPoolHash\":\"Hash of selected winner pool\"}},\"payForMerkleTree2(uint8,(bytes32,(address,uint256)[16])[],uint64)\":{\"params\":{\"depth\":\"Tree depth (determines number of nodes paid)\",\"merklePaymentTimestamp\":\"Client-provided timestamp\",\"poolCommitments\":\"Array of packed pool commitments (2^ceil(depth/2))\"},\"returns\":{\"totalAmount\":\"Total tokens paid to winners\",\"winnerPoolHash\":\"Hash of selected winner pool\"}}},\"version\":1},\"userdoc\":{\"errors\":{\"PRBMath_SD59x18_Log_InputTooSmall(int256)\":[{\"notice\":\"Thrown when taking the logarithm of a number less than or equal to zero.\"}]},\"events\":{\"DataPaymentMade(bytes32,uint256,uint256)\":{\"notice\":\"Emitted when a data payment is made (legacy event)\"},\"MerklePaymentMade(bytes32,uint8,uint256,uint64)\":{\"notice\":\"Emitted when a Merkle batch payment is made\"}},\"kind\":\"user\",\"methods\":{\"ANT_PRICE()\":{\"notice\":\"Constant ANT price for local testnet (1e18) In production, this would be fetched from Chainlink\"},\"CANDIDATES_PER_POOL()\":{\"notice\":\"Number of candidates per pool (fixed)\"},\"DEFAULT_COST_UNIT()\":{\"notice\":\"Default cost unit assigned to each data type\"},\"MAX_MERKLE_DEPTH()\":{\"notice\":\"Maximum supported Merkle tree depth\"},\"PRECISION()\":{\"notice\":\"Precision for fixed-point calculations (1e18)\"},\"antToken()\":{\"notice\":\"ANT token contract\"},\"costUnitPerDataType(uint8)\":{\"notice\":\"Cost unit per data type\"},\"estimateMerkleTreeCost(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)\":{\"notice\":\"Estimate the cost of a Merkle tree payment without executing it This is a view function (0 gas) that runs the same pricing logic as payForMerkleTree but returns only the estimated cost without executing payment.\"},\"getPaymentInfo(bytes32)\":{\"notice\":\"Get payment info by winner pool hash\"},\"maxCostUnit()\":{\"notice\":\"Maximum cost unit for capacity calculation\"},\"minPrice()\":{\"notice\":\"Minimum price floor (3 to match regular payment baseline)\"},\"payForMerkleTree(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)\":{\"notice\":\"Pay for Merkle tree batch\"},\"payForMerkleTree2(uint8,(bytes32,(address,uint256)[16])[],uint64)\":{\"notice\":\"Pay for Merkle tree batch using packed calldata (v2) Uses packed data structures where each candidate's data type and total cost unit are encoded into a single uint256 for smaller calldata and lower gas costs.\"},\"payments(bytes32)\":{\"notice\":\"Payment info indexed by winner pool hash\"},\"scalingFactor()\":{\"notice\":\"Scaling factor for the pricing formula\"}},\"notice\":\"Merkle Batch Payment Vault Handles batch payments for Merkle tree storage where multiple data chunks are paid for in a single transaction. Uses a fair median pricing mechanism based on candidate node metrics.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/MerklePaymentVault.sol\":\"MerklePaymentVault\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@prb/math/=lib/prb-math/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\",\":prb-math/=lib/prb-math/src/\"],\"viaIR\":true},\"sources\":{\"contracts/IMerklePaymentVault.sol\":{\"keccak256\":\"0x9d0d1991f00745285ade2fd6b7b757c956f4f8cee214c3d02f2d74fb34680989\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://77743e5b3f810a507260a60081cf97a351ba1f75ec1b867f696ec49fff49de7e\",\"dweb:/ipfs/Qme2wE6PxvF1tVCyqmQTZ9YicyUPuaXYbroCm2nFA2C2xQ\"]},\"contracts/MerklePaymentVault.sol\":{\"keccak256\":\"0x583d867aa427f029080c9ddfb72a56361e86b58aada8ec5144a988ed87393781\",\"license\":\"GPL-3.0\",\"urls\":[\"bzz-raw://6b6a1ec8aa21eb8cf9e981067d0aeb594d909d07f437b83d2b61e2e507f3ac25\",\"dweb:/ipfs/QmW4gdwq2X8PeNZdmWuy9CMnQK48i7LUXaUn5GPyue6dH1\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303\",\"dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV\"]},\"lib/prb-math/src/Common.sol\":{\"keccak256\":\"0x8225a3898d2f11f585da1fb82234800e9717fa080dbe53d450fd429a3a632e99\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2dcbf39ca575f68f32f0d1c66391de94082424956e7585e849813966f8c0fc05\",\"dweb:/ipfs/QmYvk8vXFDUJHrmqbtutYXfoLomLBosYLyBzuPoYBxsQ42\"]},\"lib/prb-math/src/SD59x18.sol\":{\"keccak256\":\"0xff25fe75f8e77d4c839fbe69198d273039575fa72f31ce9725bcacb80db65461\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7ed7bc89a4efa33fc9392de8104c8af609fd3372b323d7824a6a3ae888bad3be\",\"dweb:/ipfs/QmfCbW4geVSm7uJW4KUoXrejR2SywcRJrXwMBzUBWYiXJa\"]},\"lib/prb-math/src/sd1x18/Casting.sol\":{\"keccak256\":\"0x5d365f655f01598926c5d4fe5cda277f2cc7736fe38f943c11a32009077ddd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://56b378bd6039819bc12e5f17dabd9492e1410b3281f9df496cf8210539101a11\",\"dweb:/ipfs/QmcMaE64ZWMg9cFhYxdTuG8nfzeDdNuTRHMMoFXi6tSZGu\"]},\"lib/prb-math/src/sd1x18/Constants.sol\":{\"keccak256\":\"0xc14cc32061863d83912f9616de86f3c34f1ac58614b7d504c6ce07ee8efdb8e8\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://22483b1282dda6a556b0232f008a5a3296bbfd76b1886e6b72bf351b7c554fab\",\"dweb:/ipfs/QmYX9cYkrFxBbhZNKsb6uUxtrc2chmAj7vuc7UKRPGMwos\"]},\"lib/prb-math/src/sd1x18/Errors.sol\":{\"keccak256\":\"0xc3c8b1ab3d19889c356c222a3a2186d45dfc1d3a17b9ad88159bb64ee457baa6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://84fbe57569246403f778330bd7723018dfcb5f0ec50d7b1d82cc983c94a54bca\",\"dweb:/ipfs/QmWssAAnovc2EVjt58rTnxraE9B1RMivwTvYCYgpnr6oSE\"]},\"lib/prb-math/src/sd1x18/ValueType.sol\":{\"keccak256\":\"0xaa9dc7b562faf45264390d80e2ea10c5295bb8a4f10d76261a3f9c04363734c0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c6ca36acd15f5cb47cf124ddec289f84e1011f2d29056159e4570435629a3353\",\"dweb:/ipfs/QmUKdiLmZpAkNCq2TKxrPbQPUhiRFXGfjGSnY1VeHVu4y6\"]},\"lib/prb-math/src/sd21x18/Casting.sol\":{\"keccak256\":\"0x4a16adddb9ab1f6939dd4567c77205015a11081cb840029b84bbb6fdaf78ee36\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5003b2f4cd2fc1413da36bc63107c6e83a88d29693e8f97b54f300fa78f9c6d2\",\"dweb:/ipfs/QmaNJn91NLrZmeeGqnFQV1FTrLVSW852zHyWTrWJ5pf1pd\"]},\"lib/prb-math/src/sd21x18/Constants.sol\":{\"keccak256\":\"0x501c2d5cfdea9450422182059c8df1cb6a859901a07bd59631c3fa24edcc79d4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4669a65001c92919671fd879d33ce0e5030b602a7ba4d36bd2308128d8d1f396\",\"dweb:/ipfs/QmUC3bJ3qdkCmLMw3WHBcEqvuC4tExT2LXzUhgu5KQ3vi3\"]},\"lib/prb-math/src/sd21x18/Errors.sol\":{\"keccak256\":\"0xc5422ee47eb139274e538e758fb40177a1ba22c2113ef3b3446102f0150bfe0a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a1981e052e9e86e1b0e4e55a057a7af4739aedd4ead2d60e3eaa40fb703594ee\",\"dweb:/ipfs/QmPK5qSujnyk1R8ues4RhDMy1tRKKyjQ31YJTviTKq7GML\"]},\"lib/prb-math/src/sd21x18/ValueType.sol\":{\"keccak256\":\"0x532bba888370bed393464412f4ef3462d654802e71c953ad02d078e3d2701092\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://922a4e8dd813602f72d165aa1dfdf2c29b971a2abe73bebca7cd81a32ee2c880\",\"dweb:/ipfs/QmTBAJnx1r3sZpbQAuTgQtsTtvjZbpDwhCJRzkhzUumbdf\"]},\"lib/prb-math/src/sd59x18/Casting.sol\":{\"keccak256\":\"0xdf70d8e70c6d6325f3f7eb028c484bc7189ef902f1d4b5b220af2e550bb5fc39\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0b15bcd36129c5cb163d57a117435afb171182018dd6d1e766a5f49cf1f4b63d\",\"dweb:/ipfs/QmbjzkMBH4FM2rdxGbx9LQ65wVERijNcu7R9C8dQmH3y4n\"]},\"lib/prb-math/src/sd59x18/Constants.sol\":{\"keccak256\":\"0x9bcb8dd6b3e886d140ad1c32747a4f6d29a492529ceb835be878ae837aa6cc3a\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c4045c633e3618e7e90a768d92415b2f20f781fe129b4f4e26fa88f7dbf9201f\",\"dweb:/ipfs/Qmbet95pizwPno82cJ383wJtgQRSQKESmhVZ1vDrgAu7Si\"]},\"lib/prb-math/src/sd59x18/Conversions.sol\":{\"keccak256\":\"0x1c2a91d7bec997ffa764046e504705be4382001835eea47f53909d5baedad481\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b11b48183878f5d909a45c6aa462f71a49b8fe0c89c51086f96a0e01b99fe2d5\",\"dweb:/ipfs/QmWj4UF6prH5RuGuxjrpuWBaRXkSt39zkUbiJkzngKigx6\"]},\"lib/prb-math/src/sd59x18/Errors.sol\":{\"keccak256\":\"0x0a79c28c85fc8a450b0801ff2e66114eac4ec565819f5d1d8738904658fe33e2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://9e0d4fd3c998019fb8555d9e26c03bec42a8513bdf4185aeac2da3a000abaebf\",\"dweb:/ipfs/QmahFJHXcX4RwPxaQbUf6LVZEk8NSpjCV3Eif7i9iqC6Mk\"]},\"lib/prb-math/src/sd59x18/Helpers.sol\":{\"keccak256\":\"0x208570f1657cf730cb6c3d81aa14030e0d45cf906cdedea5059369d7df4bb716\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://4c78ca900edafa9338d4e3649a55ab0c84f76468d8a22fb945ba6d01e70f8fed\",\"dweb:/ipfs/QmeP4hQYfNxcATd1FsasdD4ebyu2vrC9K1N68swxUJzzZD\"]},\"lib/prb-math/src/sd59x18/Math.sol\":{\"keccak256\":\"0xd8e8b51db9b3e2fa31a60f6b8ce4ea0112c3364442ede5992aa0aa7a2c925c84\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3c56913970e34ee7b142047b21f1856a511cbdc3473b7c50418a8490e19cd462\",\"dweb:/ipfs/QmfG1F9CBDjPYD7NXora9awFfdpvBMY9SCg5pMLCFRv9tD\"]},\"lib/prb-math/src/sd59x18/ValueType.sol\":{\"keccak256\":\"0x76597ba64d37d66e0178512bc9bbc1a031a7634c45e5d5c6e9da87f46952dc9d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://36148899ad874814e9292636fb974d2eec61f1bcc0875ec39cf444d70ba40919\",\"dweb:/ipfs/QmadUe4kH2FPcdxvhCKy8yiezCvPWor4VcPzqLYSAaGDDb\"]},\"lib/prb-math/src/ud21x18/Casting.sol\":{\"keccak256\":\"0x3821aa57604f6e5b7c9c5c5cc97a6d71116e673cf3fee5f76fcd42b4cefded65\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://a80399c6b38ab45cc10d0a6683d50340cd89d9a085b6d0dcfb81e7c4e5b3ce09\",\"dweb:/ipfs/QmWNW2YD2LMkqrpAtJYeeuHN329Rx7mvfmrjsCo1p6akTL\"]},\"lib/prb-math/src/ud21x18/Constants.sol\":{\"keccak256\":\"0x0997574a1ced6c43bde6d9c9175edc5ad64cbb920a0969a9db68eea543747601\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://c09f03345a6779b002b38ffc3954258accbb2b1d0d5506d42c3bd7f117304f60\",\"dweb:/ipfs/QmTeBXRCE7H2HpqKUNsZN7Nk3rdBnFmbAUFom3E1PJeGuV\"]},\"lib/prb-math/src/ud21x18/Errors.sol\":{\"keccak256\":\"0x35a1fb789b90f8c90865884d3023deb17fcca5c7146b5ddef823496d835a5415\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0af359d07ba25bdc90de7c05ed6216833932caa75d4a02fcfc51ceeaba5a4e80\",\"dweb:/ipfs/QmavBFw73Xfp1qJiN6P1gk2Dfr8ByWo3dyCPVgDHtko2gq\"]},\"lib/prb-math/src/ud21x18/ValueType.sol\":{\"keccak256\":\"0x24838b2b1da371b9259d8ee21534a9f0cb5796aba75a4efca2374627952bee25\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://897e6b79308651671c7f3c91a0069e778b47356c9ba3f86e238398ab7f2623af\",\"dweb:/ipfs/QmZbLw3tJVRZFQnV9jWQUmF43gna841adSG2TAiwDAifGU\"]},\"lib/prb-math/src/ud2x18/Casting.sol\":{\"keccak256\":\"0x0f3141ed054e7c29dbe1acb4b88b18eb05d60e998fba6b4e503a6799faa356d6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b1e2468fc4c458082aaf4aa2e35af9ba3702f207e3c8533dd1e7da11ad605eae\",\"dweb:/ipfs/QmSm7iRH1eo4cJCwcAiiXWRH9Hn1urSS4tMdbaFbFGuTyL\"]},\"lib/prb-math/src/ud2x18/Constants.sol\":{\"keccak256\":\"0x29b0e050c865899e1fb9022b460a7829cdee248c44c4299f068ba80695eec3fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://cbaef16b662fac235349bcf97bc980dd0cba15d4e6230caae61224cdac8ea6d9\",\"dweb:/ipfs/QmZQa5XBhi7k3yhtCd8wVpnwW8htfU4sjXxWhxRypMBYkC\"]},\"lib/prb-math/src/ud2x18/Errors.sol\":{\"keccak256\":\"0x3b27e2a57438cd30d9c130f84aace24d547e5ed58e8689691d7d92ad2db38ddd\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://841cf9fb45443899c6b659300bbf503c3fd2c1a1e83b7f0e28620eed457f7437\",\"dweb:/ipfs/QmUqg8WscP5yQPw3UMUCWaB9RLU6nryGzseuyhAjNnDc1i\"]},\"lib/prb-math/src/ud2x18/ValueType.sol\":{\"keccak256\":\"0x975a2e69b48f34a4c0bd80e8a5609ac67b7264c91992c0944f9ebe7b9e3fc9d0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://65d012521c475295d7e70b7d526fcc0911d0f238ea938719d77251bba00c9b41\",\"dweb:/ipfs/QmexEvTQCCBPYRWAYnomZX5M7C2EkXQRAXqEYMNUZfazCs\"]},\"lib/prb-math/src/ud60x18/Casting.sol\":{\"keccak256\":\"0x0803318ddc98b4ba8fbfe70e5ee08d78387fe6ae00982b9960518085a751d7b6\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2e68a2f780b2e33fa5416eb60f9daa81f014c2591119f4b67bed1217d5530780\",\"dweb:/ipfs/QmZe7JTWvbfKqMnu4sxUwWCtLcCay9hH71VZUpoFCdENcr\"]},\"lib/prb-math/src/ud60x18/Constants.sol\":{\"keccak256\":\"0x2b80d26153d3fdcfb3a9ca772d9309d31ed1275f5b8b54c3ffb54d3652b37d90\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7e3a6673a156f635db94dc176baaa7274db8f9bec4461cd1152596253550ee3b\",\"dweb:/ipfs/Qmc9zT4kNSbMYaXcnbxNVqmb3P3m46ieaQxkwxqLwsvRA5\"]},\"lib/prb-math/src/ud60x18/Errors.sol\":{\"keccak256\":\"0xbab6b0e303d32f3a9d9e2fe881f0392b8c59a73051a4d34f21a403b3961b3044\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://86a019bcf2510d0691287329dc057479cc0abc48a5e15f245e7f15c03052d2c8\",\"dweb:/ipfs/QmeXe5pbpDHvN5DZ8puXmH2RJ25zDHj55wpiStWtNQPvq6\"]},\"lib/prb-math/src/ud60x18/Helpers.sol\":{\"keccak256\":\"0xf5faff881391d2c060029499a666cc5f0bea90a213150bb476fae8f02a5df268\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://76105fa22bb1b5f1fa99abf9c4fbc9577a02c7bc204f271754c407f0d75489f5\",\"dweb:/ipfs/QmVNGZSTniDuZus5DdbFubqJXCLtTaZit7YPm4ntjr5Lgr\"]},\"lib/prb-math/src/ud60x18/Math.sol\":{\"keccak256\":\"0xc4e51dfd9af62938e277e90fa724099f239d33727a35909ed48c292a76faf2fc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d731537cbc50b852c21d28625aeb2c329729afc23a7b86ff9d8ee5878f47e9d6\",\"dweb:/ipfs/QmS7Cj4pAdPZcTp7RqYXyxBc9EYX92CT8icfkNigktUsLr\"]},\"lib/prb-math/src/ud60x18/ValueType.sol\":{\"keccak256\":\"0x1b200baf25d01a8b91b97b42114248636f742b5b7028487ef4daef6621e378a3\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://b5708ed017206bda2197654e80bea9f37b3a9993434bb066c903c2865e028f47\",\"dweb:/ipfs/QmTyotZk2J5YvWkNvB2qhXBMgRGWW2UgPqR4JPocrXSr8n\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_antToken","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AntTokenNull"},{"inputs":[],"type":"error","name":"BatchLimitExceeded"},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint8","name":"max","type":"uint8"}],"type":"error","name":"DepthTooLarge"},{"inputs":[],"type":"error","name":"GracePeriodNotOver"},{"inputs":[],"type":"error","name":"InvalidAmount"},{"inputs":[],"type":"error","name":"InvalidChainlinkPrice"},{"inputs":[],"type":"error","name":"InvalidInputLength"},{"inputs":[],"type":"error","name":"InvalidQuoteHash"},{"inputs":[],"type":"error","name":"InvalidRecipientsCount"},{"inputs":[],"type":"error","name":"InvalidRoot"},{"inputs":[],"type":"error","name":"InvalidTreeDepth"},{"inputs":[{"internalType":"SD59x18","name":"x","type":"int256"}],"type":"error","name":"PRBMath_SD59x18_Log_InputTooSmall"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"type":"error","name":"PaymentAlreadyExists"},{"inputs":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"}],"type":"error","name":"PaymentNotFound"},{"inputs":[],"type":"error","name":"PriceFeedNull"},{"inputs":[],"type":"error","name":"RootAlreadyPaid"},{"inputs":[],"type":"error","name":"SequencerDown"},{"inputs":[{"internalType":"uint256","name":"poolIdx","type":"uint256"},{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"got","type":"uint256"}],"type":"error","name":"WrongCandidateCount"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"got","type":"uint256"}],"type":"error","name":"WrongPoolCount"},{"inputs":[{"internalType":"bytes32","name":"root","type":"bytes32","indexed":true},{"internalType":"uint256","name":"treeDepth","type":"uint256","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":true}],"type":"event","name":"DataPaymentMade","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32","indexed":true},{"internalType":"uint8","name":"depth","type":"uint8","indexed":false},{"internalType":"uint256","name":"totalAmount","type":"uint256","indexed":false},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64","indexed":false}],"type":"event","name":"MerklePaymentMade","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"ANT_PRICE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"CANDIDATES_PER_POOL","outputs":[{"internalType":"uint8","name":"","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"DEFAULT_COST_UNIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MAX_MERKLE_DEPTH","outputs":[{"internalType":"uint8","name":"","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"antToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}]},{"inputs":[{"internalType":"enum IMerklePaymentVault.DataType","name":"","type":"uint8"}],"stateMutability":"view","type":"function","name":"costUnitPerDataType","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"struct IMerklePaymentVault.PoolCommitment[]","name":"poolCommitments","type":"tuple[]","components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"struct IMerklePaymentVault.CandidateNode[16]","name":"candidates","type":"tuple[16]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"struct IMerklePaymentVault.QuotingMetrics","name":"metrics","type":"tuple","components":[{"internalType":"enum IMerklePaymentVault.DataType","name":"dataType","type":"uint8"},{"internalType":"uint256","name":"closeRecordsStored","type":"uint256"},{"internalType":"struct IMerklePaymentVault.Record[]","name":"recordsPerType","type":"tuple[]","components":[{"internalType":"enum IMerklePaymentVault.DataType","name":"dataType","type":"uint8"},{"internalType":"uint256","name":"records","type":"uint256"}]}]}]}]},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}],"stateMutability":"view","type":"function","name":"estimateMerkleTreeCost","outputs":[{"internalType":"uint256","name":"totalAmount","type":"uint256"}]},{"inputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getPaymentInfo","outputs":[{"internalType":"struct IMerklePaymentVault.PaymentInfo","name":"info","type":"tuple","components":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"},{"internalType":"struct IMerklePaymentVault.PaidNode[]","name":"paidNodeAddresses","type":"tuple[]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint8","name":"poolIndex","type":"uint8"}]}]}]},{"inputs":[],"stateMutability":"view","type":"function","name":"maxCostUnit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"minPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"struct IMerklePaymentVault.PoolCommitment[]","name":"poolCommitments","type":"tuple[]","components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"struct IMerklePaymentVault.CandidateNode[16]","name":"candidates","type":"tuple[16]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"struct IMerklePaymentVault.QuotingMetrics","name":"metrics","type":"tuple","components":[{"internalType":"enum IMerklePaymentVault.DataType","name":"dataType","type":"uint8"},{"internalType":"uint256","name":"closeRecordsStored","type":"uint256"},{"internalType":"struct IMerklePaymentVault.Record[]","name":"recordsPerType","type":"tuple[]","components":[{"internalType":"enum IMerklePaymentVault.DataType","name":"dataType","type":"uint8"},{"internalType":"uint256","name":"records","type":"uint256"}]}]}]}]},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}],"stateMutability":"nonpayable","type":"function","name":"payForMerkleTree","outputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32"},{"internalType":"uint256","name":"totalAmount","type":"uint256"}]},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"struct IMerklePaymentVault.PoolCommitmentPacked[]","name":"poolCommitments","type":"tuple[]","components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"struct IMerklePaymentVault.CandidateNodePacked[16]","name":"candidates","type":"tuple[16]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint256","name":"dataTypeAndTotalCostUnit","type":"uint256"}]}]},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}],"stateMutability":"nonpayable","type":"function","name":"payForMerkleTree2","outputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32"},{"internalType":"uint256","name":"totalAmount","type":"uint256"}]},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"payments","outputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"scalingFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]}],"devdoc":{"kind":"dev","methods":{"estimateMerkleTreeCost(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":{"params":{"depth":"Tree depth (determines number of nodes paid)","merklePaymentTimestamp":"Client-provided timestamp","poolCommitments":"Array of pool commitments (2^ceil(depth/2))"},"returns":{"totalAmount":"Estimated total tokens that would be paid"}},"getPaymentInfo(bytes32)":{"params":{"winnerPoolHash":"Hash returned from payForMerkleTree"},"returns":{"info":"Payment information stored on-chain"}},"payForMerkleTree(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":{"params":{"depth":"Tree depth (determines number of nodes paid)","merklePaymentTimestamp":"Client-provided timestamp","poolCommitments":"Array of pool commitments (2^ceil(depth/2))"},"returns":{"totalAmount":"Total tokens paid to winners","winnerPoolHash":"Hash of selected winner pool"}},"payForMerkleTree2(uint8,(bytes32,(address,uint256)[16])[],uint64)":{"params":{"depth":"Tree depth (determines number of nodes paid)","merklePaymentTimestamp":"Client-provided timestamp","poolCommitments":"Array of packed pool commitments (2^ceil(depth/2))"},"returns":{"totalAmount":"Total tokens paid to winners","winnerPoolHash":"Hash of selected winner pool"}}},"version":1},"userdoc":{"kind":"user","methods":{"ANT_PRICE()":{"notice":"Constant ANT price for local testnet (1e18) In production, this would be fetched from Chainlink"},"CANDIDATES_PER_POOL()":{"notice":"Number of candidates per pool (fixed)"},"DEFAULT_COST_UNIT()":{"notice":"Default cost unit assigned to each data type"},"MAX_MERKLE_DEPTH()":{"notice":"Maximum supported Merkle tree depth"},"PRECISION()":{"notice":"Precision for fixed-point calculations (1e18)"},"antToken()":{"notice":"ANT token contract"},"costUnitPerDataType(uint8)":{"notice":"Cost unit per data type"},"estimateMerkleTreeCost(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":{"notice":"Estimate the cost of a Merkle tree payment without executing it This is a view function (0 gas) that runs the same pricing logic as payForMerkleTree but returns only the estimated cost without executing payment."},"getPaymentInfo(bytes32)":{"notice":"Get payment info by winner pool hash"},"maxCostUnit()":{"notice":"Maximum cost unit for capacity calculation"},"minPrice()":{"notice":"Minimum price floor (3 to match regular payment baseline)"},"payForMerkleTree(uint8,(bytes32,(address,(uint8,uint256,(uint8,uint256)[]))[16])[],uint64)":{"notice":"Pay for Merkle tree batch"},"payForMerkleTree2(uint8,(bytes32,(address,uint256)[16])[],uint64)":{"notice":"Pay for Merkle tree batch using packed calldata (v2) Uses packed data structures where each candidate's data type and total cost unit are encoded into a single uint256 for smaller calldata and lower gas costs."},"payments(bytes32)":{"notice":"Payment info indexed by winner pool hash"},"scalingFactor()":{"notice":"Scaling factor for the pricing formula"}},"version":1}},"settings":{"remappings":["@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@prb/math/=lib/prb-math/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/","prb-math/=lib/prb-math/src/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"contracts/MerklePaymentVault.sol":"MerklePaymentVault"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/IMerklePaymentVault.sol":{"keccak256":"0x9d0d1991f00745285ade2fd6b7b757c956f4f8cee214c3d02f2d74fb34680989","urls":["bzz-raw://77743e5b3f810a507260a60081cf97a351ba1f75ec1b867f696ec49fff49de7e","dweb:/ipfs/Qme2wE6PxvF1tVCyqmQTZ9YicyUPuaXYbroCm2nFA2C2xQ"],"license":"GPL-3.0"},"contracts/MerklePaymentVault.sol":{"keccak256":"0x583d867aa427f029080c9ddfb72a56361e86b58aada8ec5144a988ed87393781","urls":["bzz-raw://6b6a1ec8aa21eb8cf9e981067d0aeb594d909d07f437b83d2b61e2e507f3ac25","dweb:/ipfs/QmW4gdwq2X8PeNZdmWuy9CMnQK48i7LUXaUn5GPyue6dH1"],"license":"GPL-3.0"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2","urls":["bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303","dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV"],"license":"MIT"},"lib/prb-math/src/Common.sol":{"keccak256":"0x8225a3898d2f11f585da1fb82234800e9717fa080dbe53d450fd429a3a632e99","urls":["bzz-raw://2dcbf39ca575f68f32f0d1c66391de94082424956e7585e849813966f8c0fc05","dweb:/ipfs/QmYvk8vXFDUJHrmqbtutYXfoLomLBosYLyBzuPoYBxsQ42"],"license":"MIT"},"lib/prb-math/src/SD59x18.sol":{"keccak256":"0xff25fe75f8e77d4c839fbe69198d273039575fa72f31ce9725bcacb80db65461","urls":["bzz-raw://7ed7bc89a4efa33fc9392de8104c8af609fd3372b323d7824a6a3ae888bad3be","dweb:/ipfs/QmfCbW4geVSm7uJW4KUoXrejR2SywcRJrXwMBzUBWYiXJa"],"license":"MIT"},"lib/prb-math/src/sd1x18/Casting.sol":{"keccak256":"0x5d365f655f01598926c5d4fe5cda277f2cc7736fe38f943c11a32009077ddd5c","urls":["bzz-raw://56b378bd6039819bc12e5f17dabd9492e1410b3281f9df496cf8210539101a11","dweb:/ipfs/QmcMaE64ZWMg9cFhYxdTuG8nfzeDdNuTRHMMoFXi6tSZGu"],"license":"MIT"},"lib/prb-math/src/sd1x18/Constants.sol":{"keccak256":"0xc14cc32061863d83912f9616de86f3c34f1ac58614b7d504c6ce07ee8efdb8e8","urls":["bzz-raw://22483b1282dda6a556b0232f008a5a3296bbfd76b1886e6b72bf351b7c554fab","dweb:/ipfs/QmYX9cYkrFxBbhZNKsb6uUxtrc2chmAj7vuc7UKRPGMwos"],"license":"MIT"},"lib/prb-math/src/sd1x18/Errors.sol":{"keccak256":"0xc3c8b1ab3d19889c356c222a3a2186d45dfc1d3a17b9ad88159bb64ee457baa6","urls":["bzz-raw://84fbe57569246403f778330bd7723018dfcb5f0ec50d7b1d82cc983c94a54bca","dweb:/ipfs/QmWssAAnovc2EVjt58rTnxraE9B1RMivwTvYCYgpnr6oSE"],"license":"MIT"},"lib/prb-math/src/sd1x18/ValueType.sol":{"keccak256":"0xaa9dc7b562faf45264390d80e2ea10c5295bb8a4f10d76261a3f9c04363734c0","urls":["bzz-raw://c6ca36acd15f5cb47cf124ddec289f84e1011f2d29056159e4570435629a3353","dweb:/ipfs/QmUKdiLmZpAkNCq2TKxrPbQPUhiRFXGfjGSnY1VeHVu4y6"],"license":"MIT"},"lib/prb-math/src/sd21x18/Casting.sol":{"keccak256":"0x4a16adddb9ab1f6939dd4567c77205015a11081cb840029b84bbb6fdaf78ee36","urls":["bzz-raw://5003b2f4cd2fc1413da36bc63107c6e83a88d29693e8f97b54f300fa78f9c6d2","dweb:/ipfs/QmaNJn91NLrZmeeGqnFQV1FTrLVSW852zHyWTrWJ5pf1pd"],"license":"MIT"},"lib/prb-math/src/sd21x18/Constants.sol":{"keccak256":"0x501c2d5cfdea9450422182059c8df1cb6a859901a07bd59631c3fa24edcc79d4","urls":["bzz-raw://4669a65001c92919671fd879d33ce0e5030b602a7ba4d36bd2308128d8d1f396","dweb:/ipfs/QmUC3bJ3qdkCmLMw3WHBcEqvuC4tExT2LXzUhgu5KQ3vi3"],"license":"MIT"},"lib/prb-math/src/sd21x18/Errors.sol":{"keccak256":"0xc5422ee47eb139274e538e758fb40177a1ba22c2113ef3b3446102f0150bfe0a","urls":["bzz-raw://a1981e052e9e86e1b0e4e55a057a7af4739aedd4ead2d60e3eaa40fb703594ee","dweb:/ipfs/QmPK5qSujnyk1R8ues4RhDMy1tRKKyjQ31YJTviTKq7GML"],"license":"MIT"},"lib/prb-math/src/sd21x18/ValueType.sol":{"keccak256":"0x532bba888370bed393464412f4ef3462d654802e71c953ad02d078e3d2701092","urls":["bzz-raw://922a4e8dd813602f72d165aa1dfdf2c29b971a2abe73bebca7cd81a32ee2c880","dweb:/ipfs/QmTBAJnx1r3sZpbQAuTgQtsTtvjZbpDwhCJRzkhzUumbdf"],"license":"MIT"},"lib/prb-math/src/sd59x18/Casting.sol":{"keccak256":"0xdf70d8e70c6d6325f3f7eb028c484bc7189ef902f1d4b5b220af2e550bb5fc39","urls":["bzz-raw://0b15bcd36129c5cb163d57a117435afb171182018dd6d1e766a5f49cf1f4b63d","dweb:/ipfs/QmbjzkMBH4FM2rdxGbx9LQ65wVERijNcu7R9C8dQmH3y4n"],"license":"MIT"},"lib/prb-math/src/sd59x18/Constants.sol":{"keccak256":"0x9bcb8dd6b3e886d140ad1c32747a4f6d29a492529ceb835be878ae837aa6cc3a","urls":["bzz-raw://c4045c633e3618e7e90a768d92415b2f20f781fe129b4f4e26fa88f7dbf9201f","dweb:/ipfs/Qmbet95pizwPno82cJ383wJtgQRSQKESmhVZ1vDrgAu7Si"],"license":"MIT"},"lib/prb-math/src/sd59x18/Conversions.sol":{"keccak256":"0x1c2a91d7bec997ffa764046e504705be4382001835eea47f53909d5baedad481","urls":["bzz-raw://b11b48183878f5d909a45c6aa462f71a49b8fe0c89c51086f96a0e01b99fe2d5","dweb:/ipfs/QmWj4UF6prH5RuGuxjrpuWBaRXkSt39zkUbiJkzngKigx6"],"license":"MIT"},"lib/prb-math/src/sd59x18/Errors.sol":{"keccak256":"0x0a79c28c85fc8a450b0801ff2e66114eac4ec565819f5d1d8738904658fe33e2","urls":["bzz-raw://9e0d4fd3c998019fb8555d9e26c03bec42a8513bdf4185aeac2da3a000abaebf","dweb:/ipfs/QmahFJHXcX4RwPxaQbUf6LVZEk8NSpjCV3Eif7i9iqC6Mk"],"license":"MIT"},"lib/prb-math/src/sd59x18/Helpers.sol":{"keccak256":"0x208570f1657cf730cb6c3d81aa14030e0d45cf906cdedea5059369d7df4bb716","urls":["bzz-raw://4c78ca900edafa9338d4e3649a55ab0c84f76468d8a22fb945ba6d01e70f8fed","dweb:/ipfs/QmeP4hQYfNxcATd1FsasdD4ebyu2vrC9K1N68swxUJzzZD"],"license":"MIT"},"lib/prb-math/src/sd59x18/Math.sol":{"keccak256":"0xd8e8b51db9b3e2fa31a60f6b8ce4ea0112c3364442ede5992aa0aa7a2c925c84","urls":["bzz-raw://3c56913970e34ee7b142047b21f1856a511cbdc3473b7c50418a8490e19cd462","dweb:/ipfs/QmfG1F9CBDjPYD7NXora9awFfdpvBMY9SCg5pMLCFRv9tD"],"license":"MIT"},"lib/prb-math/src/sd59x18/ValueType.sol":{"keccak256":"0x76597ba64d37d66e0178512bc9bbc1a031a7634c45e5d5c6e9da87f46952dc9d","urls":["bzz-raw://36148899ad874814e9292636fb974d2eec61f1bcc0875ec39cf444d70ba40919","dweb:/ipfs/QmadUe4kH2FPcdxvhCKy8yiezCvPWor4VcPzqLYSAaGDDb"],"license":"MIT"},"lib/prb-math/src/ud21x18/Casting.sol":{"keccak256":"0x3821aa57604f6e5b7c9c5c5cc97a6d71116e673cf3fee5f76fcd42b4cefded65","urls":["bzz-raw://a80399c6b38ab45cc10d0a6683d50340cd89d9a085b6d0dcfb81e7c4e5b3ce09","dweb:/ipfs/QmWNW2YD2LMkqrpAtJYeeuHN329Rx7mvfmrjsCo1p6akTL"],"license":"MIT"},"lib/prb-math/src/ud21x18/Constants.sol":{"keccak256":"0x0997574a1ced6c43bde6d9c9175edc5ad64cbb920a0969a9db68eea543747601","urls":["bzz-raw://c09f03345a6779b002b38ffc3954258accbb2b1d0d5506d42c3bd7f117304f60","dweb:/ipfs/QmTeBXRCE7H2HpqKUNsZN7Nk3rdBnFmbAUFom3E1PJeGuV"],"license":"MIT"},"lib/prb-math/src/ud21x18/Errors.sol":{"keccak256":"0x35a1fb789b90f8c90865884d3023deb17fcca5c7146b5ddef823496d835a5415","urls":["bzz-raw://0af359d07ba25bdc90de7c05ed6216833932caa75d4a02fcfc51ceeaba5a4e80","dweb:/ipfs/QmavBFw73Xfp1qJiN6P1gk2Dfr8ByWo3dyCPVgDHtko2gq"],"license":"MIT"},"lib/prb-math/src/ud21x18/ValueType.sol":{"keccak256":"0x24838b2b1da371b9259d8ee21534a9f0cb5796aba75a4efca2374627952bee25","urls":["bzz-raw://897e6b79308651671c7f3c91a0069e778b47356c9ba3f86e238398ab7f2623af","dweb:/ipfs/QmZbLw3tJVRZFQnV9jWQUmF43gna841adSG2TAiwDAifGU"],"license":"MIT"},"lib/prb-math/src/ud2x18/Casting.sol":{"keccak256":"0x0f3141ed054e7c29dbe1acb4b88b18eb05d60e998fba6b4e503a6799faa356d6","urls":["bzz-raw://b1e2468fc4c458082aaf4aa2e35af9ba3702f207e3c8533dd1e7da11ad605eae","dweb:/ipfs/QmSm7iRH1eo4cJCwcAiiXWRH9Hn1urSS4tMdbaFbFGuTyL"],"license":"MIT"},"lib/prb-math/src/ud2x18/Constants.sol":{"keccak256":"0x29b0e050c865899e1fb9022b460a7829cdee248c44c4299f068ba80695eec3fc","urls":["bzz-raw://cbaef16b662fac235349bcf97bc980dd0cba15d4e6230caae61224cdac8ea6d9","dweb:/ipfs/QmZQa5XBhi7k3yhtCd8wVpnwW8htfU4sjXxWhxRypMBYkC"],"license":"MIT"},"lib/prb-math/src/ud2x18/Errors.sol":{"keccak256":"0x3b27e2a57438cd30d9c130f84aace24d547e5ed58e8689691d7d92ad2db38ddd","urls":["bzz-raw://841cf9fb45443899c6b659300bbf503c3fd2c1a1e83b7f0e28620eed457f7437","dweb:/ipfs/QmUqg8WscP5yQPw3UMUCWaB9RLU6nryGzseuyhAjNnDc1i"],"license":"MIT"},"lib/prb-math/src/ud2x18/ValueType.sol":{"keccak256":"0x975a2e69b48f34a4c0bd80e8a5609ac67b7264c91992c0944f9ebe7b9e3fc9d0","urls":["bzz-raw://65d012521c475295d7e70b7d526fcc0911d0f238ea938719d77251bba00c9b41","dweb:/ipfs/QmexEvTQCCBPYRWAYnomZX5M7C2EkXQRAXqEYMNUZfazCs"],"license":"MIT"},"lib/prb-math/src/ud60x18/Casting.sol":{"keccak256":"0x0803318ddc98b4ba8fbfe70e5ee08d78387fe6ae00982b9960518085a751d7b6","urls":["bzz-raw://2e68a2f780b2e33fa5416eb60f9daa81f014c2591119f4b67bed1217d5530780","dweb:/ipfs/QmZe7JTWvbfKqMnu4sxUwWCtLcCay9hH71VZUpoFCdENcr"],"license":"MIT"},"lib/prb-math/src/ud60x18/Constants.sol":{"keccak256":"0x2b80d26153d3fdcfb3a9ca772d9309d31ed1275f5b8b54c3ffb54d3652b37d90","urls":["bzz-raw://7e3a6673a156f635db94dc176baaa7274db8f9bec4461cd1152596253550ee3b","dweb:/ipfs/Qmc9zT4kNSbMYaXcnbxNVqmb3P3m46ieaQxkwxqLwsvRA5"],"license":"MIT"},"lib/prb-math/src/ud60x18/Errors.sol":{"keccak256":"0xbab6b0e303d32f3a9d9e2fe881f0392b8c59a73051a4d34f21a403b3961b3044","urls":["bzz-raw://86a019bcf2510d0691287329dc057479cc0abc48a5e15f245e7f15c03052d2c8","dweb:/ipfs/QmeXe5pbpDHvN5DZ8puXmH2RJ25zDHj55wpiStWtNQPvq6"],"license":"MIT"},"lib/prb-math/src/ud60x18/Helpers.sol":{"keccak256":"0xf5faff881391d2c060029499a666cc5f0bea90a213150bb476fae8f02a5df268","urls":["bzz-raw://76105fa22bb1b5f1fa99abf9c4fbc9577a02c7bc204f271754c407f0d75489f5","dweb:/ipfs/QmVNGZSTniDuZus5DdbFubqJXCLtTaZit7YPm4ntjr5Lgr"],"license":"MIT"},"lib/prb-math/src/ud60x18/Math.sol":{"keccak256":"0xc4e51dfd9af62938e277e90fa724099f239d33727a35909ed48c292a76faf2fc","urls":["bzz-raw://d731537cbc50b852c21d28625aeb2c329729afc23a7b86ff9d8ee5878f47e9d6","dweb:/ipfs/QmS7Cj4pAdPZcTp7RqYXyxBc9EYX92CT8icfkNigktUsLr"],"license":"MIT"},"lib/prb-math/src/ud60x18/ValueType.sol":{"keccak256":"0x1b200baf25d01a8b91b97b42114248636f742b5b7028487ef4daef6621e378a3","urls":["bzz-raw://b5708ed017206bda2197654e80bea9f37b3a9993434bb066c903c2865e028f47","dweb:/ipfs/QmTyotZk2J5YvWkNvB2qhXBMgRGWW2UgPqR4JPocrXSr8n"],"license":"MIT"}},"version":1},"id":1} \ No newline at end of file diff --git a/artifacts/PaymentVaultNoProxyV6.json b/artifacts/PaymentVaultNoProxyV6.json deleted file mode 100644 index 44697ad..0000000 --- a/artifacts/PaymentVaultNoProxyV6.json +++ /dev/null @@ -1,383 +0,0 @@ -{ - "_format": "hh-sol-artifact-1", - "contractName": "PaymentVault", - "sourceName": "contracts/PaymentVaultNoProxyV6.sol", - "abi": [ - { - "inputs": [ - { - "internalType": "contract IERC20", - "name": "_antToken", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "target", - "type": "address" - } - ], - "name": "AddressEmptyCode", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "AddressInsufficientBalance", - "type": "error" - }, - { - "inputs": [], - "name": "AntTokenNull", - "type": "error" - }, - { - "inputs": [], - "name": "BatchLimitExceeded", - "type": "error" - }, - { - "inputs": [], - "name": "FailedInnerCall", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInputLength", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "token", - "type": "address" - } - ], - "name": "SafeERC20FailedOperation", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "name": "DataPaymentMade", - "type": "event" - }, - { - "inputs": [], - "name": "antToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "batchLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "name": "completedPayments", - "outputs": [ - { - "internalType": "bytes16", - "name": "rewardsAddress", - "type": "bytes16" - }, - { - "internalType": "uint128", - "name": "amount", - "type": "uint128" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "dataSize", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "closeRecordsStored", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "records", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.Record[]", - "name": "recordsPerType", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "maxRecords", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "receivedPaymentCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liveTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkDensity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkSize", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.QuotingMetrics[]", - "name": "_metrics", - "type": "tuple[]" - } - ], - "name": "getQuote", - "outputs": [ - { - "internalType": "uint256[]", - "name": "prices", - "type": "uint256[]" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "internalType": "struct IPaymentVault.DataPayment[]", - "name": "_payments", - "type": "tuple[]" - } - ], - "name": "payForQuotes", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "requiredPaymentVerificationLength", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "components": [ - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "dataSize", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "closeRecordsStored", - "type": "uint256" - }, - { - "components": [ - { - "internalType": "enum IPaymentVault.DataType", - "name": "dataType", - "type": "uint8" - }, - { - "internalType": "uint256", - "name": "records", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.Record[]", - "name": "recordsPerType", - "type": "tuple[]" - }, - { - "internalType": "uint256", - "name": "maxRecords", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "receivedPaymentCount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "liveTime", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkDensity", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "networkSize", - "type": "uint256" - } - ], - "internalType": "struct IPaymentVault.QuotingMetrics", - "name": "metrics", - "type": "tuple" - }, - { - "internalType": "address", - "name": "rewardsAddress", - "type": "address" - }, - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - } - ], - "internalType": "struct IPaymentVault.PaymentVerification[]", - "name": "_payments", - "type": "tuple[]" - } - ], - "name": "verifyPayment", - "outputs": [ - { - "components": [ - { - "internalType": "bytes32", - "name": "quoteHash", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "amountPaid", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isValid", - "type": "bool" - } - ], - "internalType": "struct IPaymentVault.PaymentVerificationResult[3]", - "name": "verificationResults", - "type": "tuple[3]" - } - ], - "stateMutability": "view", - "type": "function" - } - ], - "bytecode": "0x6080604052348015600f57600080fd5b506040516111c03803806111c0833981016040819052602c916076565b6001600160a01b038116605257604051632d06160b60e21b815260040160405180910390fd5b600080546001600160a01b0319166001600160a01b039290921691909117905560a4565b600060208284031215608757600080fd5b81516001600160a01b0381168114609d57600080fd5b9392505050565b61110d806100b36000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c80634ec42e8e1161005b5780634ec42e8e146100e2578063b6c2141b1461010d578063c7170bb614610122578063fe3c806e1461012a57600080fd5b806338f03e75146100825780633ffbb252146100ab578063474740b1146100cb575b600080fd5b610095610090366004610c06565b610188565b6040516100a29190610c48565b60405180910390f35b6100be6100b9366004610c06565b610538565b6040516100a29190610c98565b6100d461020081565b6040519081526020016100a2565b6000546100f5906001600160a01b031681565b6040516001600160a01b0390911681526020016100a2565b61012061011b366004610cdb565b6105c1565b005b6100d4600581565b610160610138366004610d52565b600160205260009081526040902054608081901b90600160801b90046001600160801b031682565b604080516001600160801b031990931683526001600160801b039091166020830152016100a2565b610190610adf565b600582146101b157604051637db491eb60e01b815260040160405180910390fd5b60006101bd8484610708565b90506000805b60038110156101e3576001828111156101da578092505b506001016101c3565b5060008060005b60038110156102615760006001600087846003811061020b5761020b610d6b565b602002015160400151815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b03161115610259578261025281610d97565b9350508091505b6001016101ea565b506000826001036102f85760006001600087856003811061028457610284610d6b565b602090810291909101516040908101518352828201939093529082016000208251808401909352546001600160801b0319608082901b168352600160801b90046001600160801b03169082015290506102de856003610db0565b81602001516001600160801b0316106102f657600191505b505b60005b600381101561052c5760006001600088846003811061031c5761031c610d6b565b6020908102919091015160409081015183528282019390935290820160009081208351808501909452546001600160801b0319608082901b168452600160801b90046001600160801b03169183019190915290915060019080851561042f5760208401516001600160801b03161561042457610399896003610db0565b84602001516001600160801b0316101591506103dc8a86600381106103c0576103c0610d6b565b6020020151602001516001600160801b031960609190911b1690565b84516001600160801b0319908116911614801561041d575060008a866003811061040857610408610d6b565b6020020151602001516001600160a01b031614155b90506104b3565b5060019050806104b3565b60208401516001600160801b03161580159061045857508284602001516001600160801b031610155b915061046f8a86600381106103c0576103c0610d6b565b84516001600160801b031990811691161480156104b0575060008a866003811061049b5761049b610d6b565b6020020151602001516001600160a01b031614155b90505b600060405180606001604052808c88600381106104d2576104d2610d6b565b602002015160400151815260200186602001516001600160801b031681526020018480156104fd5750835b151590529050808c876003811061051657610516610d6b565b60200201525050600190930192506102fb915050565b50505050505092915050565b606060008267ffffffffffffffff81111561055557610555610dc7565b60405190808252806020026020018201604052801561057e578160200160208202803683370190505b50905060005b838110156105b957600060019050808383815181106105a5576105a5610d6b565b602090810291909101015250600101610584565b509392505050565b806102008111156105e557604051630d67f41160e21b815260040160405180910390fd5b60005b81811015610702573684848381811061060357610603610d6b565b60600291909101915061063790503361061f6020840184610df9565b6000546001600160a01b0316919060208501356108ea565b60408051808201909152806106626106526020850185610df9565b60601b6001600160801b03191690565b6001600160801b0319168152602083810180356001600160801b03818116948401949094526040808701356000818152600186529190912086519690940151909416600160801b0260809590951c949094179091559091906106c49084610df9565b6001600160a01b03167ff998960b1c6f0e0e89b7bbe6b6fbf3e03e6f08eee5b8430877d8adb8e149d58060405160405180910390a4506001016105e8565b50505050565b610710610b1e565b60005b828110156108e25760006001600086868581811061073357610733610d6b565b90506020028101906107459190610e14565b60409081013582526020808301939093529081016000908120825180840184529054608081901b6001600160801b03191682526001600160801b03600160801b9182900481168387019081528951860151855260019096529390922054935190945092048116911611156107fd576020830180516040850152835190528484838181106107d4576107d4610d6b565b90506020028101906107e69190610e14565b6107ef90610f94565b8360005b60200201526108d9565b6020808401516040908101516000908152600183522054908201516001600160801b03600160801b9092048216911610610870576020830151604084015284848381811061084d5761084d610d6b565b905060200281019061085f9190610e14565b61086890610f94565b8360016107f3565b60408084015181015160009081526001602090815291902054908201516001600160801b03600160801b90920482169116106108d9578484838181106108b8576108b8610d6b565b90506020028101906108ca9190610e14565b6108d390610f94565b60408401525b50600101610713565b505b92915050565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092019092526020810180516001600160e01b03166323b872dd60e01b17905261070291869190600090610950908416836109a8565b905080516000141580156109755750808060200190518101906109739190611086565b155b156109a357604051635274afe760e01b81526001600160a01b03841660048201526024015b60405180910390fd5b505050565b60606109b6838360006109bd565b9392505050565b6060814710156109e25760405163cd78605960e01b815230600482015260240161099a565b600080856001600160a01b031684866040516109fe91906110a8565b60006040518083038185875af1925050503d8060008114610a3b576040519150601f19603f3d011682016040523d82523d6000602084013e610a40565b606091505b5091509150610a50868383610a5a565b9695505050505050565b606082610a6f57610a6a82610ab6565b6109b6565b8151158015610a8657506001600160a01b0384163b155b15610aaf57604051639996b31560e01b81526001600160a01b038516600482015260240161099a565b50806109b6565b805115610ac65780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60405180606001604052806003905b6040805160608101825260008082526020808301829052928201528252600019909201910181610aee5790505090565b60405180606001604052806003905b610b35610b4b565b815260200190600190039081610b2d5790505090565b6040518060600160405280610ba6604080516101208101909152806000815260200160008152602001600081526020016060815260200160008152602001600081526020016000815260200160008152602001600081525090565b815260006020820181905260409091015290565b60008083601f840112610bcc57600080fd5b50813567ffffffffffffffff811115610be457600080fd5b6020830191508360208260051b8501011115610bff57600080fd5b9250929050565b60008060208385031215610c1957600080fd5b823567ffffffffffffffff811115610c3057600080fd5b610c3c85828601610bba565b90969095509350505050565b6101208101818360005b6003811015610c8f578151805184526020810151602085015260408101511515604085015250606083019250602082019150600181019050610c52565b50505092915050565b602080825282518282018190526000918401906040840190835b81811015610cd0578351835260209384019390920191600101610cb2565b509095945050505050565b60008060208385031215610cee57600080fd5b823567ffffffffffffffff811115610d0557600080fd5b8301601f81018513610d1657600080fd5b803567ffffffffffffffff811115610d2d57600080fd5b856020606083028401011115610d4257600080fd5b6020919091019590945092505050565b600060208284031215610d6457600080fd5b5035919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201610da957610da9610d81565b5060010190565b80820281158282048414176108e4576108e4610d81565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b0381168114610df457600080fd5b919050565b600060208284031215610e0b57600080fd5b6109b682610ddd565b60008235605e19833603018112610e2a57600080fd5b9190910192915050565b6040805190810167ffffffffffffffff81118282101715610e5757610e57610dc7565b60405290565b6040516060810167ffffffffffffffff81118282101715610e5757610e57610dc7565b604051610120810167ffffffffffffffff81118282101715610e5757610e57610dc7565b604051601f8201601f1916810167ffffffffffffffff81118282101715610ecd57610ecd610dc7565b604052919050565b803560048110610df457600080fd5b600082601f830112610ef557600080fd5b813567ffffffffffffffff811115610f0f57610f0f610dc7565b610f1e60208260051b01610ea4565b8082825260208201915060208360061b860101925085831115610f4057600080fd5b602085015b83811015610f8a5760408188031215610f5d57600080fd5b610f65610e34565b610f6e82610ed5565b8152602082810135818301529084529290920191604001610f45565b5095945050505050565b600060608236031215610fa657600080fd5b610fae610e5d565b823567ffffffffffffffff811115610fc557600080fd5b8301610120368290031215610fd957600080fd5b610fe1610e80565b610fea82610ed5565b81526020828101359082015260408083013590820152606082013567ffffffffffffffff81111561101a57600080fd5b61102636828501610ee4565b6060830152506080828101359082015260a0808301359082015260c0808301359082015260e080830135908201526101009182013591810191909152815261107060208401610ddd565b6020820152604092830135928101929092525090565b60006020828403121561109857600080fd5b815180151581146109b657600080fd5b6000825160005b818110156110c957602081860181015185830152016110af565b50600092019182525091905056fea26469706673582212204b7461b1591664a7d8cdc62200ab8ab67a4af204bf48b77dfa4538c767e5e60864736f6c634300081c0033", - "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061007d5760003560e01c80634ec42e8e1161005b5780634ec42e8e146100e2578063b6c2141b1461010d578063c7170bb614610122578063fe3c806e1461012a57600080fd5b806338f03e75146100825780633ffbb252146100ab578063474740b1146100cb575b600080fd5b610095610090366004610c06565b610188565b6040516100a29190610c48565b60405180910390f35b6100be6100b9366004610c06565b610538565b6040516100a29190610c98565b6100d461020081565b6040519081526020016100a2565b6000546100f5906001600160a01b031681565b6040516001600160a01b0390911681526020016100a2565b61012061011b366004610cdb565b6105c1565b005b6100d4600581565b610160610138366004610d52565b600160205260009081526040902054608081901b90600160801b90046001600160801b031682565b604080516001600160801b031990931683526001600160801b039091166020830152016100a2565b610190610adf565b600582146101b157604051637db491eb60e01b815260040160405180910390fd5b60006101bd8484610708565b90506000805b60038110156101e3576001828111156101da578092505b506001016101c3565b5060008060005b60038110156102615760006001600087846003811061020b5761020b610d6b565b602002015160400151815260200190815260200160002060000160109054906101000a90046001600160801b03166001600160801b03161115610259578261025281610d97565b9350508091505b6001016101ea565b506000826001036102f85760006001600087856003811061028457610284610d6b565b602090810291909101516040908101518352828201939093529082016000208251808401909352546001600160801b0319608082901b168352600160801b90046001600160801b03169082015290506102de856003610db0565b81602001516001600160801b0316106102f657600191505b505b60005b600381101561052c5760006001600088846003811061031c5761031c610d6b565b6020908102919091015160409081015183528282019390935290820160009081208351808501909452546001600160801b0319608082901b168452600160801b90046001600160801b03169183019190915290915060019080851561042f5760208401516001600160801b03161561042457610399896003610db0565b84602001516001600160801b0316101591506103dc8a86600381106103c0576103c0610d6b565b6020020151602001516001600160801b031960609190911b1690565b84516001600160801b0319908116911614801561041d575060008a866003811061040857610408610d6b565b6020020151602001516001600160a01b031614155b90506104b3565b5060019050806104b3565b60208401516001600160801b03161580159061045857508284602001516001600160801b031610155b915061046f8a86600381106103c0576103c0610d6b565b84516001600160801b031990811691161480156104b0575060008a866003811061049b5761049b610d6b565b6020020151602001516001600160a01b031614155b90505b600060405180606001604052808c88600381106104d2576104d2610d6b565b602002015160400151815260200186602001516001600160801b031681526020018480156104fd5750835b151590529050808c876003811061051657610516610d6b565b60200201525050600190930192506102fb915050565b50505050505092915050565b606060008267ffffffffffffffff81111561055557610555610dc7565b60405190808252806020026020018201604052801561057e578160200160208202803683370190505b50905060005b838110156105b957600060019050808383815181106105a5576105a5610d6b565b602090810291909101015250600101610584565b509392505050565b806102008111156105e557604051630d67f41160e21b815260040160405180910390fd5b60005b81811015610702573684848381811061060357610603610d6b565b60600291909101915061063790503361061f6020840184610df9565b6000546001600160a01b0316919060208501356108ea565b60408051808201909152806106626106526020850185610df9565b60601b6001600160801b03191690565b6001600160801b0319168152602083810180356001600160801b03818116948401949094526040808701356000818152600186529190912086519690940151909416600160801b0260809590951c949094179091559091906106c49084610df9565b6001600160a01b03167ff998960b1c6f0e0e89b7bbe6b6fbf3e03e6f08eee5b8430877d8adb8e149d58060405160405180910390a4506001016105e8565b50505050565b610710610b1e565b60005b828110156108e25760006001600086868581811061073357610733610d6b565b90506020028101906107459190610e14565b60409081013582526020808301939093529081016000908120825180840184529054608081901b6001600160801b03191682526001600160801b03600160801b9182900481168387019081528951860151855260019096529390922054935190945092048116911611156107fd576020830180516040850152835190528484838181106107d4576107d4610d6b565b90506020028101906107e69190610e14565b6107ef90610f94565b8360005b60200201526108d9565b6020808401516040908101516000908152600183522054908201516001600160801b03600160801b9092048216911610610870576020830151604084015284848381811061084d5761084d610d6b565b905060200281019061085f9190610e14565b61086890610f94565b8360016107f3565b60408084015181015160009081526001602090815291902054908201516001600160801b03600160801b90920482169116106108d9578484838181106108b8576108b8610d6b565b90506020028101906108ca9190610e14565b6108d390610f94565b60408401525b50600101610713565b505b92915050565b604080516001600160a01b038581166024830152848116604483015260648083018590528351808403909101815260849092019092526020810180516001600160e01b03166323b872dd60e01b17905261070291869190600090610950908416836109a8565b905080516000141580156109755750808060200190518101906109739190611086565b155b156109a357604051635274afe760e01b81526001600160a01b03841660048201526024015b60405180910390fd5b505050565b60606109b6838360006109bd565b9392505050565b6060814710156109e25760405163cd78605960e01b815230600482015260240161099a565b600080856001600160a01b031684866040516109fe91906110a8565b60006040518083038185875af1925050503d8060008114610a3b576040519150601f19603f3d011682016040523d82523d6000602084013e610a40565b606091505b5091509150610a50868383610a5a565b9695505050505050565b606082610a6f57610a6a82610ab6565b6109b6565b8151158015610a8657506001600160a01b0384163b155b15610aaf57604051639996b31560e01b81526001600160a01b038516600482015260240161099a565b50806109b6565b805115610ac65780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b60405180606001604052806003905b6040805160608101825260008082526020808301829052928201528252600019909201910181610aee5790505090565b60405180606001604052806003905b610b35610b4b565b815260200190600190039081610b2d5790505090565b6040518060600160405280610ba6604080516101208101909152806000815260200160008152602001600081526020016060815260200160008152602001600081526020016000815260200160008152602001600081525090565b815260006020820181905260409091015290565b60008083601f840112610bcc57600080fd5b50813567ffffffffffffffff811115610be457600080fd5b6020830191508360208260051b8501011115610bff57600080fd5b9250929050565b60008060208385031215610c1957600080fd5b823567ffffffffffffffff811115610c3057600080fd5b610c3c85828601610bba565b90969095509350505050565b6101208101818360005b6003811015610c8f578151805184526020810151602085015260408101511515604085015250606083019250602082019150600181019050610c52565b50505092915050565b602080825282518282018190526000918401906040840190835b81811015610cd0578351835260209384019390920191600101610cb2565b509095945050505050565b60008060208385031215610cee57600080fd5b823567ffffffffffffffff811115610d0557600080fd5b8301601f81018513610d1657600080fd5b803567ffffffffffffffff811115610d2d57600080fd5b856020606083028401011115610d4257600080fd5b6020919091019590945092505050565b600060208284031215610d6457600080fd5b5035919050565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201610da957610da9610d81565b5060010190565b80820281158282048414176108e4576108e4610d81565b634e487b7160e01b600052604160045260246000fd5b80356001600160a01b0381168114610df457600080fd5b919050565b600060208284031215610e0b57600080fd5b6109b682610ddd565b60008235605e19833603018112610e2a57600080fd5b9190910192915050565b6040805190810167ffffffffffffffff81118282101715610e5757610e57610dc7565b60405290565b6040516060810167ffffffffffffffff81118282101715610e5757610e57610dc7565b604051610120810167ffffffffffffffff81118282101715610e5757610e57610dc7565b604051601f8201601f1916810167ffffffffffffffff81118282101715610ecd57610ecd610dc7565b604052919050565b803560048110610df457600080fd5b600082601f830112610ef557600080fd5b813567ffffffffffffffff811115610f0f57610f0f610dc7565b610f1e60208260051b01610ea4565b8082825260208201915060208360061b860101925085831115610f4057600080fd5b602085015b83811015610f8a5760408188031215610f5d57600080fd5b610f65610e34565b610f6e82610ed5565b8152602082810135818301529084529290920191604001610f45565b5095945050505050565b600060608236031215610fa657600080fd5b610fae610e5d565b823567ffffffffffffffff811115610fc557600080fd5b8301610120368290031215610fd957600080fd5b610fe1610e80565b610fea82610ed5565b81526020828101359082015260408083013590820152606082013567ffffffffffffffff81111561101a57600080fd5b61102636828501610ee4565b6060830152506080828101359082015260a0808301359082015260c0808301359082015260e080830135908201526101009182013591810191909152815261107060208401610ddd565b6020820152604092830135928101929092525090565b60006020828403121561109857600080fd5b815180151581146109b657600080fd5b6000825160005b818110156110c957602081860181015185830152016110af565b50600092019182525091905056fea26469706673582212204b7461b1591664a7d8cdc62200ab8ab67a4af204bf48b77dfa4538c767e5e60864736f6c634300081c0033", - "linkReferences": {}, - "deployedLinkReferences": {} -} diff --git a/artifacts/PaymentVaultV2.json b/artifacts/PaymentVaultV2.json new file mode 100644 index 0000000..16a8cb5 --- /dev/null +++ b/artifacts/PaymentVaultV2.json @@ -0,0 +1 @@ +{"abi":[{"type":"constructor","inputs":[{"name":"_antToken","type":"address","internalType":"contract IERC20"},{"name":"_batchLimit","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"CANDIDATES_PER_POOL","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"MAX_MERKLE_DEPTH","inputs":[],"outputs":[{"name":"","type":"uint8","internalType":"uint8"}],"stateMutability":"view"},{"type":"function","name":"antToken","inputs":[],"outputs":[{"name":"","type":"address","internalType":"contract IERC20"}],"stateMutability":"view"},{"type":"function","name":"batchLimit","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"completedMerklePayments","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"stateMutability":"view"},{"type":"function","name":"completedPayments","inputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"rewardsAddress","type":"bytes16","internalType":"bytes16"},{"name":"amount","type":"uint128","internalType":"uint128"}],"stateMutability":"view"},{"type":"function","name":"getCompletedMerklePayment","inputs":[{"name":"winnerHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"tuple","internalType":"struct CompletedMerklePayment","components":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"},{"name":"paidNodeAddresses","type":"tuple[]","internalType":"struct PaidNode[]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"poolIndex","type":"uint8","internalType":"uint8"},{"name":"amount","type":"uint256","internalType":"uint256"}]}]}],"stateMutability":"view"},{"type":"function","name":"payForMerkleTree","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"poolCommitments","type":"tuple[]","internalType":"struct PoolCommitment[]","components":[{"name":"poolHash","type":"bytes32","internalType":"bytes32"},{"name":"candidates","type":"tuple[16]","internalType":"struct CandidateNode[16]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"}]}]},{"name":"merklePaymentTimestamp","type":"uint64","internalType":"uint64"}],"outputs":[{"name":"winnerPoolHash","type":"bytes32","internalType":"bytes32"},{"name":"totalAmount","type":"uint256","internalType":"uint256"}],"stateMutability":"nonpayable"},{"type":"function","name":"payForQuotes","inputs":[{"name":"_payments","type":"tuple[]","internalType":"struct DataPayment[]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"quoteHash","type":"bytes32","internalType":"bytes32"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"verifyPayment","inputs":[{"name":"_payments","type":"tuple[]","internalType":"struct DataPayment[]","components":[{"name":"rewardsAddress","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"quoteHash","type":"bytes32","internalType":"bytes32"}]}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct PaymentVerificationResult[]","components":[{"name":"quoteHash","type":"bytes32","internalType":"bytes32"},{"name":"amountPaid","type":"uint256","internalType":"uint256"},{"name":"isValid","type":"bool","internalType":"bool"}]}],"stateMutability":"view"},{"type":"event","name":"DataPaymentMade","inputs":[{"name":"rewardsAddress","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"quoteHash","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"MerklePaymentMade","inputs":[{"name":"winnerPoolHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"depth","type":"uint8","indexed":false,"internalType":"uint8"},{"name":"totalAmount","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"merklePaymentTimestamp","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"error","name":"AntTokenNull","inputs":[]},{"type":"error","name":"BatchLimitExceeded","inputs":[]},{"type":"error","name":"DepthTooLarge","inputs":[{"name":"depth","type":"uint8","internalType":"uint8"},{"name":"maxDepth","type":"uint8","internalType":"uint8"}]},{"type":"error","name":"InvalidInputLength","inputs":[]},{"type":"error","name":"PaymentAlreadyExists","inputs":[{"name":"winnerPoolHash","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"WrongPoolCount","inputs":[{"name":"expected","type":"uint256","internalType":"uint256"},{"name":"actual","type":"uint256","internalType":"uint256"}]}],"bytecode":{"object":"0x608034608c57601f61100638819003918201601f19168301916001600160401b038311848410176090578084926040948552833981010312608c5780516001600160a01b0381169190829003608c5760200151908015607d575f80546001600160a01b031916919091179055600155604051610f6190816100a58239f35b632d06160b60e21b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffdfe6080806040526004361015610012575f80fd5b5f3560e01c908163043b758714610c5557508063137978c314610c3a5780633499016514610ad0578063474740b114610ab35780634ec42e8e14610a8c5780635460f2401461040f5780635d847f0d14610248578063b6c2141b14610109578063cf41319f146100cc5763fe3c806e1461008a575f80fd5b346100c85760203660031901126100c8576004355f5260026020526040805f20548151906001600160801b03198160801b16825260801c6020820152f35b5f80fd5b346100c85760203660031901126100c8576004355f5260036020526040805f20546001600160401b0382519160ff8116835260081c166020820152f35b346100c85761011736610c6e565b6001548111610239575f5b81811061012b57005b806101396001928486610d85565b604081013590811561020f57838060a01b035f54169061016b61015b82610d95565b9260208301359384913390610e99565b61018761017782610d95565b60601b6001600160801b03191690565b6040519061019482610cdb565b6001600160801b0319168152602081016001600160801b0384168152845f52600260205260405f20915160801c906001600160801b0319905160801b161790556101e3858060a01b0391610d95565b167ff998960b1c6f0e0e89b7bbe6b6fbf3e03e6f08eee5b8430877d8adb8e149d5805f80a45b01610122565b6102349150838060a01b035f541690602061022982610d95565b910135913390610e99565b610209565b630d67f41160e21b5f5260045ffd5b346100c85761025636610c6e565b9061026082610d17565b9161026e6040519384610cf6565b808352601f1961027d82610d17565b015f5b8181106103f85750505f5b8181106102f057836040518091602082016020835281518091526020604084019201905f5b8181106102be575050500390f35b9193509160206060600192604087518051835284810151858401520151151560408201520194019101918493926102b0565b6102fb818385610d85565b906060823603126100c85760405160019261031582610cc0565b61031e81610d2e565b82526020604081840192828101358452013560408401908082525f526002825260405f20936040519461035086610cdb565b54946001600160801b03198660801b169586825260801c938491015282151593846103ed575b505160a087901b87900316606081901b6001600160801b03191690941493846103e3575b505192826103db575b50604051926103b184610cc0565b83526020830152151560408201526103c98287610d71565b526103d48186610d71565b500161028b565b9150886103a3565b151593508961039a565b51831493508a610376565b602090610403610d53565b82828801015201610280565b346100c85760603660031901126100c85760043560ff81168091036100c857602435906001600160401b0382116100c857366023830112156100c8578160040135916001600160401b0383116100c8573660246104208502830101116100c857604435926001600160401b038416918285036100c857600c8411610a74576001840160ff811161075657607f600191821c161b808303610a5d575060405191602083014481524260408501523360601b60608501526001600160401b0360c01b8760c01b1693846074820152605c81526104ea607c82610cf6565b519020811561076a578190069081101561077e57610420020161042060231982360301126100c8576040519361051f85610cdb565b60248201358552366063830112156100c85761020094604051926105438785610cf6565b83906104448101903682116100c857604401915b818310610a2557505050602081019283525191825f52600360205260ff60405f205416610a125760405161058b8782610cf6565b863682375f5b601081106109ee57506105a390610da9565b9283831b938085046001851b1490151715610756576105c183610d17565b946105cf6040519687610cf6565b838652601f196105de85610d17565b013660208801376040519788018881106001600160401b03821117610953576040525f885260016020890152600260408901526003606089015260046080890152600560a0890152600660c0890152600760e089015260086101008901526009610120890152600a610140890152600b610160890152600c610180890152600d6101a0890152600e6101c0890152600f6101e08901526040516020810191448352836040830152606082015260488152610699606882610cf6565b519020935f935f5b60ff811690828210156107b957602060ff881614610792575b60ff871660ff8114610756576001019660ff16602081101561077e578260100360ff81116107565760ff16801561076a578960ff921a061682019060ff8211610756578261074e60ff6107458f96829783600198168461071a8284610d42565b5116906107348661072b8686610d42565b51169184610d42565b5261073f8383610d42565b52610d42565b5116918c610d71565b5201166106a1565b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b95509560405160208101918252602081526107ae604082610cf6565b519020955f956106ba565b50505f838152600360205260409020805468ffffffffffffffffff1916821760088c901b68ffffffffffffffff00161781558890886107f784610d17565b966108056040519889610cf6565b848852601f1961081486610d17565b015f5b8181106109d7575050841561076a578486045f5b868110610967575050505060010194805190680100000000000000008211610953578654828855808310610901575b50602001955f5260205f20955f905b8282106108b65760408787817f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec60608a8a875191825285602083015287820152a282519182526020820152f35b6002602082604060019451858060a01b038151168d549060ff60a01b8684015160a01b16916affffffffffffffffffffff60a81b1617178d550151848c015501980191019096610869565b6001600160ff1b0381168103610756576001600160ff1b038316830361075657875f5260205f209060011b8101908360011b015b818110610942575061085a565b5f8082556001820155600201610935565b634e487b7160e01b5f52604160045260245ffd5b8060ff61097660019387610d71565b5116828060a01b03610989828751610d42565b515116906109a1858333878060a01b035f5416610e99565b604051916109ae83610cc0565b825260208201528360408201526109c5828d610d71565b526109d0818c610d71565b500161082b565b6020906109e2610d53565b82828d01015201610817565b8060206109fe6001938651610d42565b510151610a0b8285610d42565b5201610591565b82639d8c19ed60e01b5f5260045260245ffd5b6040833603126100c85760206040918251610a3f81610cdb565b610a4886610d2e565b81528286013583820152815201920191610557565b90506347fe11b560e11b5f5260045260245260445ffd5b8363164b5f0360e11b5f52600452600c60245260445ffd5b346100c8575f3660031901126100c8575f546040516001600160a01b039091168152602090f35b346100c8575f3660031901126100c8576020600154604051908152f35b346100c85760203660031901126100c857606060408051610af081610cc0565b5f81525f602082015201526004355f52600360205260405f20604051610b1581610cc0565b600182549260ff841683526001600160401b03602084019460081c168452018054610b3f81610d17565b91610b4d6040519384610cf6565b81835260208301905f5260205f205f915b838310610bfa576001600160401b03878787604082019081526040519384936020855260ff6080860194511660208601525116604084015251906060808401528151809152602060a084019201905f5b818110610bbc575050500390f35b825180516001600160a01b0316855260208181015160ff16818701526040918201519186019190915286955060609094019390920191600101610bae565b60026020600192604051610c0d81610cc0565b60ff8654868060a01b038116835260a01c1683820152848601546040820152815201920192019190610b5e565b346100c8575f3660031901126100c857602060405160108152f35b346100c8575f3660031901126100c85780600c60209252f35b9060206003198301126100c8576004356001600160401b0381116100c857826023820112156100c8578060040135926001600160401b0384116100c857602460608502830101116100c8576024019190565b606081019081106001600160401b0382111761095357604052565b604081019081106001600160401b0382111761095357604052565b90601f801991011681019081106001600160401b0382111761095357604052565b6001600160401b0381116109535760051b60200190565b35906001600160a01b03821682036100c857565b90601081101561077e5760051b0190565b60405190610d6082610cc0565b5f6040838281528260208201520152565b805182101561077e5760209160051b010190565b919081101561077e576060020190565b356001600160a01b03811681036100c85790565b5f91600f5b80840180851161075657610dc59060011c84610d42565b51908481925b83821115610e06575060088310610de6575050915b91610dae565b93945092905060088111610dfa5792610de0565b50916101009150015190565b9590949392945b86610e188287610d42565b511015610e2f575f19811461075657600101610e0d565b93945b610e3c8187610d42565b51871015610e52578015610756575f1901610e32565b92939095838211610dcb579290600190610e6c8388610d42565b51610e778289610d42565b51610e82858a610d42565b52610e8d8289610d42565b5201905f190192610dcb565b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af19060015f5114821615610f0a575b6040525f60605215610eea5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b906001811516610f2257823b15153d15161690610edb565b503d5f823e3d90fdfea264697066735822122054b1373b8865f9c6b0d3e14bb23c75fd8f64edf19fa31761ba2b60cd5867526664736f6c634300081c0033","sourceMap":"721:6063:2:-:0;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;1313:32;;;1309:84;;-1:-1:-1;721:6063:2;;-1:-1:-1;;;;;;721:6063:2;;;;;;;;;;;;;;;;;;1309:84;1368:14;;;-1:-1:-1;1368:14:2;;-1:-1:-1;1368:14:2;721:6063;-1:-1:-1;721:6063:2;;;;;;-1:-1:-1;721:6063:2;;;;;-1:-1:-1;721:6063:2","linkReferences":{}},"deployedBytecode":{"object":"0x6080806040526004361015610012575f80fd5b5f3560e01c908163043b758714610c5557508063137978c314610c3a5780633499016514610ad0578063474740b114610ab35780634ec42e8e14610a8c5780635460f2401461040f5780635d847f0d14610248578063b6c2141b14610109578063cf41319f146100cc5763fe3c806e1461008a575f80fd5b346100c85760203660031901126100c8576004355f5260026020526040805f20548151906001600160801b03198160801b16825260801c6020820152f35b5f80fd5b346100c85760203660031901126100c8576004355f5260036020526040805f20546001600160401b0382519160ff8116835260081c166020820152f35b346100c85761011736610c6e565b6001548111610239575f5b81811061012b57005b806101396001928486610d85565b604081013590811561020f57838060a01b035f54169061016b61015b82610d95565b9260208301359384913390610e99565b61018761017782610d95565b60601b6001600160801b03191690565b6040519061019482610cdb565b6001600160801b0319168152602081016001600160801b0384168152845f52600260205260405f20915160801c906001600160801b0319905160801b161790556101e3858060a01b0391610d95565b167ff998960b1c6f0e0e89b7bbe6b6fbf3e03e6f08eee5b8430877d8adb8e149d5805f80a45b01610122565b6102349150838060a01b035f541690602061022982610d95565b910135913390610e99565b610209565b630d67f41160e21b5f5260045ffd5b346100c85761025636610c6e565b9061026082610d17565b9161026e6040519384610cf6565b808352601f1961027d82610d17565b015f5b8181106103f85750505f5b8181106102f057836040518091602082016020835281518091526020604084019201905f5b8181106102be575050500390f35b9193509160206060600192604087518051835284810151858401520151151560408201520194019101918493926102b0565b6102fb818385610d85565b906060823603126100c85760405160019261031582610cc0565b61031e81610d2e565b82526020604081840192828101358452013560408401908082525f526002825260405f20936040519461035086610cdb565b54946001600160801b03198660801b169586825260801c938491015282151593846103ed575b505160a087901b87900316606081901b6001600160801b03191690941493846103e3575b505192826103db575b50604051926103b184610cc0565b83526020830152151560408201526103c98287610d71565b526103d48186610d71565b500161028b565b9150886103a3565b151593508961039a565b51831493508a610376565b602090610403610d53565b82828801015201610280565b346100c85760603660031901126100c85760043560ff81168091036100c857602435906001600160401b0382116100c857366023830112156100c8578160040135916001600160401b0383116100c8573660246104208502830101116100c857604435926001600160401b038416918285036100c857600c8411610a74576001840160ff811161075657607f600191821c161b808303610a5d575060405191602083014481524260408501523360601b60608501526001600160401b0360c01b8760c01b1693846074820152605c81526104ea607c82610cf6565b519020811561076a578190069081101561077e57610420020161042060231982360301126100c8576040519361051f85610cdb565b60248201358552366063830112156100c85761020094604051926105438785610cf6565b83906104448101903682116100c857604401915b818310610a2557505050602081019283525191825f52600360205260ff60405f205416610a125760405161058b8782610cf6565b863682375f5b601081106109ee57506105a390610da9565b9283831b938085046001851b1490151715610756576105c183610d17565b946105cf6040519687610cf6565b838652601f196105de85610d17565b013660208801376040519788018881106001600160401b03821117610953576040525f885260016020890152600260408901526003606089015260046080890152600560a0890152600660c0890152600760e089015260086101008901526009610120890152600a610140890152600b610160890152600c610180890152600d6101a0890152600e6101c0890152600f6101e08901526040516020810191448352836040830152606082015260488152610699606882610cf6565b519020935f935f5b60ff811690828210156107b957602060ff881614610792575b60ff871660ff8114610756576001019660ff16602081101561077e578260100360ff81116107565760ff16801561076a578960ff921a061682019060ff8211610756578261074e60ff6107458f96829783600198168461071a8284610d42565b5116906107348661072b8686610d42565b51169184610d42565b5261073f8383610d42565b52610d42565b5116918c610d71565b5201166106a1565b634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52601260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b95509560405160208101918252602081526107ae604082610cf6565b519020955f956106ba565b50505f838152600360205260409020805468ffffffffffffffffff1916821760088c901b68ffffffffffffffff00161781558890886107f784610d17565b966108056040519889610cf6565b848852601f1961081486610d17565b015f5b8181106109d7575050841561076a578486045f5b868110610967575050505060010194805190680100000000000000008211610953578654828855808310610901575b50602001955f5260205f20955f905b8282106108b65760408787817f89f0ad3859fec321e325bcc553fe234bcad374789a86f7ba932067f3f05affec60608a8a875191825285602083015287820152a282519182526020820152f35b6002602082604060019451858060a01b038151168d549060ff60a01b8684015160a01b16916affffffffffffffffffffff60a81b1617178d550151848c015501980191019096610869565b6001600160ff1b0381168103610756576001600160ff1b038316830361075657875f5260205f209060011b8101908360011b015b818110610942575061085a565b5f8082556001820155600201610935565b634e487b7160e01b5f52604160045260245ffd5b8060ff61097660019387610d71565b5116828060a01b03610989828751610d42565b515116906109a1858333878060a01b035f5416610e99565b604051916109ae83610cc0565b825260208201528360408201526109c5828d610d71565b526109d0818c610d71565b500161082b565b6020906109e2610d53565b82828d01015201610817565b8060206109fe6001938651610d42565b510151610a0b8285610d42565b5201610591565b82639d8c19ed60e01b5f5260045260245ffd5b6040833603126100c85760206040918251610a3f81610cdb565b610a4886610d2e565b81528286013583820152815201920191610557565b90506347fe11b560e11b5f5260045260245260445ffd5b8363164b5f0360e11b5f52600452600c60245260445ffd5b346100c8575f3660031901126100c8575f546040516001600160a01b039091168152602090f35b346100c8575f3660031901126100c8576020600154604051908152f35b346100c85760203660031901126100c857606060408051610af081610cc0565b5f81525f602082015201526004355f52600360205260405f20604051610b1581610cc0565b600182549260ff841683526001600160401b03602084019460081c168452018054610b3f81610d17565b91610b4d6040519384610cf6565b81835260208301905f5260205f205f915b838310610bfa576001600160401b03878787604082019081526040519384936020855260ff6080860194511660208601525116604084015251906060808401528151809152602060a084019201905f5b818110610bbc575050500390f35b825180516001600160a01b0316855260208181015160ff16818701526040918201519186019190915286955060609094019390920191600101610bae565b60026020600192604051610c0d81610cc0565b60ff8654868060a01b038116835260a01c1683820152848601546040820152815201920192019190610b5e565b346100c8575f3660031901126100c857602060405160108152f35b346100c8575f3660031901126100c85780600c60209252f35b9060206003198301126100c8576004356001600160401b0381116100c857826023820112156100c8578060040135926001600160401b0384116100c857602460608502830101116100c8576024019190565b606081019081106001600160401b0382111761095357604052565b604081019081106001600160401b0382111761095357604052565b90601f801991011681019081106001600160401b0382111761095357604052565b6001600160401b0381116109535760051b60200190565b35906001600160a01b03821682036100c857565b90601081101561077e5760051b0190565b60405190610d6082610cc0565b5f6040838281528260208201520152565b805182101561077e5760209160051b010190565b919081101561077e576060020190565b356001600160a01b03811681036100c85790565b5f91600f5b80840180851161075657610dc59060011c84610d42565b51908481925b83821115610e06575060088310610de6575050915b91610dae565b93945092905060088111610dfa5792610de0565b50916101009150015190565b9590949392945b86610e188287610d42565b511015610e2f575f19811461075657600101610e0d565b93945b610e3c8187610d42565b51871015610e52578015610756575f1901610e32565b92939095838211610dcb579290600190610e6c8388610d42565b51610e778289610d42565b51610e82858a610d42565b52610e8d8289610d42565b5201905f190192610dcb565b6040516323b872dd60e01b5f9081526001600160a01b039384166004529290931660245260449390935260209060648180865af19060015f5114821615610f0a575b6040525f60605215610eea5750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b906001811516610f2257823b15153d15161690610edb565b503d5f823e3d90fdfea264697066735822122054b1373b8865f9c6b0d3e14bb23c75fd8f64edf19fa31761ba2b60cd5867526664736f6c634300081c0033","sourceMap":"721:6063:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;;864:61;721:6063;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;;975:73;721:6063;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4330:10;721:6063;4316:24;;4312:82;;721:6063;4424:15;;;;;;721:6063;4441:3;4495:12;;4330:10;4495:12;;;;:::i;:::-;721:6063;4526:21;;721:6063;4526:35;;;4522:263;;721:6063;;;;;;;;4870:26;4914:18;4870:26;;;:::i;:::-;4914:18;;;;721:6063;4842:10;;;;4914:18;;:::i;:::-;5055:43;5071:26;;;:::i;:::-;721:6063;;-1:-1:-1;;;;;;721:6063:2;;6650:132;5055:43;721:6063;;;;;;:::i;:::-;-1:-1:-1;;;;;721:6063:2;;;;4914:18;5004:162;;-1:-1:-1;;;;;721:6063:2;;;;;;;4961:17;4914:18;721:6063;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;5219:26;721:6063;;;;;5219:26;;:::i;:::-;721:6063;5186:148;721:6063;5186:148;;4409:13;721:6063;4409:13;;4522:263;4708:18;721:6063;;;;;;;;;;4660:26;4914:18;4660:26;;;:::i;:::-;4708:18;;721:6063;4842:10;;4708:18;;:::i;:::-;4762:8;;4312:82;4363:20;;;721:6063;4363:20;721:6063;;4363:20;721:6063;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;721:6063:2;;;:::i;:::-;;;;;;;;;5660:13;;721:6063;5675:20;;;;;;721:6063;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;5697:3;5746:12;;;;;:::i;:::-;721:6063;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;5811:17;721:6063;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;5912:23;;;5911:84;;;;5697:3;-1:-1:-1;721:6063:2;;;;;;;;;;;;;-1:-1:-1;;;;;;721:6063:2;6029:86;;;;;:145;;5697:3;721:6063;;6362:25;;;;5697:3;721:6063;;;;;;;:::i;:::-;;;;6214:188;;721:6063;;;;6214:188;;721:6063;6189:213;;;;:::i;:::-;;;;;;:::i;:::-;;721:6063;5660:13;;6362:25;;-1:-1:-1;6362:25:2;;;6029:145;6136:37;;;-1:-1:-1;6029:145:2;;;5911:84;721:6063;5957:37;;;-1:-1:-1;5911:84:2;;;721:6063;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;1140:2;1753:24;;1749:100;;267:1:1;721:6063:2;;;;;;;;267:1:1;721:6063:2;;;;;1988:39;;;1984:130;;721:6063;;;603:148:1;721:6063:2;603:148:1;;637:16;721:6063:2;;671:15:1;721:6063:2;;;;2256:10;721:6063;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;603:148:1;;;;;;;:::i;:::-;721:6063:2;580:181:1;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2481:23;721:6063;;;;;;;;2477:124;;721:6063;;;;;;:::i;:::-;;;;;;2683:23;721:6063;2683:23;;;;2805:33;;;;:::i;:::-;:48;721:6063;;;;;;;267:1:1;721:6063:2;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;721:6063:2;;;:::i;:::-;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;267:1:1;721:6063:2;1051:61:1;;721:6063:2;272:1:1;721:6063:2;1051:61:1;;721:6063:2;2481:23;721:6063;1051:61:1;;721:6063:2;;1051:61:1;;;721:6063:2;;1051:61:1;;;721:6063:2;;;1051:61:1;;721:6063:2;1079:1:1;721:6063:2;1051:61:1;;721:6063:2;1081:1:1;1051:61;;;721:6063:2;1083:1:1;1051:61;;;721:6063:2;1085:2:1;1051:61;;;721:6063:2;1088:2:1;1051:61;;;721:6063:2;1140:2;1051:61:1;;;721:6063:2;1094:2:1;1051:61;;;721:6063:2;1097:2:1;1051:61;;;721:6063:2;1100:2:1;1051:61;;;721:6063:2;;;;1161:55:1;;637:16;;721:6063:2;;;;;;;;;;;1161:55:1;;;;;;;:::i;:::-;721:6063:2;1138:88:1;;1237:19;721:6063:2;1272:11:1;721:6063:2;1296:3:1;721:6063:2;;;1285:9:1;;;;;;;721:6063:2;;;;1376:15:1;1372:125;;1296:3;721:6063:2;1584:11:1;721:6063:2;;;;;;267:1:1;721:6063:2;;;;;1579:17:1;;;;;721:6063:2;;;;;;;;;;;;;;1628:20:1;721:6063:2;1628:20:1;1579:17;721:6063:2;;;;;;;;;;;1750:23:1;721:6063:2;1763:10:1;721:6063:2;;;;;267:1:1;721:6063:2;;1712:10:1;;;;;:::i;:::-;721:6063:2;;1724:10:1;1684:51;1724:10;;;;;:::i;:::-;721:6063:2;;1684:51:1;;;:::i;:::-;721:6063:2;1684:51:1;;;;:::i;:::-;721:6063:2;1763:10:1;:::i;:::-;721:6063:2;;1750:23:1;;;:::i;:::-;721:6063:2;;;1272:11:1;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;1579:17:1;721:6063:2;;;;;;;;;;;1372:125:1;721:6063:2;;;;;;1428:22:1;;721:6063:2;;;;1428:22:1;;;721:6063:2;1428:22:1;;:::i;:::-;721:6063:2;1418:33:1;;1469:13;721:6063:2;1372:125:1;;;1285:9;-1:-1:-1;;721:6063:2;;;;2481:23;721:6063;;;;;;;-1:-1:-1;;721:6063:2;;;1081:1:1;721:6063:2;;;;;;;;1285:9:1;;;721:6063:2;;;:::i;:::-;;;;;;;;:::i;:::-;;;;-1:-1:-1;;721:6063:2;;;:::i;:::-;;;;;;;;;;;;;;;;;;;3426:9;;;;;;3946:22;;;;267:1:1;3946:22:2;721:6063;;;;;;;;;;;;;;;;;;;3406:530;721:6063;;;;;;;;;;;;;;;;;;;;;;3993:135;721:6063;;;;;;;;;;;;;;;;;3993:135;721:6063;;;;;;;;;;;272:1:1;721:6063:2;;;267:1:1;721:6063:2;;;;;;;;;;;;;;;;;;;;1051:61:1;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;267:1:1;721:6063:2;;;;;267:1:1;721:6063:2;;;;;;;;;;;;;;;;267:1:1;721:6063:2;;;272:1:1;721:6063:2;;;;;;;;;;;;;;;3437:3;3472:16;721:6063;3472:16;267:1:1;3472:16:2;;;:::i;:::-;721:6063;;;;;;;3527:47;:38;;;:47;:::i;:::-;;721:6063;;2256:10;3724:13;2256:10;;;721:6063;;;;;;;;3724:13;:::i;:::-;721:6063;;;;;;:::i;:::-;;;;3778:147;;721:6063;3778:147;721:6063;3778:147;;721:6063;3766:159;;;;:::i;:::-;;;;;;:::i;:::-;;721:6063;3411:13;;721:6063;;;;;:::i;:::-;;;;;;;;;;2708:3;2739:21;721:6063;2739:24;267:1:1;2739:21:2;;;:24;:::i;:::-;;:31;721:6063;2727:43;;;;:::i;:::-;721:6063;;2668:13;;2477:124;2554:36;;;;721:6063;2554:36;721:6063;;;;2554:36;721:6063;;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;1984:130;2050:53;;;;;721:6063;2050:53;721:6063;;;;;;2050:53;1749:100;1800:38;;;;721:6063;1800:38;721:6063;;1140:2;721:6063;;;;1800:38;721:6063;;;;;;-1:-1:-1;;721:6063:2;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;832:25;721:6063;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;6602:23;721:6063;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;721:6063:2;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;1239:2;721:6063;;;;;;;;;-1:-1:-1;;721:6063:2;;;;;1140:2;721:6063;;;;;;;-1:-1:-1;;721:6063:2;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;:::o;:::-;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;:::o;:::-;;;603:148:1;;721:6063:2;;;;;;;;-1:-1:-1;;;;;721:6063:2;;;;;;;:::o;:::-;-1:-1:-1;;;;;721:6063:2;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;;;721:6063:2;;;;;;:::o;:::-;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;-1:-1:-1;721:6063:2;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;;;;721:6063:2;;;;;;;:::o;1821:950:1:-;721:6063:2;1929:18:1;1945:2;2033:714;721:6063:2;;;;;;;;2076:22:1;721:6063:2;2040:4:1;721:6063:2;2076:22:1;;:::i;:::-;721:6063:2;2112:16:1;;2142:17;2199:325;2206:6;;;;;;;-1:-1:-1;1989:1:1;2578:6;-1:-1:-1;2578:6:1;;2604:9;;2574:163;;2033:714;;;2574:163;2638:6;;-1:-1:-1;2638:6:1;;-1:-1:-1;1989:1:1;2638:6;;1989:1;;2634:103;2574:163;;2634:103;721:6063:2;;;;;;;2711:11:1;:::o;2199:325::-;2232:24;;;;;;;2239:4;;;;;:::i;:::-;721:6063:2;2239:12:1;;;;-1:-1:-1;;721:6063:2;;;;2040:4:1;721:6063:2;2232:24:1;;2239:12;;;2275:24;2290:4;;;;:::i;:::-;721:6063:2;2282:12:1;;;;;721:6063:2;;;;-1:-1:-1;;721:6063:2;2275:24:1;;2282:12;;;;;2322:6;;;2199:325;2318:192;2368:4;;2040;2368;;;;;:::i;:::-;721:6063:2;2374:4:1;;;;:::i;:::-;721:6063:2;2352:27:1;;;;:::i;:::-;721:6063:2;2352:27:1;;;;:::i;:::-;721:6063:2;;;;;;2318:192:1;2199:325;;1662:232:8;10404:1148;;-1:-1:-1;;;;10404:1148:8;;;-1:-1:-1;;;;;10404:1148:8;;;;;;;;;;;;;;;;;;;-1:-1:-1;;1662:232:8;10404:1148;;;1809:4;-1:-1:-1;10404:1148:8;;;;;;;1662:232;10404:1148;;-1:-1:-1;10404:1148:8;;1766:48;1762:126;;1662:232;:::o;1762:126::-;-1:-1:-1;;;;1837:40:8;;;-1:-1:-1;;;;;721:6063:2;;;;10404:1148:8;721:6063:2;10404:1148:8;;1837:40;10404:1148;;1809:4;10404:1148;;;;;;;;;;;;;;;;;;;-1:-1:-1;10404:1148:8;;;;","linkReferences":{}},"methodIdentifiers":{"CANDIDATES_PER_POOL()":"137978c3","MAX_MERKLE_DEPTH()":"043b7587","antToken()":"4ec42e8e","batchLimit()":"474740b1","completedMerklePayments(bytes32)":"cf41319f","completedPayments(bytes32)":"fe3c806e","getCompletedMerklePayment(bytes32)":"34990165","payForMerkleTree(uint8,(bytes32,(address,uint256)[16])[],uint64)":"5460f240","payForQuotes((address,uint256,bytes32)[])":"b6c2141b","verifyPayment((address,uint256,bytes32)[])":"5d847f0d"},"rawMetadata":"{\"compiler\":{\"version\":\"0.8.28+commit.7893614a\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"_antToken\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_batchLimit\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"AntTokenNull\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"BatchLimitExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint8\",\"name\":\"maxDepth\",\"type\":\"uint8\"}],\"name\":\"DepthTooLarge\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidInputLength\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"}],\"name\":\"PaymentAlreadyExists\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"}],\"name\":\"SafeERC20FailedOperation\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"expected\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"actual\",\"type\":\"uint256\"}],\"name\":\"WrongPoolCount\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"quoteHash\",\"type\":\"bytes32\"}],\"name\":\"DataPaymentMade\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"MerklePaymentMade\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"CANDIDATES_PER_POOL\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"MAX_MERKLE_DEPTH\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"antToken\",\"outputs\":[{\"internalType\":\"contract IERC20\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batchLimit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"completedMerklePayments\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"completedPayments\",\"outputs\":[{\"internalType\":\"bytes16\",\"name\":\"rewardsAddress\",\"type\":\"bytes16\"},{\"internalType\":\"uint128\",\"name\":\"amount\",\"type\":\"uint128\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerHash\",\"type\":\"bytes32\"}],\"name\":\"getCompletedMerklePayment\",\"outputs\":[{\"components\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint8\",\"name\":\"poolIndex\",\"type\":\"uint8\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct PaidNode[]\",\"name\":\"paidNodeAddresses\",\"type\":\"tuple[]\"}],\"internalType\":\"struct CompletedMerklePayment\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint8\",\"name\":\"depth\",\"type\":\"uint8\"},{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"poolHash\",\"type\":\"bytes32\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"internalType\":\"struct CandidateNode[16]\",\"name\":\"candidates\",\"type\":\"tuple[16]\"}],\"internalType\":\"struct PoolCommitment[]\",\"name\":\"poolCommitments\",\"type\":\"tuple[]\"},{\"internalType\":\"uint64\",\"name\":\"merklePaymentTimestamp\",\"type\":\"uint64\"}],\"name\":\"payForMerkleTree\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"winnerPoolHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"totalAmount\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"quoteHash\",\"type\":\"bytes32\"}],\"internalType\":\"struct DataPayment[]\",\"name\":\"_payments\",\"type\":\"tuple[]\"}],\"name\":\"payForQuotes\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"rewardsAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"quoteHash\",\"type\":\"bytes32\"}],\"internalType\":\"struct DataPayment[]\",\"name\":\"_payments\",\"type\":\"tuple[]\"}],\"name\":\"verifyPayment\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"quoteHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"amountPaid\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"isValid\",\"type\":\"bool\"}],\"internalType\":\"struct PaymentVerificationResult[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"errors\":{\"SafeERC20FailedOperation(address)\":[{\"details\":\"An operation with an ERC-20 token failed.\"}]},\"kind\":\"dev\",\"methods\":{},\"title\":\"PaymentVaultV2\",\"version\":1},\"userdoc\":{\"events\":{\"MerklePaymentMade(bytes32,uint8,uint256,uint64)\":{\"notice\":\"Emitted when a Merkle batch payment is made\"}},\"kind\":\"user\",\"methods\":{\"CANDIDATES_PER_POOL()\":{\"notice\":\"Number of candidates per pool (fixed)\"},\"MAX_MERKLE_DEPTH()\":{\"notice\":\"Maximum supported Merkle tree depth\"}},\"notice\":\"Unified payment vault for both single-node and merkle batch payments. No proxy, no Ownable \\u2014 for local Anvil testing. Nodes calculate their own prices as (chunks_stored / 6000)^2.\",\"version\":1}},\"settings\":{\"compilationTarget\":{\"contracts/PaymentVaultV2.sol\":\"PaymentVaultV2\"},\"evmVersion\":\"cancun\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/\",\":@prb/math/=lib/prb-math/\",\":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts/\"],\"viaIR\":true},\"sources\":{\"contracts/IPaymentVaultV2.sol\":{\"keccak256\":\"0x4b4246ed4f9c7cb3d484821e4f9b82acfb3f0d1e95ae3bd64790df6d8ac40863\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2a5cd4ca0cde103b99f72ef27a9b5456bbd934f5ca11974cc4441328b1bff141\",\"dweb:/ipfs/QmfTkArPacPx5nwHS8f6ygVzDejVmzhyswteE2RupUNkS1\"]},\"contracts/MerklePaymentLib.sol\":{\"keccak256\":\"0x628a074eab87cfd7fbe2f29ff889a0fc97ee74933cd100962fde56fbefd48e83\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://cd817a18a982efef881258a2c1d5caefcef6e5c7466e1a34c4c6698231b0eb9b\",\"dweb:/ipfs/QmbRBbYViT1TxEUnsUpoDPa1YzC57vLfDvLg9Mf95z2WjY\"]},\"contracts/PaymentVaultV2.sol\":{\"keccak256\":\"0xf93009f884aac758031cdf2d4a69caa4d6b08fcea3ab9dfc38462aed05192ce4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://2a0f30c2a63371353818e218e0d7482c1b0d266dc330c7510d0b95fb8b2bfc0f\",\"dweb:/ipfs/QmeH7m2A3V8Zij22UXcU4XBqAQWTJSjWx7YJpoyEuh9Xt8\"]},\"contracts/Types.sol\":{\"keccak256\":\"0xa0c0268a03fff200e31f99b3ab15dcb7dce9ad162183aaeecffad51a93ed8a43\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://55308d1066e4f1379e66aa23719df23ae94104a9be0383fe7dec990f00897f6d\",\"dweb:/ipfs/QmWNasx7GsPjByWjD49JdwmjnDYMoJiXEEFeekr5w1g1y6\"]},\"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol\":{\"keccak256\":\"0xd5ea07362ab630a6a3dee4285a74cf2377044ca2e4be472755ad64d7c5d4b69d\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://da5e832b40fc5c3145d3781e2e5fa60ac2052c9d08af7e300dc8ab80c4343100\",\"dweb:/ipfs/QmTzf7N5ZUdh5raqtzbM11yexiUoLC9z3Ws632MCuycq1d\"]},\"lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol\":{\"keccak256\":\"0x0afcb7e740d1537b252cb2676f600465ce6938398569f09ba1b9ca240dde2dfc\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1c299900ac4ec268d4570ecef0d697a3013cd11a6eb74e295ee3fbc945056037\",\"dweb:/ipfs/Qmab9owJoxcA7vJT5XNayCMaUR1qxqj1NDzzisduwaJMcZ\"]},\"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol\":{\"keccak256\":\"0x1a6221315ce0307746c2c4827c125d821ee796c74a676787762f4778671d4f44\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://1bb2332a7ee26dd0b0de9b7fe266749f54820c99ab6a3bcb6f7e6b751d47ee2d\",\"dweb:/ipfs/QmcRWpaBeCYkhy68PR3B4AgD7asuQk7PwkWxrvJbZcikLF\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303\",\"dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV\"]},\"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol\":{\"keccak256\":\"0x304d732678032a9781ae85c8f204c8fba3d3a5e31c02616964e75cfdc5049098\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://299ced486011781dc98f638059678323c03079fefae1482abaa2135b22fa92d0\",\"dweb:/ipfs/QmbZNbcPTBxNvwChavN2kkZZs7xHhYL7mv51KrxMhsMs3j\"]},\"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617\",\"dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u\"]}},\"version\":1}","metadata":{"compiler":{"version":"0.8.28+commit.7893614a"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"contract IERC20","name":"_antToken","type":"address"},{"internalType":"uint256","name":"_batchLimit","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"type":"error","name":"AntTokenNull"},{"inputs":[],"type":"error","name":"BatchLimitExceeded"},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint8","name":"maxDepth","type":"uint8"}],"type":"error","name":"DepthTooLarge"},{"inputs":[],"type":"error","name":"InvalidInputLength"},{"inputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32"}],"type":"error","name":"PaymentAlreadyExists"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"type":"error","name":"SafeERC20FailedOperation"},{"inputs":[{"internalType":"uint256","name":"expected","type":"uint256"},{"internalType":"uint256","name":"actual","type":"uint256"}],"type":"error","name":"WrongPoolCount"},{"inputs":[{"internalType":"address","name":"rewardsAddress","type":"address","indexed":true},{"internalType":"uint256","name":"amount","type":"uint256","indexed":true},{"internalType":"bytes32","name":"quoteHash","type":"bytes32","indexed":true}],"type":"event","name":"DataPaymentMade","anonymous":false},{"inputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32","indexed":true},{"internalType":"uint8","name":"depth","type":"uint8","indexed":false},{"internalType":"uint256","name":"totalAmount","type":"uint256","indexed":false},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64","indexed":false}],"type":"event","name":"MerklePaymentMade","anonymous":false},{"inputs":[],"stateMutability":"view","type":"function","name":"CANDIDATES_PER_POOL","outputs":[{"internalType":"uint8","name":"","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"MAX_MERKLE_DEPTH","outputs":[{"internalType":"uint8","name":"","type":"uint8"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"antToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}]},{"inputs":[],"stateMutability":"view","type":"function","name":"batchLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}]},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"completedMerklePayments","outputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}]},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function","name":"completedPayments","outputs":[{"internalType":"bytes16","name":"rewardsAddress","type":"bytes16"},{"internalType":"uint128","name":"amount","type":"uint128"}]},{"inputs":[{"internalType":"bytes32","name":"winnerHash","type":"bytes32"}],"stateMutability":"view","type":"function","name":"getCompletedMerklePayment","outputs":[{"internalType":"struct CompletedMerklePayment","name":"","type":"tuple","components":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"},{"internalType":"struct PaidNode[]","name":"paidNodeAddresses","type":"tuple[]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint8","name":"poolIndex","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"}]}]}]},{"inputs":[{"internalType":"uint8","name":"depth","type":"uint8"},{"internalType":"struct PoolCommitment[]","name":"poolCommitments","type":"tuple[]","components":[{"internalType":"bytes32","name":"poolHash","type":"bytes32"},{"internalType":"struct CandidateNode[16]","name":"candidates","type":"tuple[16]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}]}]},{"internalType":"uint64","name":"merklePaymentTimestamp","type":"uint64"}],"stateMutability":"nonpayable","type":"function","name":"payForMerkleTree","outputs":[{"internalType":"bytes32","name":"winnerPoolHash","type":"bytes32"},{"internalType":"uint256","name":"totalAmount","type":"uint256"}]},{"inputs":[{"internalType":"struct DataPayment[]","name":"_payments","type":"tuple[]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"quoteHash","type":"bytes32"}]}],"stateMutability":"nonpayable","type":"function","name":"payForQuotes"},{"inputs":[{"internalType":"struct DataPayment[]","name":"_payments","type":"tuple[]","components":[{"internalType":"address","name":"rewardsAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"quoteHash","type":"bytes32"}]}],"stateMutability":"view","type":"function","name":"verifyPayment","outputs":[{"internalType":"struct PaymentVerificationResult[]","name":"","type":"tuple[]","components":[{"internalType":"bytes32","name":"quoteHash","type":"bytes32"},{"internalType":"uint256","name":"amountPaid","type":"uint256"},{"internalType":"bool","name":"isValid","type":"bool"}]}]}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{"CANDIDATES_PER_POOL()":{"notice":"Number of candidates per pool (fixed)"},"MAX_MERKLE_DEPTH()":{"notice":"Maximum supported Merkle tree depth"}},"version":1}},"settings":{"remappings":["@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/","@prb/math/=lib/prb-math/","erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/","forge-std/=lib/forge-std/src/","halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/","openzeppelin-contracts/=lib/openzeppelin-contracts/"],"optimizer":{"enabled":true,"runs":200},"metadata":{"bytecodeHash":"ipfs"},"compilationTarget":{"contracts/PaymentVaultV2.sol":"PaymentVaultV2"},"evmVersion":"cancun","libraries":{},"viaIR":true},"sources":{"contracts/IPaymentVaultV2.sol":{"keccak256":"0x4b4246ed4f9c7cb3d484821e4f9b82acfb3f0d1e95ae3bd64790df6d8ac40863","urls":["bzz-raw://2a5cd4ca0cde103b99f72ef27a9b5456bbd934f5ca11974cc4441328b1bff141","dweb:/ipfs/QmfTkArPacPx5nwHS8f6ygVzDejVmzhyswteE2RupUNkS1"],"license":"MIT"},"contracts/MerklePaymentLib.sol":{"keccak256":"0x628a074eab87cfd7fbe2f29ff889a0fc97ee74933cd100962fde56fbefd48e83","urls":["bzz-raw://cd817a18a982efef881258a2c1d5caefcef6e5c7466e1a34c4c6698231b0eb9b","dweb:/ipfs/QmbRBbYViT1TxEUnsUpoDPa1YzC57vLfDvLg9Mf95z2WjY"],"license":"MIT"},"contracts/PaymentVaultV2.sol":{"keccak256":"0xf93009f884aac758031cdf2d4a69caa4d6b08fcea3ab9dfc38462aed05192ce4","urls":["bzz-raw://2a0f30c2a63371353818e218e0d7482c1b0d266dc330c7510d0b95fb8b2bfc0f","dweb:/ipfs/QmeH7m2A3V8Zij22UXcU4XBqAQWTJSjWx7YJpoyEuh9Xt8"],"license":"MIT"},"contracts/Types.sol":{"keccak256":"0xa0c0268a03fff200e31f99b3ab15dcb7dce9ad162183aaeecffad51a93ed8a43","urls":["bzz-raw://55308d1066e4f1379e66aa23719df23ae94104a9be0383fe7dec990f00897f6d","dweb:/ipfs/QmWNasx7GsPjByWjD49JdwmjnDYMoJiXEEFeekr5w1g1y6"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/IERC1363.sol":{"keccak256":"0xd5ea07362ab630a6a3dee4285a74cf2377044ca2e4be472755ad64d7c5d4b69d","urls":["bzz-raw://da5e832b40fc5c3145d3781e2e5fa60ac2052c9d08af7e300dc8ab80c4343100","dweb:/ipfs/QmTzf7N5ZUdh5raqtzbM11yexiUoLC9z3Ws632MCuycq1d"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol":{"keccak256":"0x0afcb7e740d1537b252cb2676f600465ce6938398569f09ba1b9ca240dde2dfc","urls":["bzz-raw://1c299900ac4ec268d4570ecef0d697a3013cd11a6eb74e295ee3fbc945056037","dweb:/ipfs/Qmab9owJoxcA7vJT5XNayCMaUR1qxqj1NDzzisduwaJMcZ"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol":{"keccak256":"0x1a6221315ce0307746c2c4827c125d821ee796c74a676787762f4778671d4f44","urls":["bzz-raw://1bb2332a7ee26dd0b0de9b7fe266749f54820c99ab6a3bcb6f7e6b751d47ee2d","dweb:/ipfs/QmcRWpaBeCYkhy68PR3B4AgD7asuQk7PwkWxrvJbZcikLF"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol":{"keccak256":"0x74ed01eb66b923d0d0cfe3be84604ac04b76482a55f9dd655e1ef4d367f95bc2","urls":["bzz-raw://5282825a626cfe924e504274b864a652b0023591fa66f06a067b25b51ba9b303","dweb:/ipfs/QmeCfPykghhMc81VJTrHTC7sF6CRvaA1FXVq2pJhwYp1dV"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol":{"keccak256":"0x304d732678032a9781ae85c8f204c8fba3d3a5e31c02616964e75cfdc5049098","urls":["bzz-raw://299ced486011781dc98f638059678323c03079fefae1482abaa2135b22fa92d0","dweb:/ipfs/QmbZNbcPTBxNvwChavN2kkZZs7xHhYL7mv51KrxMhsMs3j"],"license":"MIT"},"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol":{"keccak256":"0x8891738ffe910f0cf2da09566928589bf5d63f4524dd734fd9cedbac3274dd5c","urls":["bzz-raw://971f954442df5c2ef5b5ebf1eb245d7105d9fbacc7386ee5c796df1d45b21617","dweb:/ipfs/QmadRjHbkicwqwwh61raUEapaVEtaLMcYbQZWs9gUkgj3u"],"license":"MIT"}},"version":1},"id":2} \ No newline at end of file diff --git a/contracts/IMerklePaymentVault.sol b/contracts/IMerklePaymentVault.sol deleted file mode 100644 index ce90956..0000000 --- a/contracts/IMerklePaymentVault.sol +++ /dev/null @@ -1,220 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -/// @title IMerklePaymentVault -/// @notice Interface for the Merkle Batch Payment Vault -/// @dev Handles batch payments for Merkle tree storage where multiple data chunks -/// are paid for in a single transaction using a fair median pricing mechanism -interface IMerklePaymentVault { - // ============ Types ============ - - struct MerklePayment { - uint256 treeDepth; - uint256 amount; - uint256 paymentTstamp; - address[] recipients; - } - - struct DataPayment { - address rewardsAddress; - uint256 amount; - bytes32 quoteHash; - } - - struct Payment { - bytes16 rewardsAddress; - uint128 amount; - } - - /// @notice Types of data that can be stored - enum DataType { - GraphEntry, - Scratchpad, - Chunk, - Pointer - } - - /// @notice Record count per data type - struct Record { - DataType dataType; - uint256 records; - } - - /// @notice Quoting metrics provided by candidate nodes - struct QuotingMetrics { - DataType dataType; - uint256 closeRecordsStored; - Record[] recordsPerType; - } - - /// @notice One candidate node with its metrics - struct CandidateNode { - address rewardsAddress; - QuotingMetrics metrics; - } - - /// @notice Pool commitment with 16 candidates - struct PoolCommitment { - bytes32 poolHash; // Cryptographic commitment to full pool data - CandidateNode[16] candidates; // Fixed size: always 16 - } - - /// @notice Packed candidate node for compact calldata (v2) - /// @dev dataTypeAndTotalCostUnit packs (totalCostUnit << 8) | dataType - struct CandidateNodePacked { - address rewardsAddress; - uint256 dataTypeAndTotalCostUnit; - } - - /// @notice Packed pool commitment with 16 packed candidates (v2) - struct PoolCommitmentPacked { - bytes32 poolHash; // Cryptographic commitment to full pool data - CandidateNodePacked[16] candidates; // Fixed size: always 16 - } - - /// @notice Individual paid node record - struct PaidNode { - address rewardsAddress; - uint8 poolIndex; // Index in winner pool (0-15) - } - - /// @notice Payment information stored on-chain - struct PaymentInfo { - uint8 depth; // Merkle tree depth - uint64 merklePaymentTimestamp; // Payment timestamp - PaidNode[] paidNodeAddresses; // List of paid nodes - } - - struct CostUnit { - uint256 costUnit; - uint256 costUnitMax; - } - - struct PaymentVerification { - QuotingMetrics metrics; - DataPayment dataPayment; - } - - struct PaymentVerificationResult { - bytes32 quoteHash; - uint256 amountPaid; - bool isValid; - } - - // ============ Events ============ - - /// @notice Emitted when a Merkle batch payment is made - /// @param winnerPoolHash Hash of the selected winner pool - /// @param depth Tree depth - /// @param totalAmount Total tokens paid to winners - /// @param merklePaymentTimestamp Client-provided timestamp - event MerklePaymentMade( - bytes32 indexed winnerPoolHash, uint8 depth, uint256 totalAmount, uint64 merklePaymentTimestamp - ); - - /// @notice Emitted when a data payment is made (legacy event) - /// @param root Merkle root - /// @param treeDepth Tree depth - /// @param amount Payment amount - event DataPaymentMade(bytes32 indexed root, uint256 indexed treeDepth, uint256 indexed amount); - - // ============ Errors ============ - - error InvalidRoot(); - - error RootAlreadyPaid(); - - error InvalidAmount(); - - error InvalidTreeDepth(); - - error InvalidRecipientsCount(); - - error AntTokenNull(); - error BatchLimitExceeded(); - - error InvalidInputLength(); - - error PriceFeedNull(); - - error InvalidChainlinkPrice(); - - error SequencerDown(); - error GracePeriodNotOver(); - - error InvalidQuoteHash(); - - error DepthTooLarge(uint8 depth, uint8 max); - - error WrongPoolCount(uint256 expected, uint256 got); - - error PaymentAlreadyExists(bytes32 poolHash); - - error PaymentNotFound(bytes32 poolHash); - - error WrongCandidateCount(uint256 poolIdx, uint256 expected, uint256 got); - - // ============ View Functions ============ - - /// @notice Returns the ANT token contract address - /// @return The IERC20 token contract - function antToken() external view returns (IERC20); - - /// @notice Returns payment info for a winner pool hash - /// @param winnerPoolHash Hash of the winner pool - /// @return depth Tree depth - /// @return merklePaymentTimestamp Payment timestamp - function payments(bytes32 winnerPoolHash) external view returns (uint8 depth, uint64 merklePaymentTimestamp); - - /// @notice Maximum supported Merkle tree depth - /// @return Maximum depth value - function MAX_MERKLE_DEPTH() external view returns (uint8); - - /// @notice Number of candidates per pool - /// @return Number of candidates (always 16) - function CANDIDATES_PER_POOL() external view returns (uint8); - - /// @notice Get payment info by winner pool hash - /// @param winnerPoolHash Hash returned from payForMerkleTree - /// @return info Payment information stored on-chain - function getPaymentInfo(bytes32 winnerPoolHash) external view returns (PaymentInfo memory info); - - /// @notice Estimate the cost of a Merkle tree payment without executing it - /// @dev This is a view function (0 gas) that runs the same pricing logic as - /// payForMerkleTree but returns only the estimated cost - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return totalAmount Estimated total tokens that would be paid - function estimateMerkleTreeCost( - uint8 depth, - PoolCommitment[] calldata poolCommitments, - uint64 merklePaymentTimestamp - ) external view returns (uint256 totalAmount); - - // ============ State-Changing Functions ============ - - /// @notice Pay for Merkle tree batch - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return winnerPoolHash Hash of selected winner pool - /// @return totalAmount Total tokens paid to winners - function payForMerkleTree(uint8 depth, PoolCommitment[] calldata poolCommitments, uint64 merklePaymentTimestamp) - external - returns (bytes32 winnerPoolHash, uint256 totalAmount); - - /// @notice Pay for Merkle tree batch using packed calldata (v2) - /// @dev Uses packed data structures for smaller calldata and lower gas costs. - /// The dataTypeAndTotalCostUnit field packs (totalCostUnit << 8) | dataType. - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of packed pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return winnerPoolHash Hash of selected winner pool - /// @return totalAmount Total tokens paid to winners - function payForMerkleTree2(uint8 depth, PoolCommitmentPacked[] calldata poolCommitments, uint64 merklePaymentTimestamp) - external - returns (bytes32 winnerPoolHash, uint256 totalAmount); -} diff --git a/contracts/IPaymentVaultV2.sol b/contracts/IPaymentVaultV2.sol new file mode 100644 index 0000000..27c5792 --- /dev/null +++ b/contracts/IPaymentVaultV2.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import { + PoolCommitment, + DataPayment, + PaymentVerificationResult +} from "./Types.sol"; + +interface IPaymentVaultV2 { + error AntTokenNull(); + error BatchLimitExceeded(); + error InvalidInputLength(); + error DepthTooLarge(uint8 depth, uint8 maxDepth); + error WrongPoolCount(uint256 expected, uint256 actual); + error PaymentAlreadyExists(bytes32 winnerPoolHash); + + event DataPaymentMade( + address indexed rewardsAddress, + uint256 indexed amount, + bytes32 indexed quoteHash + ); + + /// Emitted when a Merkle batch payment is made + event MerklePaymentMade( + bytes32 indexed winnerPoolHash, + uint8 depth, + uint256 totalAmount, + uint64 merklePaymentTimestamp + ); + + function payForMerkleTree( + uint8 depth, + PoolCommitment[] calldata poolCommitments, + uint64 merklePaymentTimestamp + ) external returns (bytes32 winnerPoolHash, uint256 totalAmount); + + function payForQuotes(DataPayment[] calldata _payments) external; + + function verifyPayment( + DataPayment[] calldata _payments + ) external view returns (PaymentVerificationResult[] memory); +} diff --git a/contracts/MerklePaymentLib.sol b/contracts/MerklePaymentLib.sol new file mode 100644 index 0000000..0b11472 --- /dev/null +++ b/contracts/MerklePaymentLib.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +library MerklePaymentLib { + /// Calculate expected number of reward pools: 2^ceil(depth/2) + function expectedRewardPools(uint8 depth) internal pure returns (uint256) { + uint8 halfDepth = (depth + 1) / 2; // ceil division + return 1 << halfDepth; // 2^halfDepth + } + + /// Select winner pool using deterministic pseudo-randomness + function selectWinnerPool( + uint256 poolCount, + address sender, + uint64 timestamp + ) internal view returns (uint256) { + bytes32 seed = keccak256( + abi.encodePacked( + block.prevrandao, + block.timestamp, + sender, + timestamp + ) + ); + return uint256(seed) % poolCount; + } + + function selectWinnerNodes( + uint8 depth, + bytes32 poolHash, + uint64 timestamp + ) internal view returns (uint8[] memory) { + uint8[] memory winners = new uint8[](depth); + uint8[16] memory indices = [ + 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 + ]; + + bytes32 seed = keccak256( + abi.encodePacked(block.prevrandao, poolHash, timestamp) + ); + + uint8 byteIndex = 0; + + for (uint8 i = 0; i < depth; ++i) { + // Expand seed only when we run out of bytes + if (byteIndex == 32) { + seed = keccak256(abi.encodePacked(seed)); + byteIndex = 0; + } + + // Use a single byte as random number + uint8 rnd = uint8(seed[byteIndex++]); + + uint8 j = uint8(i + (rnd % (16 - i))); + + // swap + (indices[i], indices[j]) = (indices[j], indices[i]); + + winners[i] = indices[i]; + } + + return winners; + } + + function median16(uint256[16] memory a) internal pure returns (uint256) { + uint256 left = 0; + uint256 right = 15; // fixed length - 1 + uint256 k = 8; // median index for 16 elements + + while (true) { + uint256 pivot = a[(left + right) >> 1]; + uint256 i = left; + uint256 j = right; + + // Partition + while (i <= j) { + while (a[i] < pivot) i++; + + while (pivot < a[j]) j--; + + if (i <= j) { + (a[i], a[j]) = (a[j], a[i]); + unchecked { + i++; + j--; + } + } + } + + // Narrow search region + if (k <= j) { + right = j; + } else if (i <= k) { + left = i; + } else { + return a[k]; + } + } + revert(); + } +} diff --git a/contracts/MerklePaymentVault.sol b/contracts/MerklePaymentVault.sol deleted file mode 100644 index 1aedabd..0000000 --- a/contracts/MerklePaymentVault.sol +++ /dev/null @@ -1,493 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 -pragma solidity ^0.8.20; - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@prb/math/src/SD59x18.sol"; -import "./IMerklePaymentVault.sol"; - -/// Merkle Batch Payment Vault -/// -/// Handles batch payments for Merkle tree storage where multiple data chunks -/// are paid for in a single transaction. Uses a fair median pricing mechanism -/// based on candidate node metrics. -contract MerklePaymentVault is IMerklePaymentVault { - // ============ State ============ - - /// ANT token contract - IERC20 public immutable override antToken; - - /// Payment info indexed by winner pool hash - mapping(bytes32 => PaymentInfo) public override payments; - - /// Maximum supported Merkle tree depth - uint8 public constant override MAX_MERKLE_DEPTH = 12; - - /// Number of candidates per pool (fixed) - uint8 public constant override CANDIDATES_PER_POOL = 16; - - // ============ Pricing Constants ============ - - /// Precision for fixed-point calculations (1e18) - uint256 public constant PRECISION = 1e18; - - /// Constant ANT price for local testnet (1e18) - /// In production, this would be fetched from Chainlink - uint256 public constant ANT_PRICE = 1e18; - - /// Scaling factor for the pricing formula - uint256 public scalingFactor = 1e18; - - /// Minimum price floor (3 to match regular payment baseline) - uint256 public minPrice = 3; - - /// Maximum cost unit for capacity calculation - uint256 public maxCostUnit = 1e24; - - /// Cost unit per data type - mapping(DataType => uint256) public costUnitPerDataType; - - // ============ Constructor ============ - - constructor(address _antToken) { - require(_antToken != address(0), "Invalid token address"); - antToken = IERC20(_antToken); - - // Initialize cost units per data type (matching prod contract) - costUnitPerDataType[DataType.GraphEntry] = 1; - costUnitPerDataType[DataType.Scratchpad] = 100; - costUnitPerDataType[DataType.Chunk] = 10; - costUnitPerDataType[DataType.Pointer] = 20; - } - - // ============ Main Functions ============ - - /// Pay for Merkle tree batch - /// - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return winnerPoolHash Hash of selected winner pool - /// @return totalAmount Total tokens paid to winners - function payForMerkleTree(uint8 depth, PoolCommitment[] calldata poolCommitments, uint64 merklePaymentTimestamp) - external - override - returns (bytes32 winnerPoolHash, uint256 totalAmount) - { - // Validate depth - if (depth > MAX_MERKLE_DEPTH) { - revert DepthTooLarge(depth, MAX_MERKLE_DEPTH); - } - - // Validate pool count: 2^ceil(depth/2) - uint256 expectedPools = _expectedRewardPools(depth); - if (poolCommitments.length != expectedPools) { - revert WrongPoolCount(expectedPools, poolCommitments.length); - } - - // Validate each pool has exactly CANDIDATES_PER_POOL candidates - for (uint256 i = 0; i < poolCommitments.length; i++) { - if (poolCommitments[i].candidates.length != CANDIDATES_PER_POOL) { - revert WrongCandidateCount(i, CANDIDATES_PER_POOL, poolCommitments[i].candidates.length); - } - } - - // Select winner pool deterministically - uint256 winnerPoolIdx = _selectWinnerPool(poolCommitments.length, msg.sender, merklePaymentTimestamp); - PoolCommitment calldata winnerPool = poolCommitments[winnerPoolIdx]; - winnerPoolHash = winnerPool.poolHash; - - // Check if payment already exists for this pool - if (payments[winnerPoolHash].depth != 0) { - revert PaymentAlreadyExists(winnerPoolHash); - } - - // Calculate median price from all CANDIDATES_PER_POOL candidates - uint256 medianPrice = _calculateMedianPrice(winnerPool.candidates); - uint256 numChunks = 1 << depth; // 2^depth chunks in the merkle tree - totalAmount = medianPrice * numChunks; - - // Select depth winner nodes from pool - uint8[] memory winnerIndices = _selectWinnerNodes(depth, winnerPoolHash, merklePaymentTimestamp); - - // Initialize storage for payment info - PaymentInfo storage info = payments[winnerPoolHash]; - info.depth = depth; - info.merklePaymentTimestamp = merklePaymentTimestamp; - - // Transfer tokens to winners and store payment records - // depth winners share the total amount (each gets totalAmount/depth) - uint256 amountPerNode = totalAmount / depth; - for (uint256 i = 0; i < depth; i++) { - uint8 nodeIdx = winnerIndices[i]; - address rewardsAddress = winnerPool.candidates[nodeIdx].rewardsAddress; - - // Transfer tokens to winner - antToken.transferFrom(msg.sender, rewardsAddress, amountPerNode); - - // Store paid node info - info.paidNodeAddresses.push(PaidNode({rewardsAddress: rewardsAddress, poolIndex: nodeIdx})); - } - - emit MerklePaymentMade(winnerPoolHash, depth, totalAmount, merklePaymentTimestamp); - - return (winnerPoolHash, totalAmount); - } - - /// Pay for Merkle tree batch using packed calldata (v2) - /// - /// Uses packed data structures where each candidate's data type and total cost unit - /// are encoded into a single uint256 for smaller calldata and lower gas costs. - /// - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of packed pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return winnerPoolHash Hash of selected winner pool - /// @return totalAmount Total tokens paid to winners - function payForMerkleTree2(uint8 depth, PoolCommitmentPacked[] calldata poolCommitments, uint64 merklePaymentTimestamp) - external - override - returns (bytes32 winnerPoolHash, uint256 totalAmount) - { - // Validate depth - if (depth > MAX_MERKLE_DEPTH) { - revert DepthTooLarge(depth, MAX_MERKLE_DEPTH); - } - - // Validate pool count: 2^ceil(depth/2) - uint256 expectedPools = _expectedRewardPools(depth); - if (poolCommitments.length != expectedPools) { - revert WrongPoolCount(expectedPools, poolCommitments.length); - } - - // Select winner pool deterministically - uint256 winnerPoolIdx = _selectWinnerPool(poolCommitments.length, msg.sender, merklePaymentTimestamp); - PoolCommitmentPacked calldata winnerPool = poolCommitments[winnerPoolIdx]; - winnerPoolHash = winnerPool.poolHash; - - // Check if payment already exists for this pool - if (payments[winnerPoolHash].depth != 0) { - revert PaymentAlreadyExists(winnerPoolHash); - } - - // Calculate median price from all CANDIDATES_PER_POOL packed candidates - uint256 medianPrice = _calculateMedianPricePacked(winnerPool.candidates); - uint256 numChunks = 1 << depth; // 2^depth chunks in the merkle tree - totalAmount = medianPrice * numChunks; - - // Select depth winner nodes from pool - uint8[] memory winnerIndices = _selectWinnerNodes(depth, winnerPoolHash, merklePaymentTimestamp); - - // Initialize storage for payment info - PaymentInfo storage info = payments[winnerPoolHash]; - info.depth = depth; - info.merklePaymentTimestamp = merklePaymentTimestamp; - - // Transfer tokens to winners and store payment records - // depth winners share the total amount (each gets totalAmount/depth) - uint256 amountPerNode = totalAmount / depth; - for (uint256 i = 0; i < depth; i++) { - uint8 nodeIdx = winnerIndices[i]; - address rewardsAddress = winnerPool.candidates[nodeIdx].rewardsAddress; - - // Transfer tokens to winner - antToken.transferFrom(msg.sender, rewardsAddress, amountPerNode); - - // Store paid node info - info.paidNodeAddresses.push(PaidNode({rewardsAddress: rewardsAddress, poolIndex: nodeIdx})); - } - - emit MerklePaymentMade(winnerPoolHash, depth, totalAmount, merklePaymentTimestamp); - - return (winnerPoolHash, totalAmount); - } - - /// Estimate the cost of a Merkle tree payment without executing it - /// - /// This is a view function (0 gas) that runs the same pricing logic as - /// payForMerkleTree but returns only the estimated cost without executing payment. - /// - /// @param depth Tree depth (determines number of nodes paid) - /// @param poolCommitments Array of pool commitments (2^ceil(depth/2)) - /// @param merklePaymentTimestamp Client-provided timestamp - /// @return totalAmount Estimated total tokens that would be paid - function estimateMerkleTreeCost( - uint8 depth, - PoolCommitment[] calldata poolCommitments, - uint64 merklePaymentTimestamp - ) external view override returns (uint256 totalAmount) { - // Validate depth - if (depth > MAX_MERKLE_DEPTH) { - revert DepthTooLarge(depth, MAX_MERKLE_DEPTH); - } - - // Validate pool count: 2^ceil(depth/2) - uint256 expectedPools = _expectedRewardPools(depth); - if (poolCommitments.length != expectedPools) { - revert WrongPoolCount(expectedPools, poolCommitments.length); - } - - // Validate each pool has exactly CANDIDATES_PER_POOL candidates - for (uint256 i = 0; i < poolCommitments.length; i++) { - if (poolCommitments[i].candidates.length != CANDIDATES_PER_POOL) { - revert WrongCandidateCount(i, CANDIDATES_PER_POOL, poolCommitments[i].candidates.length); - } - } - - // Select winner pool deterministically (same logic as payForMerkleTree) - uint256 winnerPoolIdx = _selectWinnerPool(poolCommitments.length, msg.sender, merklePaymentTimestamp); - PoolCommitment calldata winnerPool = poolCommitments[winnerPoolIdx]; - - // Calculate median price from all CANDIDATES_PER_POOL candidates - uint256 medianPrice = _calculateMedianPrice(winnerPool.candidates); - uint256 numChunks = 1 << depth; // 2^depth chunks in the merkle tree - totalAmount = medianPrice * numChunks; - - return totalAmount; - } - - /// Get payment info by winner pool hash - /// - /// @param winnerPoolHash Hash returned from payForMerkleTree - /// @return info Payment information stored on-chain - function getPaymentInfo(bytes32 winnerPoolHash) external view override returns (PaymentInfo memory info) { - info = payments[winnerPoolHash]; - if (info.depth == 0) { - revert PaymentNotFound(winnerPoolHash); - } - return info; - } - - // ============ Internal Functions ============ - - /// Calculate expected number of reward pools: 2^ceil(depth/2) - function _expectedRewardPools(uint8 depth) internal pure returns (uint256) { - uint8 halfDepth = (depth + 1) / 2; // ceil division - return 1 << halfDepth; // 2^halfDepth - } - - /// Select winner pool using deterministic pseudo-randomness - function _selectWinnerPool(uint256 poolCount, address sender, uint64 timestamp) internal view returns (uint256) { - bytes32 seed = keccak256(abi.encodePacked(block.prevrandao, block.timestamp, sender, timestamp)); - return uint256(seed) % poolCount; - } - - /// Calculate median price from CANDIDATES_PER_POOL packed candidate quotes (v2) - function _calculateMedianPricePacked(CandidateNodePacked[16] calldata candidates) internal view returns (uint256) { - // Get quote for each candidate - uint256[16] memory quotes; - for (uint256 i = 0; i < 16; i++) { - quotes[i] = _getQuotePacked(candidates[i].dataTypeAndTotalCostUnit); - } - - // Sort quotes - _sortQuotes(quotes); - - // Return median (average of 8th and 9th elements, 0-indexed: [7] and [8]) - return (quotes[7] + quotes[8]) / 2; - } - - /// Calculate quote for a single node from packed data (v2) - /// - /// Unpacks the dataTypeAndTotalCostUnit field and applies the same pricing formula as _getQuote. - /// Format: packed = (totalCostUnit << 8) | dataType - function _getQuotePacked(uint256 dataTypeAndTotalCostUnit) internal view returns (uint256) { - // Unpack: dataType is lower 8 bits, totalCostUnit is upper bits - DataType dataType = DataType(uint8(dataTypeAndTotalCostUnit & 0xFF)); - uint256 totalCostUnit = dataTypeAndTotalCostUnit >> 8; - - uint256 lowerBound = _getBound(totalCostUnit); - uint256 upperBound = _getBound(totalCostUnit + costUnitPerDataType[dataType]); - - // Edge cases: if bounds are equal or at precision, return minPrice - if (lowerBound == upperBound || lowerBound == PRECISION || upperBound == PRECISION) { - return minPrice; - } - - // Calculate |rUpper - 1| and |rLower - 1| for logarithm - uint256 upperDiff = _absDiff(upperBound, PRECISION); - uint256 lowerDiff = _absDiff(lowerBound, PRECISION); - - // Avoid log(0) - return minPrice if either diff is 0 - if (upperDiff == 0 || lowerDiff == 0) { - return minPrice; - } - - // Calculate ln|rUpper - 1| - ln|rLower - 1| using PRB Math - int256 logUpper = _calculateLn(upperDiff); - int256 logLower = _calculateLn(lowerDiff); - int256 logDiff = logUpper - logLower; - - // Calculate linear part: rUpper - rLower - uint256 linearPart = _absDiff(upperBound, lowerBound); - - // Formula components: - // partOne = (-s/ANT) * logDiff - // partTwo = pMin * linearPart / PRECISION - // partThree = linearPart / ANT - int256 partOne = (-int256(scalingFactor) * logDiff) / int256(ANT_PRICE * PRECISION); - uint256 partTwo = (linearPart * minPrice) / PRECISION; - uint256 partThree = linearPart / ANT_PRICE; - - // Combine: price = partOne + partTwo - partThree - int256 price = partOne + int256(partTwo) - int256(partThree); - - // Return price, with minPrice as floor - if (price <= 0) { - return minPrice; - } - return uint256(price); - } - - /// Calculate median price from CANDIDATES_PER_POOL candidate quotes - function _calculateMedianPrice(CandidateNode[16] calldata candidates) internal view returns (uint256) { - // Get quote for each candidate - uint256[16] memory quotes; - for (uint256 i = 0; i < 16; i++) { - quotes[i] = _getQuote(candidates[i].metrics); - } - - // Sort quotes - _sortQuotes(quotes); - - // Return median (average of 8th and 9th elements, 0-indexed: [7] and [8]) - return (quotes[7] + quotes[8]) / 2; - } - - /// Calculate quote for a single node based on metrics - /// - /// Uses the production pricing formula: - /// price = (-s/ANT) * (ln|rUpper - 1| - ln|rLower - 1|) + pMin*(rUpper - rLower) - (rUpper - rLower)/ANT - /// - /// Where: - /// - s = scalingFactor - /// - ANT = antPrice - /// - pMin = minPrice - /// - rLower = totalCostUnit / maxCostUnit - /// - rUpper = (totalCostUnit + costUnitPerDataType[dataType]) / maxCostUnit - function _getQuote(QuotingMetrics calldata metrics) internal view returns (uint256) { - uint256 totalCostUnit = _getTotalCostUnit(metrics); - uint256 lowerBound = _getBound(totalCostUnit); - uint256 upperBound = _getBound(totalCostUnit + costUnitPerDataType[metrics.dataType]); - - // Edge cases: if bounds are equal or at precision, return minPrice - if (lowerBound == upperBound || lowerBound == PRECISION || upperBound == PRECISION) { - return minPrice; - } - - // Calculate |rUpper - 1| and |rLower - 1| for logarithm - uint256 upperDiff = _absDiff(upperBound, PRECISION); - uint256 lowerDiff = _absDiff(lowerBound, PRECISION); - - // Avoid log(0) - return minPrice if either diff is 0 - if (upperDiff == 0 || lowerDiff == 0) { - return minPrice; - } - - // Calculate ln|rUpper - 1| - ln|rLower - 1| using PRB Math - int256 logUpper = _calculateLn(upperDiff); - int256 logLower = _calculateLn(lowerDiff); - int256 logDiff = logUpper - logLower; - - // Calculate linear part: rUpper - rLower - uint256 linearPart = _absDiff(upperBound, lowerBound); - - // Formula components: - // partOne = (-s/ANT) * logDiff - // partTwo = pMin * linearPart / PRECISION - // partThree = linearPart / ANT - int256 partOne = (-int256(scalingFactor) * logDiff) / int256(ANT_PRICE * PRECISION); - uint256 partTwo = (linearPart * minPrice) / PRECISION; - uint256 partThree = linearPart / ANT_PRICE; - - // Combine: price = partOne + partTwo - partThree - int256 price = partOne + int256(partTwo) - int256(partThree); - - // Return price, with minPrice as floor - if (price <= 0) { - return minPrice; - } - return uint256(price); - } - - /// Calculate total cost unit from metrics - function _getTotalCostUnit(QuotingMetrics calldata metrics) internal view returns (uint256) { - uint256 total = 0; - for (uint256 i = 0; i < metrics.recordsPerType.length; i++) { - Record calldata record = metrics.recordsPerType[i]; - total += costUnitPerDataType[record.dataType] * record.records; - } - return total; - } - - /// Calculate bound ratio: value / maxCostUnit - function _getBound(uint256 value) internal view returns (uint256) { - if (maxCostUnit == 0) { - return 0; - } - return (value * PRECISION) / maxCostUnit; - } - - /// Calculate absolute difference between two values - function _absDiff(uint256 a, uint256 b) internal pure returns (uint256) { - if (a >= b) { - return a - b; - } - return b - a; - } - - /// Calculate natural logarithm using PRB Math - function _calculateLn(uint256 x) internal pure returns (int256) { - if (x == 0) { - revert("ln(0) undefined"); - } - // Convert to SD59x18 (scaled by 1e18) - SD59x18 value = sd(int256(x)); - SD59x18 result = ln(value); - return result.unwrap(); - } - - /// Sort array of CANDIDATES_PER_POOL quotes using insertion sort (efficient for small arrays) - function _sortQuotes(uint256[16] memory quotes) internal pure { - for (uint256 i = 1; i < 16; i++) { - uint256 key = quotes[i]; - uint256 j = i; - while (j > 0 && quotes[j - 1] > key) { - quotes[j] = quotes[j - 1]; - j--; - } - quotes[j] = key; - } - } - - /// Select depth winner nodes from pool deterministically - function _selectWinnerNodes(uint8 depth, bytes32 poolHash, uint64 timestamp) - internal - view - returns (uint8[] memory) - { - uint8[] memory winners = new uint8[](depth); - bool[16] memory selected; - - bytes32 seed = keccak256(abi.encodePacked(block.prevrandao, poolHash, timestamp)); - - uint256 selectedCount = 0; - uint256 attempts = 0; - - // Select unique random indices - while (selectedCount < depth && attempts < 100) { - seed = keccak256(abi.encodePacked(seed, attempts)); - uint8 idx = uint8(uint256(seed) % 16); - - if (!selected[idx]) { - selected[idx] = true; - winners[selectedCount] = idx; - selectedCount++; - } - attempts++; - } - - require(selectedCount == depth, "Failed to select enough winners"); - - return winners; - } -} diff --git a/contracts/PaymentVaultV2.sol b/contracts/PaymentVaultV2.sol new file mode 100644 index 0000000..8feb6b1 --- /dev/null +++ b/contracts/PaymentVaultV2.sol @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import { + PaidNode, + CompletedMerklePayment, + CompletedPayment, + CandidateNode, + PoolCommitment, + DataPayment, + PaymentVerificationResult +} from "./Types.sol"; + +import {MerklePaymentLib} from "./MerklePaymentLib.sol"; +import {IPaymentVaultV2} from "./IPaymentVaultV2.sol"; + +/// @title PaymentVaultV2 +/// @notice Unified payment vault for both single-node and merkle batch payments. +/// No proxy, no Ownable — for local Anvil testing. +/// Nodes calculate their own prices as (chunks_stored / 6000)^2. +contract PaymentVaultV2 is IPaymentVaultV2 { + using SafeERC20 for IERC20; + + IERC20 public antToken; + + uint256 public batchLimit; + + mapping(bytes32 => CompletedPayment) public completedPayments; + + // mapping from winner hash to payment + mapping(bytes32 => CompletedMerklePayment) public completedMerklePayments; + + /// Maximum supported Merkle tree depth + uint8 public constant MAX_MERKLE_DEPTH = 12; + + /// Number of candidates per pool (fixed) + uint8 public constant CANDIDATES_PER_POOL = 16; + + constructor(IERC20 _antToken, uint256 _batchLimit) { + if (address(_antToken) == address(0)) { + revert AntTokenNull(); + } + + antToken = _antToken; + batchLimit = _batchLimit; + } + + function payForMerkleTree( + uint8 depth, + PoolCommitment[] calldata poolCommitments, + uint64 merklePaymentTimestamp + ) external returns (bytes32 winnerPoolHash, uint256 totalAmount) { + // check that the depth is less than max allowed depth + if (depth > MAX_MERKLE_DEPTH) { + revert DepthTooLarge(depth, MAX_MERKLE_DEPTH); + } + + // validate pool count: 2^ceil(depth/2) + uint256 expectedPools = MerklePaymentLib.expectedRewardPools(depth); + if (poolCommitments.length != expectedPools) { + revert WrongPoolCount(expectedPools, poolCommitments.length); + } + + // select winner + uint256 winnerPoolIdx = MerklePaymentLib.selectWinnerPool( + poolCommitments.length, + msg.sender, + merklePaymentTimestamp + ); + PoolCommitment memory winnerPool = poolCommitments[winnerPoolIdx]; + winnerPoolHash = winnerPool.poolHash; + + // verify unique payment + if (completedMerklePayments[winnerPoolHash].depth != 0) { + revert PaymentAlreadyExists(winnerPoolHash); + } + + uint256[CANDIDATES_PER_POOL] memory quotes; + for (uint256 i = 0; i < CANDIDATES_PER_POOL; i++) { + quotes[i] = winnerPool.candidates[i].amount; + } + + totalAmount = MerklePaymentLib.median16(quotes) * (1 << depth); + + // select winner nodes + uint8[] memory winnerIndices = MerklePaymentLib.selectWinnerNodes( + depth, + winnerPoolHash, + merklePaymentTimestamp + ); + + CompletedMerklePayment storage info = completedMerklePayments[ + winnerPoolHash + ]; + info.depth = depth; + info.merklePaymentTimestamp = merklePaymentTimestamp; + + PaidNode[] memory result = new PaidNode[](depth); + + // transfer payments + uint256 amountPerNode = totalAmount / depth; + + for (uint256 i = 0; i < depth; i++) { + uint8 nodeIdx = winnerIndices[i]; + address rewardsAddress = winnerPool + .candidates[nodeIdx] + .rewardsAddress; + + antToken.safeTransferFrom( + msg.sender, + rewardsAddress, + amountPerNode + ); + + result[i] = PaidNode({ + rewardsAddress: rewardsAddress, + poolIndex: nodeIdx, + amount: amountPerNode + }); + } + + info.paidNodeAddresses = result; + + emit MerklePaymentMade( + winnerPoolHash, + depth, + totalAmount, + merklePaymentTimestamp + ); + + return (winnerPoolHash, totalAmount); + } + + function payForQuotes(DataPayment[] calldata _payments) external { + uint256 paymentsLen = _payments.length; + + if (paymentsLen > batchLimit) { + revert BatchLimitExceeded(); + } + + for (uint256 i = 0; i < paymentsLen; i++) { + DataPayment calldata dataPayment = _payments[i]; + + if (dataPayment.quoteHash == bytes32(0)) { + antToken.safeTransferFrom( + msg.sender, + dataPayment.rewardsAddress, + dataPayment.amount + ); + continue; + } + + antToken.safeTransferFrom( + msg.sender, + dataPayment.rewardsAddress, + dataPayment.amount + ); + + completedPayments[dataPayment.quoteHash] = CompletedPayment({ + rewardsAddress: getFirst16Bytes(dataPayment.rewardsAddress), + amount: uint128(dataPayment.amount) + }); + + emit DataPaymentMade( + dataPayment.rewardsAddress, + dataPayment.amount, + dataPayment.quoteHash + ); + } + } + + function verifyPayment( + DataPayment[] calldata _payments + ) external view returns (PaymentVerificationResult[] memory) { + PaymentVerificationResult[] + memory verificationResults = new PaymentVerificationResult[]( + _payments.length + ); + for (uint256 i = 0; i < _payments.length; i++) { + DataPayment memory _payment = _payments[i]; + + CompletedPayment memory dataPayment = completedPayments[ + _payment.quoteHash + ]; + + bool isAmountOk = (dataPayment.amount != 0) && + (dataPayment.amount == _payment.amount); + + bool isAddressOk = dataPayment.rewardsAddress == + getFirst16Bytes(_payment.rewardsAddress) && + (_payment.rewardsAddress != address(0)); + + verificationResults[i] = PaymentVerificationResult({ + quoteHash: _payment.quoteHash, + amountPaid: dataPayment.amount, + isValid: isAmountOk && isAddressOk + }); + } + + return verificationResults; + } + + function getCompletedMerklePayment( + bytes32 winnerHash + ) external view returns (CompletedMerklePayment memory) { + return completedMerklePayments[winnerHash]; + } + + function getFirst16Bytes(address addr) internal pure returns (bytes16) { + return bytes16(uint128(uint160(addr) >> 32)); + } +} diff --git a/contracts/Types.sol b/contracts/Types.sol new file mode 100644 index 0000000..2c71268 --- /dev/null +++ b/contracts/Types.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/// Individual paid node record +struct PaidNode { + address rewardsAddress; + uint8 poolIndex; // Index in winner pool (0-19) + uint256 amount; +} + +/// Payment information stored on-chain +struct CompletedMerklePayment { + uint8 depth; // Merkle tree depth + uint64 merklePaymentTimestamp; // Payment timestamp + PaidNode[] paidNodeAddresses; // List of paid nodes +} + +struct CompletedPayment { + bytes16 rewardsAddress; + uint128 amount; +} + +struct CandidateNode { + address rewardsAddress; + uint256 amount; +} + +struct PoolCommitment { + bytes32 poolHash; // Cryptographic commitment to full pool data + CandidateNode[16] candidates; // Fixed size: always 16 +} + +struct DataPayment { + address rewardsAddress; + uint256 amount; + bytes32 quoteHash; +} + +struct PaymentVerificationResult { + bytes32 quoteHash; + uint256 amountPaid; + bool isValid; +} diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..5fd1781 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 5fd1781b1454fd1ef8e722282f86f9293cacf256 diff --git a/src/contract/merkle_payment_vault/error.rs b/src/contract/merkle_payment_vault/error.rs deleted file mode 100644 index be19f6c..0000000 --- a/src/contract/merkle_payment_vault/error.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright 2025 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use crate::contract::merkle_payment_vault::interface::IMerklePaymentVault; -use crate::retry; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Contract error: {0}")] - Contract(#[from] alloy::contract::Error), - #[error("RPC error: {0}")] - Rpc(String), - #[error("Merkle payments address not configured for this network")] - MerklePaymentsAddressNotConfigured, - #[error(transparent)] - Transaction(#[from] retry::TransactionError), - - // Smart contract custom errors (from IMerklePaymentVault.json) - #[error("ANT token address is null")] - AntTokenNull, - #[error("Batch limit exceeded")] - BatchLimitExceeded, - #[error("Merkle tree depth {depth} exceeds maximum allowed depth {max}")] - DepthTooLarge { depth: u8, max: u8 }, - #[error("Grace period not over")] - GracePeriodNotOver, - #[error("Invalid amount")] - InvalidAmount, - #[error("Invalid Chainlink price")] - InvalidChainlinkPrice, - #[error("Invalid input length")] - InvalidInputLength, - #[error("Invalid quote hash")] - InvalidQuoteHash, - #[error("Invalid recipients count")] - InvalidRecipientsCount, - #[error("Invalid root")] - InvalidRoot, - #[error("Invalid tree depth")] - InvalidTreeDepth, - #[error("Payment already exists for pool hash: {0}")] - PaymentAlreadyExists(String), - #[error("Payment not found for pool hash: {0}")] - PaymentNotFound(String), - #[error("Price feed address is null")] - PriceFeedNull, - #[error("Root already paid")] - RootAlreadyPaid, - #[error("Sequencer is down")] - SequencerDown, - #[error("Wrong candidate count in pool {pool_idx}: expected {expected}, got {got}")] - WrongCandidateCount { - pool_idx: u64, - expected: u64, - got: u64, - }, - #[error("Wrong pool count: expected {expected}, got {got}")] - WrongPoolCount { expected: u64, got: u64 }, -} - -impl Error { - /// Try to decode a contract error from revert data - pub(crate) fn try_decode_revert(data: &[u8]) -> Option { - use alloy::sol_types::SolInterface; - - // The revert data should start with the 4-byte selector followed by the error data - if data.len() < 4 { - return None; - } - - let selector: [u8; 4] = data[..4].try_into().ok()?; - let error_data = &data[4..]; - - // Try to decode as IMerklePaymentVaultErrors - if let Ok(contract_error) = - IMerklePaymentVault::IMerklePaymentVaultErrors::abi_decode_raw(selector, error_data) - { - return Some(Self::from_contract_error(contract_error)); - } - - None - } - - /// Convert a decoded contract error to our Error type - fn from_contract_error(error: IMerklePaymentVault::IMerklePaymentVaultErrors) -> Self { - use IMerklePaymentVault::IMerklePaymentVaultErrors; - - match error { - IMerklePaymentVaultErrors::AntTokenNull(_) => Self::AntTokenNull, - IMerklePaymentVaultErrors::BatchLimitExceeded(_) => Self::BatchLimitExceeded, - IMerklePaymentVaultErrors::DepthTooLarge(e) => Self::DepthTooLarge { - depth: e.depth, - max: e.max, - }, - IMerklePaymentVaultErrors::GracePeriodNotOver(_) => Self::GracePeriodNotOver, - IMerklePaymentVaultErrors::InvalidAmount(_) => Self::InvalidAmount, - IMerklePaymentVaultErrors::InvalidChainlinkPrice(_) => Self::InvalidChainlinkPrice, - IMerklePaymentVaultErrors::InvalidInputLength(_) => Self::InvalidInputLength, - IMerklePaymentVaultErrors::InvalidQuoteHash(_) => Self::InvalidQuoteHash, - IMerklePaymentVaultErrors::InvalidRecipientsCount(_) => Self::InvalidRecipientsCount, - IMerklePaymentVaultErrors::InvalidRoot(_) => Self::InvalidRoot, - IMerklePaymentVaultErrors::InvalidTreeDepth(_) => Self::InvalidTreeDepth, - IMerklePaymentVaultErrors::PaymentAlreadyExists(e) => { - Self::PaymentAlreadyExists(hex::encode(e.poolHash)) - } - IMerklePaymentVaultErrors::PaymentNotFound(e) => { - Self::PaymentNotFound(hex::encode(e.poolHash)) - } - IMerklePaymentVaultErrors::PriceFeedNull(_) => Self::PriceFeedNull, - IMerklePaymentVaultErrors::RootAlreadyPaid(_) => Self::RootAlreadyPaid, - IMerklePaymentVaultErrors::SequencerDown(_) => Self::SequencerDown, - IMerklePaymentVaultErrors::WrongCandidateCount(e) => Self::WrongCandidateCount { - pool_idx: e.poolIdx.try_into().unwrap_or(u64::MAX), - expected: e.expected.try_into().unwrap_or(u64::MAX), - got: e.got.try_into().unwrap_or(u64::MAX), - }, - IMerklePaymentVaultErrors::WrongPoolCount(e) => Self::WrongPoolCount { - expected: e.expected.try_into().unwrap_or(u64::MAX), - got: e.got.try_into().unwrap_or(u64::MAX), - }, - } - } -} diff --git a/src/contract/merkle_payment_vault/handler.rs b/src/contract/merkle_payment_vault/handler.rs deleted file mode 100644 index 098fc09..0000000 --- a/src/contract/merkle_payment_vault/handler.rs +++ /dev/null @@ -1,454 +0,0 @@ -// Copyright 2025 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use crate::common::{Address, Amount, Calldata}; -use crate::contract::merkle_payment_vault::error::Error; -use crate::contract::merkle_payment_vault::interface::IMerklePaymentVault; -use crate::contract::merkle_payment_vault::interface::IMerklePaymentVault::IMerklePaymentVaultInstance; -use crate::merkle_batch_payment::{CandidateNodePacked, PoolCommitmentPacked, PoolHash}; -use crate::retry::{GasInfo, TransactionError}; -use crate::transaction_config::TransactionConfig; -use alloy::consensus::Transaction; -use alloy::network::{Network, TransactionResponse}; -use alloy::primitives::FixedBytes; -use alloy::providers::Provider; -use alloy::sol_types::SolCall; -use exponential_backoff::Backoff; -use std::time::{Duration, SystemTime}; - -/// Arbitrum produces ~4 blocks per second (0.25s block time). -const ARBITRUM_BLOCKS_PER_SECOND: u64 = 4; - -/// Fixed padding (in seconds) added to the adaptive lookback to absorb clock skew. -const LOOKBACK_PADDING_SECS: u64 = 3600; - -/// Minimum lookback (blocks). Floor for fresh payments; ~14 hours at Arbitrum block time. -const MIN_LOOKBACK_BLOCKS: u64 = 200_000; - -/// Maximum lookback (blocks). ~7.2 days at Arbitrum block time, aligns with -/// `MERKLE_PAYMENT_EXPIRATION`. -const MAX_LOOKBACK_BLOCKS: u64 = 2_500_000; - -/// Divisor for the safety margin added to the raw lookback estimate (1/5 = 20%). -const LOOKBACK_MARGIN_DIVISOR: u64 = 5; - -pub struct MerklePaymentVaultHandler, N: Network> { - pub contract: IMerklePaymentVaultInstance, -} - -impl MerklePaymentVaultHandler -where - P: Provider, - N: Network, -{ - /// Create a new handler instance - pub fn new(contract_address: Address, provider: P) -> Self { - let contract = IMerklePaymentVault::new(contract_address, provider); - Self { contract } - } - - /// Set the provider - pub fn set_provider(&mut self, provider: P) { - let address = *self.contract.address(); - self.contract = IMerklePaymentVault::new(address, provider); - } - - /// Get the MerklePaymentMade event from a transaction hash with retry logic - /// - /// This function retries up to 2 times with exponential backoff if the event - /// is not found immediately. This handles cases where the transaction may not - /// be fully indexed yet. - /// - /// # Arguments - /// * `tx_hash` - The transaction hash to query - /// - /// # Returns - /// * The MerklePaymentMade event from the transaction - async fn get_merkle_payment_event( - &self, - tx_hash: crate::common::TxHash, - ) -> Result { - const MAX_ATTEMPTS: u32 = 3; - const INITIAL_DELAY_MS: u64 = 500; - const MAX_DELAY_MS: u64 = 8000; - - // Configure backoff with exponential delays between attempts - let backoff = Backoff::new( - MAX_ATTEMPTS, - Duration::from_millis(INITIAL_DELAY_MS), - Some(Duration::from_millis(MAX_DELAY_MS)), - ); - - let mut last_error = None; - let mut attempt = 1; - - for duration_opt in backoff { - match self.try_get_merkle_payment_event(tx_hash).await { - Ok(event) => return Ok(event), - Err(e) => { - last_error = Some(e); - - // Sleep before next attempt if duration is provided - if let Some(duration) = duration_opt { - debug!( - "Failed to get MerklePaymentMade event (attempt {}/{}), retrying in {}ms", - attempt, - MAX_ATTEMPTS, - duration.as_millis() - ); - tokio::time::sleep(duration).await; - } - attempt += 1; - } - } - } - - Err(last_error.unwrap_or_else(|| { - Error::Rpc("Failed to get MerklePaymentMade event after retries".to_string()) - })) - } - - /// Try to get the MerklePaymentMade event from a transaction hash (single attempt) - async fn try_get_merkle_payment_event( - &self, - tx_hash: crate::common::TxHash, - ) -> Result { - // Get the transaction to find its block number - let tx = self - .contract - .provider() - .get_transaction_by_hash(tx_hash) - .await - .map_err(|e| Error::Rpc(format!("Failed to get transaction: {e}")))? - .ok_or_else(|| Error::Rpc("Transaction not found".to_string()))?; - - let block_number = tx - .block_number() - .ok_or_else(|| Error::Rpc("Transaction has no block number".to_string()))?; - - // Get the MerklePaymentMade event from that block - let events = self - .contract - .MerklePaymentMade_filter() - .from_block(block_number) - .to_block(block_number) - .query() - .await - .map_err(|e| Error::Rpc(format!("Failed to query MerklePaymentMade events: {e}")))?; - - events - .into_iter() - .find(|(_, log)| log.transaction_hash == Some(tx_hash)) - .map(|(event, _)| event) - .ok_or_else(|| { - Error::Rpc("MerklePaymentMade event not found in transaction".to_string()) - }) - } - - /// Send transaction with retries and handle revert errors - async fn send_transaction_and_handle_errors( - &self, - calldata: Calldata, - to: Address, - transaction_config: &TransactionConfig, - ) -> Result<(crate::common::TxHash, GasInfo), Error> { - let tx_result = crate::retry::send_transaction_with_retries( - self.contract.provider(), - calldata, - to, - "pay for merkle tree", - transaction_config, - ) - .await; - - match tx_result { - Ok((hash, gas_info)) => Ok((hash, gas_info)), - Err(TransactionError::TransactionReverted { - message, - revert_data, - nonce, - }) => { - let error = self.decode_revert_error(message, revert_data, nonce); - Err(error) - } - Err(other_err) => Err(Error::from(other_err)), - } - } - - /// Decode revert data or return generic transaction error - fn decode_revert_error( - &self, - message: String, - revert_data: Option, - nonce: Option, - ) -> Error { - if let Some(revert_data_bytes) = &revert_data - && let Some(decoded_err) = Error::try_decode_revert(revert_data_bytes) - { - return decoded_err; - } - - Error::Transaction(TransactionError::TransactionReverted { - message, - revert_data, - nonce, - }) - } - - /// Pay for Merkle tree batch using packed calldata - /// - /// Uses the `payForMerkleTree2` contract function which accepts - /// packed data structures for smaller calldata and lower gas costs. - /// - /// # Arguments - /// * `depth` - Merkle tree depth - /// * `pool_commitments` - Packed pool commitments - /// * `merkle_payment_timestamp` - Payment timestamp - /// * `transaction_config` - Transaction configuration - /// - /// # Returns - /// * Tuple of (winner pool hash, total amount paid, gas info) - pub async fn pay_for_merkle_tree( - &self, - depth: u8, - pool_commitments: I, - merkle_payment_timestamp: u64, - transaction_config: &TransactionConfig, - ) -> Result<(PoolHash, Amount, GasInfo), Error> - where - I: IntoIterator, - T: Into, - { - debug!("Paying for Merkle tree: depth={depth}, timestamp={merkle_payment_timestamp}"); - - let (calldata, to) = - self.pay_for_merkle_tree_calldata(depth, pool_commitments, merkle_payment_timestamp)?; - - let (tx_hash, gas_info) = self - .send_transaction_and_handle_errors(calldata, to, transaction_config) - .await?; - - let event = self.get_merkle_payment_event(tx_hash).await?; - - let winner_pool_hash = event.winnerPoolHash.0; - let total_amount = event.totalAmount; - - debug!( - "MerklePaymentMade event: winnerPoolHash={}, depth={}, totalAmount={}, timestamp={}", - hex::encode(winner_pool_hash), - event.depth, - total_amount, - event.merklePaymentTimestamp - ); - - Ok((winner_pool_hash, total_amount, gas_info)) - } - - /// Get calldata for payForMerkleTree2 (packed format) - fn pay_for_merkle_tree_calldata( - &self, - depth: u8, - pool_commitments: I, - merkle_payment_timestamp: u64, - ) -> Result<(Calldata, Address), Error> - where - I: IntoIterator, - T: Into, - { - let pool_commitments: Vec = pool_commitments - .into_iter() - .map(|item| item.into()) - .collect(); - - let calldata = self - .contract - .payForMerkleTree2(depth, pool_commitments, merkle_payment_timestamp) - .calldata() - .to_owned(); - - Ok((calldata, *self.contract.address())) - } - - /// Estimate the cost of a Merkle tree payment without executing it - /// - /// This is a view function (0 gas) that runs the same pricing logic as - /// pay_for_merkle_tree but returns only the estimated cost. - /// - /// # Arguments - /// * `depth` - Merkle tree depth - /// * `pool_commitments` - Pool commitments with metrics - /// * `merkle_payment_timestamp` - Payment timestamp - /// - /// # Returns - /// * `Amount` - Estimated total cost - pub async fn estimate_merkle_tree_cost( - &self, - depth: u8, - pool_commitments: I, - merkle_payment_timestamp: u64, - ) -> Result - where - I: IntoIterator, - T: Into, - { - debug!("Estimating Merkle tree cost: depth={depth}, timestamp={merkle_payment_timestamp}",); - - let pool_commitments: Vec = pool_commitments - .into_iter() - .map(|item| item.into()) - .collect(); - - let total_amount = self - .contract - .estimateMerkleTreeCost(depth, pool_commitments, merkle_payment_timestamp) - .call() - .await - .map_err(Error::Contract)?; - - Ok(total_amount) - } - - /// Get the packed pool commitments from a payment transaction's calldata - /// - /// This finds the MerklePaymentMade event by winnerPoolHash (indexed), - /// fetches the transaction, and decodes the payForMerkleTree2 calldata - /// to extract the PoolCommitmentPacked array that was submitted on-chain. - /// - /// This is used by nodes to verify that clients sent correct cost units. - pub async fn get_payment_packed_commitments( - &self, - winner_pool_hash: PoolHash, - merkle_payment_timestamp: u64, - ) -> Result, Error> { - // Find the MerklePaymentMade event for this pool hash - let pool_hash_topic = FixedBytes::<32>::from(winner_pool_hash); - - // Get the current block number so we can scope the event query. - // Public RPC providers limit unbounded eth_getLogs queries, so we - // compute an adaptive lookback based on the payment's age. - let current_block = self - .contract - .provider() - .get_block_number() - .await - .map_err(|e| Error::Rpc(format!("Failed to get current block number: {e}")))?; - - let now_unix = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap_or_default() - .as_secs(); - let age_secs = now_unix.saturating_sub(merkle_payment_timestamp); - let raw_blocks = age_secs.saturating_mul(ARBITRUM_BLOCKS_PER_SECOND); - // Add 20% margin plus fixed padding for clock skew - let buffered = raw_blocks - .saturating_add(raw_blocks / LOOKBACK_MARGIN_DIVISOR) - .saturating_add(LOOKBACK_PADDING_SECS.saturating_mul(ARBITRUM_BLOCKS_PER_SECOND)); - let lookback = buffered.clamp(MIN_LOOKBACK_BLOCKS, MAX_LOOKBACK_BLOCKS); - let from_block = current_block.saturating_sub(lookback); - - // Query events filtered by the indexed winnerPoolHash within the recent block range - let events = self - .contract - .MerklePaymentMade_filter() - .topic1(pool_hash_topic) - .from_block(from_block) - .to_block(current_block) - .query() - .await - .map_err(|e| { - Error::Rpc(format!( - "Failed to query MerklePaymentMade events for pool hash {}: {e}", - hex::encode(winner_pool_hash) - )) - })?; - - let (_event, log) = events.into_iter().next().ok_or_else(|| { - Error::Rpc(format!( - "No MerklePaymentMade event found for pool hash {}", - hex::encode(winner_pool_hash) - )) - })?; - - let tx_hash = log.transaction_hash.ok_or_else(|| { - Error::Rpc("MerklePaymentMade event log has no transaction hash".to_string()) - })?; - - // Fetch the full transaction to get its calldata - let tx = self - .contract - .provider() - .get_transaction_by_hash(tx_hash) - .await - .map_err(|e| Error::Rpc(format!("Failed to get transaction {tx_hash}: {e}")))? - .ok_or_else(|| Error::Rpc(format!("Transaction {tx_hash} not found")))?; - - // Get the input/calldata from the transaction - let input = tx.input(); - - // Decode the payForMerkleTree2 calldata - let decoded = IMerklePaymentVault::payForMerkleTree2Call::abi_decode(input) - .map_err(|e| Error::Rpc(format!("Failed to decode payForMerkleTree2 calldata: {e}")))?; - - // Convert from contract types to our types - let packed_commitments: Vec = decoded - .poolCommitments - .into_iter() - .map(|pc| { - let candidates: Vec = pc - .candidates - .into_iter() - .map(|c| CandidateNodePacked { - rewards_address: c.rewardsAddress, - data_type_and_total_cost_unit: c.dataTypeAndTotalCostUnit, - }) - .collect(); - - PoolCommitmentPacked { - pool_hash: pc.poolHash.0, - candidates: candidates - .try_into() - .expect("contract enforces CANDIDATES_PER_POOL candidates"), - } - }) - .collect(); - - Ok(packed_commitments) - } - - /// Get payment info for a winner pool hash - pub async fn get_payment_info( - &self, - winner_pool_hash: PoolHash, - ) -> Result { - debug!( - "Getting payment info for pool hash: {}", - hex::encode(winner_pool_hash) - ); - - let info = self - .contract - .getPaymentInfo(winner_pool_hash.into()) - .call() - .await - .map_err(Error::Contract)?; - - // Check if payment exists (depth == 0 means not found) - if info.depth == 0 { - return Err(Error::PaymentNotFound(hex::encode(winner_pool_hash))); - } - - debug!( - "getPaymentInfo returned: depth={}, timestamp={}, paid_nodes={}", - info.depth, - info.merklePaymentTimestamp, - info.paidNodeAddresses.len() - ); - - Ok(info) - } -} diff --git a/src/contract/merkle_payment_vault/implementation.rs b/src/contract/merkle_payment_vault/implementation.rs deleted file mode 100644 index cc20f0d..0000000 --- a/src/contract/merkle_payment_vault/implementation.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2025 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use crate::common::Address; -use alloy::network::Network; -use alloy::providers::Provider; -use alloy::sol; - -sol!( - #[allow(missing_docs)] - #[sol(rpc)] - MerklePaymentVaultImplementation, - "artifacts/MerklePaymentVault.json" -); - -/// Deploys the Merkle payment vault contract and returns the contract address -pub async fn deploy(provider: &P, network_token_address: Address) -> Address -where - P: Provider, - N: Network, -{ - let contract = MerklePaymentVaultImplementation::deploy(provider, network_token_address) - .await - .expect("Could not deploy Merkle payment vault implementation contract"); - - *contract.address() -} diff --git a/src/contract/merkle_payment_vault/interface.rs b/src/contract/merkle_payment_vault/interface.rs deleted file mode 100644 index 19b16ee..0000000 --- a/src/contract/merkle_payment_vault/interface.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2025 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use crate::contract::data_type_conversion; -use crate::merkle_batch_payment::CANDIDATES_PER_POOL; -use crate::quoting_metrics::QuotingMetrics; -use alloy::primitives::U256; -use alloy::sol; - -// Generate bindings from ABI -sol!( - #[allow(missing_docs)] - #[derive(Debug)] - #[sol(rpc)] - IMerklePaymentVault, - "abi/IMerklePaymentVault.json" -); - -// Re-export contract instance type -pub use IMerklePaymentVault::IMerklePaymentVaultInstance; - -// Re-export PoolHash (doesn't conflict with generated types) -pub use crate::merkle_batch_payment::PoolHash; - -// Implement conversions from our API types to contract types -impl From for IMerklePaymentVault::PoolCommitment { - fn from(pool: crate::merkle_batch_payment::PoolCommitment) -> Self { - // Convert the exact-sized array directly - let candidates_array: [IMerklePaymentVault::CandidateNode; CANDIDATES_PER_POOL] = - pool.candidates.map(|c| c.into()); - - Self { - poolHash: pool.pool_hash.into(), - candidates: candidates_array, - } - } -} - -impl From for IMerklePaymentVault::CandidateNode { - fn from(node: crate::merkle_batch_payment::CandidateNode) -> Self { - Self { - rewardsAddress: node.rewards_address, - metrics: node.metrics.into(), - } - } -} - -impl From for IMerklePaymentVault::QuotingMetrics { - fn from(metrics: QuotingMetrics) -> Self { - Self { - dataType: data_type_conversion(metrics.data_type), - closeRecordsStored: U256::from(metrics.close_records_stored), - recordsPerType: metrics - .records_per_type - .into_iter() - .map(|(data_type, records)| IMerklePaymentVault::Record { - dataType: data_type_conversion(data_type), - records: U256::from(records), - }) - .collect(), - } - } -} - -// Conversions for packed types (v2) -impl From - for IMerklePaymentVault::PoolCommitmentPacked -{ - fn from(pool: crate::merkle_batch_payment::PoolCommitmentPacked) -> Self { - let candidates_array: [IMerklePaymentVault::CandidateNodePacked; CANDIDATES_PER_POOL] = - pool.candidates.map(|c| c.into()); - - Self { - poolHash: pool.pool_hash.into(), - candidates: candidates_array, - } - } -} - -impl From - for IMerklePaymentVault::CandidateNodePacked -{ - fn from(node: crate::merkle_batch_payment::CandidateNodePacked) -> Self { - Self { - rewardsAddress: node.rewards_address, - dataTypeAndTotalCostUnit: node.data_type_and_total_cost_unit, - } - } -} diff --git a/src/contract/merkle_payment_vault/mod.rs b/src/contract/merkle_payment_vault/mod.rs deleted file mode 100644 index 74eb16c..0000000 --- a/src/contract/merkle_payment_vault/mod.rs +++ /dev/null @@ -1,244 +0,0 @@ -// Copyright 2025 MaidSafe.net limited. -// -// This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -// Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed -// under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. Please review the Licences for the specific language governing -// permissions and limitations relating to use of the SAFE Network Software. - -use crate::contract::merkle_payment_vault::error::Error; -use crate::contract::merkle_payment_vault::handler::MerklePaymentVaultHandler; -use crate::merkle_batch_payment::{PoolCommitmentPacked, PoolHash}; -use crate::utils::http_provider; - -pub mod error; -pub mod handler; -pub mod implementation; -pub mod interface; - -/// Helper function to get payment info for a Merkle payment verification -/// Returns the payment info if the payment exists on-chain -pub async fn get_merkle_payment_info( - network: &crate::Network, - winner_pool_hash: PoolHash, -) -> Result { - let merkle_vault_address = network - .merkle_payments_address() - .ok_or(Error::MerklePaymentsAddressNotConfigured)?; - - debug!( - "get_merkle_payment_info: contract={:?}, pool_hash={}", - merkle_vault_address, - hex::encode(winner_pool_hash) - ); - - let provider = http_provider(network.rpc_url().clone()); - let merkle_vault = MerklePaymentVaultHandler::new(*merkle_vault_address, provider); - - merkle_vault.get_payment_info(winner_pool_hash).await -} - -/// Helper function to get the packed pool commitments from a payment transaction's calldata. -/// -/// Nodes use this to verify that clients sent correct cost units to the contract. -/// The function finds the MerklePaymentMade event by winnerPoolHash, fetches the -/// transaction, and decodes the payForMerkleTree2 calldata. -pub async fn get_merkle_payment_packed_commitments( - network: &crate::Network, - winner_pool_hash: PoolHash, - merkle_payment_timestamp: u64, -) -> Result, Error> { - let merkle_vault_address = network - .merkle_payments_address() - .ok_or(Error::MerklePaymentsAddressNotConfigured)?; - - let provider = http_provider(network.rpc_url().clone()); - let merkle_vault = MerklePaymentVaultHandler::new(*merkle_vault_address, provider); - - merkle_vault - .get_payment_packed_commitments(winner_pool_hash, merkle_payment_timestamp) - .await -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::common::U256; - use crate::merkle_batch_payment::{CandidateNode, PoolCommitment}; - use crate::quoting_metrics::QuotingMetrics; - use crate::testnet::{deploy_network_token_contract, start_node}; - use crate::transaction_config::TransactionConfig; - use crate::wallet::wallet_address; - use alloy::providers::WalletProvider; - - #[tokio::test] - async fn test_smart_contract() { - // Start local Anvil node - let (_anvil, rpc_url) = start_node().unwrap(); - - // Deploy network token - let network_token = deploy_network_token_contract(&rpc_url, &_anvil) - .await - .unwrap(); - let token_address = *network_token.contract.address(); - - // Deploy Merkle payment vault using the same provider as network token - let vault_address = - implementation::deploy(network_token.contract.provider(), token_address).await; - - // Create handler with the same provider - let vault_handler = MerklePaymentVaultHandler::new( - vault_address, - network_token.contract.provider().clone(), - ); - - // Get wallet address - let wallet_addr = wallet_address(network_token.contract.provider().wallet()); - - // Transaction config - let tx_config = TransactionConfig::default(); - - // Approve vault contract to spend tokens (wallet already has tokens from deployment) - network_token - .approve(vault_address, U256::MAX, &tx_config) - .await - .expect("Failed to approve tokens"); - - // Create test pool commitments with candidates - let depth = 4u8; - let num_pools = 2usize.pow((depth / 2) as u32); - let timestamp = 1234567890u64; - - let mut pool_commitments = Vec::new(); - for pool_idx in 0..num_pools { - // Create 16 candidates per pool - let mut candidates = Vec::new(); - for candidate_idx in 0..16 { - candidates.push(CandidateNode { - rewards_address: wallet_addr, // Use wallet address for simplicity - metrics: QuotingMetrics { - data_type: 0, - data_size: 1024 * (candidate_idx + 1), - close_records_stored: 100 + candidate_idx, - max_records: 1000, - received_payment_count: 10, - live_time: 3600, - records_per_type: vec![(0, 50), (1, 30), (2, 20)], - network_density: Some([0u8; 32]), - network_size: Some(100), - }, - }); - } - - pool_commitments.push(PoolCommitment { - pool_hash: [pool_idx as u8; 32], - candidates: candidates - .try_into() - .expect("Should have exactly 16 candidates"), - }); - } - - // Test 1: Estimate Merkle tree cost - println!("Test 1: Estimating Merkle tree cost..."); - let estimated_cost = vault_handler - .estimate_merkle_tree_cost(depth, pool_commitments.clone(), timestamp) - .await - .expect("Failed to estimate cost"); - - println!("Estimated cost: {estimated_cost} tokens"); - assert!( - estimated_cost > U256::ZERO, - "Cost should be greater than zero" - ); - - // Test 2: Pay for Merkle tree (using packed commitments) - println!("\nTest 2: Paying for Merkle tree..."); - let pool_commitments_packed: Vec<_> = pool_commitments - .iter() - .map(|c| c.to_packed().expect("cost unit packing")) - .collect(); - let (winner_pool_hash, total_amount, gas_info) = vault_handler - .pay_for_merkle_tree(depth, pool_commitments_packed, timestamp, &tx_config) - .await - .expect("Failed to pay for Merkle tree"); - println!("Gas used: {:?}", gas_info.actual_gas_used); - - println!("Payment transaction sent successfully"); - - println!( - "Payment successful for pool: {}, amount: {}", - hex::encode(winner_pool_hash), - total_amount - ); - - // Test 3: Get payment info - println!("\nTest 3: Retrieving payment info..."); - let payment_info = vault_handler - .get_payment_info(winner_pool_hash) - .await - .expect("Failed to get payment info"); - - println!("Payment info retrieved:"); - println!(" Depth: {}", payment_info.depth); - println!(" Timestamp: {}", payment_info.merklePaymentTimestamp); - println!(" Paid nodes: {}", payment_info.paidNodeAddresses.len()); - - assert_eq!( - payment_info.depth, depth, - "Stored depth should match what we paid for" - ); - assert_eq!( - payment_info.merklePaymentTimestamp, timestamp, - "Stored timestamp should match" - ); - assert!( - !payment_info.paidNodeAddresses.is_empty(), - "Should have paid nodes" - ); - - // Test 4: Try to pay again with the same pools. - // - // The contract picks a winner from submitted pools using block.prevrandao. - // With 4 pools, the second call may select a different winner (different block), - // which is valid — only the SAME pool hash should be rejected as duplicate. - println!("\nTest 4: Testing duplicate payment detection..."); - let all_packed: Vec<_> = pool_commitments - .iter() - .map(|c| c.to_packed().expect("cost unit packing")) - .collect(); - let duplicate_result = vault_handler - .pay_for_merkle_tree(depth, all_packed, timestamp, &tx_config) - .await; - - // The contract selects a winner using block.prevrandao, so it may pick a different - // pool than the first call. If it picks the same winner, we get PaymentAlreadyExists. - // If it picks a different winner, it succeeds (paying for a new pool is valid). - // Both outcomes are correct behavior — the important thing is that the SAME pool - // hash can't be paid twice. - match &duplicate_result { - Err(error::Error::PaymentAlreadyExists(_)) => { - println!("Correctly detected duplicate payment (same winner selected)!"); - } - Ok((new_winner, _, _)) => { - println!( - "Different winner selected: {} (original: {})", - hex::encode(new_winner), - hex::encode(winner_pool_hash) - ); - assert_ne!( - *new_winner, winner_pool_hash, - "Same winner should have been rejected as duplicate" - ); - // Verify original payment still exists - let original_info = vault_handler - .get_payment_info(winner_pool_hash) - .await - .expect("Original payment should still exist"); - assert_eq!(original_info.depth, depth); - } - Err(e) => panic!("Unexpected error: {e:?}"), - } - - println!("\n✅ All tests passed!"); - } -} diff --git a/src/contract/mod.rs b/src/contract/mod.rs index bebd584..405f0c7 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -6,16 +6,5 @@ // KIND, either express or implied. Please review the Licences for the specific language governing // permissions and limitations relating to use of the SAFE Network Software. -pub mod merkle_payment_vault; pub mod network_token; pub mod payment_vault; - -pub fn data_type_conversion(data_type: u32) -> u8 { - match data_type { - 0 => 2, // Chunk - 1 => 0, // GraphEntry - 2 => 3, // Pointer - 3 => 1, // Scratchpad - _ => 4, // Does not exist - } -} diff --git a/src/contract/payment_vault/error.rs b/src/contract/payment_vault/error.rs index 62f24b6..73eabac 100644 --- a/src/contract/payment_vault/error.rs +++ b/src/contract/payment_vault/error.rs @@ -1,20 +1,77 @@ +use crate::contract::payment_vault::interface::IPaymentVault; use crate::retry; -use alloy::transports::{RpcError, TransportErrorKind}; #[derive(thiserror::Error, Debug)] pub enum Error { - #[error(transparent)] - ContractError(#[from] alloy::contract::Error), - #[error(transparent)] - RpcError(#[from] RpcError), - #[error(transparent)] - PendingTransactionError(#[from] alloy::providers::PendingTransactionError), - #[error("Payment is invalid.")] - PaymentInvalid, - #[error("Payment verification length must be 3.")] - PaymentVerificationLengthInvalid, - #[error("Timeout: {0:?}")] - Timeout(#[from] tokio::time::error::Elapsed), + #[error("Contract error: {0}")] + Contract(#[from] alloy::contract::Error), + #[error("RPC error: {0}")] + Rpc(String), #[error(transparent)] Transaction(#[from] retry::TransactionError), + + // Smart contract custom errors + #[error("ANT token address is null")] + AntTokenNull, + #[error("Batch limit exceeded")] + BatchLimitExceeded, + #[error("Merkle tree depth {depth} exceeds maximum allowed depth {max_depth}")] + DepthTooLarge { depth: u8, max_depth: u8 }, + #[error("Invalid input length")] + InvalidInputLength, + #[error("Payment already exists for pool hash: {0}")] + PaymentAlreadyExists(String), + #[error("Payment not found for pool hash: {0}")] + PaymentNotFound(String), + #[error("Wrong pool count: expected {expected}, got {actual}")] + WrongPoolCount { expected: u64, actual: u64 }, +} + +impl Error { + /// Try to decode a contract error from revert data + pub(crate) fn try_decode_revert(data: &[u8]) -> Option { + use alloy::sol_types::SolInterface; + + // The revert data should start with the 4-byte selector followed by the error data + if data.len() < 4 { + return None; + } + + let selector: [u8; 4] = data[..4].try_into().ok()?; + let error_data = &data[4..]; + + // Try to decode as IPaymentVaultErrors + if let Ok(contract_error) = + IPaymentVault::IPaymentVaultErrors::abi_decode_raw(selector, error_data) + { + return Some(Self::from_contract_error(contract_error)); + } + + None + } + + /// Convert a decoded contract error to our Error type + fn from_contract_error(error: IPaymentVault::IPaymentVaultErrors) -> Self { + use IPaymentVault::IPaymentVaultErrors; + + match error { + IPaymentVaultErrors::AntTokenNull(_) => Self::AntTokenNull, + IPaymentVaultErrors::BatchLimitExceeded(_) => Self::BatchLimitExceeded, + IPaymentVaultErrors::DepthTooLarge(e) => Self::DepthTooLarge { + depth: e.depth, + max_depth: e.maxDepth, + }, + IPaymentVaultErrors::InvalidInputLength(_) => Self::InvalidInputLength, + IPaymentVaultErrors::PaymentAlreadyExists(e) => { + Self::PaymentAlreadyExists(hex::encode(e.winnerPoolHash)) + } + IPaymentVaultErrors::WrongPoolCount(e) => Self::WrongPoolCount { + expected: e.expected.try_into().unwrap_or(u64::MAX), + actual: e.actual.try_into().unwrap_or(u64::MAX), + }, + IPaymentVaultErrors::SafeERC20FailedOperation(e) => { + Self::Rpc(format!("SafeERC20 transfer failed for token: {}", e.token)) + } + } + } } diff --git a/src/contract/payment_vault/handler.rs b/src/contract/payment_vault/handler.rs index 565193f..846e476 100644 --- a/src/contract/payment_vault/handler.rs +++ b/src/contract/payment_vault/handler.rs @@ -2,10 +2,13 @@ use crate::common::{Address, Amount, Calldata, TxHash}; use crate::contract::payment_vault::error::Error; use crate::contract::payment_vault::interface::IPaymentVault; use crate::contract::payment_vault::interface::IPaymentVault::IPaymentVaultInstance; -use crate::retry::{GasInfo, retry, send_transaction_with_retries}; +use crate::merkle_batch_payment::PoolHash; +use crate::retry::{GasInfo, TransactionError, send_transaction_with_retries}; use crate::transaction_config::TransactionConfig; -use alloy::network::Network; +use alloy::network::{Network, TransactionResponse}; use alloy::providers::Provider; +use exponential_backoff::Backoff; +use std::time::Duration; pub struct PaymentVaultHandler, N: Network> { pub contract: IPaymentVaultInstance, @@ -28,32 +31,7 @@ where self.contract = IPaymentVault::new(address, provider); } - /// Fetch a quote from the contract - pub async fn get_quote>>( - &self, - metrics: I, - ) -> Result, Error> { - let metrics: Vec<_> = metrics.into_iter().map(|v| v.into()).collect(); - - debug!("Getting quotes for metrics: {metrics:?}"); - - let mut amounts = retry( - || async { self.contract.getQuote(metrics.clone()).call().await }, - "getQuote", - None, - ) - .await?; - - // FIXME: temporary logic until the local smart contract gets updated - if amounts.len() == 1 { - let value = amounts[0]; - amounts.resize(metrics.len(), value); - } - - debug!("Returned quotes are: {:?}", amounts); - - Ok(amounts) - } + // ── Single-node (quote) payments ──────────────────────────────────── /// Pay for quotes. /// Returns the transaction hash and gas information. @@ -92,32 +70,241 @@ where Ok((calldata, *self.contract.address())) } - /// Verify if payments are valid - pub async fn verify_payment>>( + // ── Merkle batch payments ─────────────────────────────────────────── + + /// Pay for Merkle tree batch. + /// + /// Sends `payForMerkleTree` with unpacked `PoolCommitment` structs (candidates have price). + /// + /// # Returns + /// * Tuple of (winner pool hash, total amount paid, gas info) + pub async fn pay_for_merkle_tree( + &self, + depth: u8, + pool_commitments: I, + merkle_payment_timestamp: u64, + transaction_config: &TransactionConfig, + ) -> Result<(PoolHash, Amount, GasInfo), Error> + where + I: IntoIterator, + T: Into, + { + debug!("Paying for Merkle tree: depth={depth}, timestamp={merkle_payment_timestamp}"); + + let (calldata, to) = + self.pay_for_merkle_tree_calldata(depth, pool_commitments, merkle_payment_timestamp)?; + + let (tx_hash, gas_info) = self + .send_transaction_and_handle_errors(calldata, to, transaction_config) + .await?; + + let event = self.get_merkle_payment_event(tx_hash).await?; + + let winner_pool_hash = event.winnerPoolHash.0; + let total_amount = event.totalAmount; + + debug!( + "MerklePaymentMade event: winnerPoolHash={}, depth={}, totalAmount={}, timestamp={}", + hex::encode(winner_pool_hash), + event.depth, + total_amount, + event.merklePaymentTimestamp + ); + + Ok((winner_pool_hash, total_amount, gas_info)) + } + + /// Get calldata for payForMerkleTree + fn pay_for_merkle_tree_calldata( &self, - payment_verifications: I, - ) -> Result<[IPaymentVault::PaymentVerificationResult; 3], Error> { - let payment_verifications: Vec = payment_verifications + depth: u8, + pool_commitments: I, + merkle_payment_timestamp: u64, + ) -> Result<(Calldata, Address), Error> + where + I: IntoIterator, + T: Into, + { + let pool_commitments: Vec = pool_commitments .into_iter() - .map(|v| v.into()) + .map(|item| item.into()) .collect(); - debug!("Verifying payments: {payment_verifications:?}"); - - let results = retry( - || async { - self.contract - .verifyPayment(payment_verifications.clone()) - .call() - .await - }, - "verifyPayment", - None, + let calldata = self + .contract + .payForMerkleTree(depth, pool_commitments, merkle_payment_timestamp) + .calldata() + .to_owned(); + + Ok((calldata, *self.contract.address())) + } + + /// Get completed merkle payment info for a winner pool hash. + /// + /// Calls `getCompletedMerklePayment` on the contract, which returns + /// `CompletedMerklePayment` containing depth, timestamp, and paid nodes + /// (each with rewards address, pool index, and amount). + pub async fn get_completed_merkle_payment( + &self, + winner_pool_hash: PoolHash, + ) -> Result { + debug!( + "Getting completed merkle payment for pool hash: {}", + hex::encode(winner_pool_hash) + ); + + let info = self + .contract + .getCompletedMerklePayment(winner_pool_hash.into()) + .call() + .await + .map_err(Error::Contract)?; + + // Check if payment exists (depth == 0 means not found) + if info.depth == 0 { + return Err(Error::PaymentNotFound(hex::encode(winner_pool_hash))); + } + + debug!( + "getCompletedMerklePayment returned: depth={}, timestamp={}, paid_nodes={}", + info.depth, + info.merklePaymentTimestamp, + info.paidNodeAddresses.len() + ); + + Ok(info) + } + + // ── Private helpers ───────────────────────────────────────────────── + + /// Get the MerklePaymentMade event from a transaction hash with retry logic. + /// + /// Retries up to 2 times with exponential backoff if the event is not found + /// immediately (handles cases where the transaction may not be fully indexed). + async fn get_merkle_payment_event( + &self, + tx_hash: TxHash, + ) -> Result { + const MAX_ATTEMPTS: u32 = 3; + const INITIAL_DELAY_MS: u64 = 500; + const MAX_DELAY_MS: u64 = 8000; + + let backoff = Backoff::new( + MAX_ATTEMPTS, + Duration::from_millis(INITIAL_DELAY_MS), + Some(Duration::from_millis(MAX_DELAY_MS)), + ); + + let mut last_error = None; + let mut attempt = 1; + + for duration_opt in backoff { + match self.try_get_merkle_payment_event(tx_hash).await { + Ok(event) => return Ok(event), + Err(e) => { + last_error = Some(e); + + if let Some(duration) = duration_opt { + debug!( + "Failed to get MerklePaymentMade event (attempt {}/{}), retrying in {}ms", + attempt, + MAX_ATTEMPTS, + duration.as_millis() + ); + tokio::time::sleep(duration).await; + } + attempt += 1; + } + } + } + + Err(last_error.unwrap_or_else(|| { + Error::Rpc("Failed to get MerklePaymentMade event after retries".to_string()) + })) + } + + /// Try to get the MerklePaymentMade event from a transaction hash (single attempt) + async fn try_get_merkle_payment_event( + &self, + tx_hash: TxHash, + ) -> Result { + let tx = self + .contract + .provider() + .get_transaction_by_hash(tx_hash) + .await + .map_err(|e| Error::Rpc(format!("Failed to get transaction: {e}")))? + .ok_or_else(|| Error::Rpc("Transaction not found".to_string()))?; + + let block_number = tx + .block_number() + .ok_or_else(|| Error::Rpc("Transaction has no block number".to_string()))?; + + let events = self + .contract + .MerklePaymentMade_filter() + .from_block(block_number) + .to_block(block_number) + .query() + .await + .map_err(|e| Error::Rpc(format!("Failed to query MerklePaymentMade events: {e}")))?; + + events + .into_iter() + .find(|(_, log)| log.transaction_hash == Some(tx_hash)) + .map(|(event, _)| event) + .ok_or_else(|| { + Error::Rpc("MerklePaymentMade event not found in transaction".to_string()) + }) + } + + /// Send transaction with retries and handle revert errors + async fn send_transaction_and_handle_errors( + &self, + calldata: Calldata, + to: Address, + transaction_config: &TransactionConfig, + ) -> Result<(TxHash, GasInfo), Error> { + let tx_result = crate::retry::send_transaction_with_retries( + self.contract.provider(), + calldata, + to, + "pay for merkle tree", + transaction_config, ) - .await?; + .await; + + match tx_result { + Ok((hash, gas_info)) => Ok((hash, gas_info)), + Err(TransactionError::TransactionReverted { + message, + revert_data, + nonce, + }) => { + let error = self.decode_revert_error(message, revert_data, nonce); + Err(error) + } + Err(other_err) => Err(Error::from(other_err)), + } + } - debug!("Payment verification results: {:?}", results); + /// Decode revert data or return generic transaction error + fn decode_revert_error( + &self, + message: String, + revert_data: Option, + nonce: Option, + ) -> Error { + if let Some(revert_data_bytes) = &revert_data + && let Some(decoded_err) = Error::try_decode_revert(revert_data_bytes) + { + return decoded_err; + } - Ok(results) + Error::Transaction(TransactionError::TransactionReverted { + message, + revert_data, + nonce, + }) } } diff --git a/src/contract/payment_vault/implementation.rs b/src/contract/payment_vault/implementation.rs index ef8497b..4fca752 100644 --- a/src/contract/payment_vault/implementation.rs +++ b/src/contract/payment_vault/implementation.rs @@ -1,5 +1,6 @@ use crate::common::Address; use alloy::network::Network; +use alloy::primitives::U256; use alloy::providers::Provider; use alloy::sol; @@ -7,18 +8,24 @@ sol!( #[allow(missing_docs)] #[sol(rpc)] PaymentVaultImplementation, - "artifacts/PaymentVaultNoProxyV6.json" + "artifacts/PaymentVaultV2.json" ); -/// Deploys the payment vault contract and returns the contract address +/// Default batch limit for local deployments. +const DEFAULT_BATCH_LIMIT: U256 = U256::from_limbs([512, 0, 0, 0]); + +/// Deploys the unified payment vault contract and returns the contract address. +/// +/// Uses a default batch limit of 512 for local testing. pub async fn deploy(provider: &P, network_token_address: Address) -> Address where P: Provider, N: Network, { - let contract = PaymentVaultImplementation::deploy(provider, network_token_address) - .await - .expect("Could not deploy payment vault implementation contract"); + let contract = + PaymentVaultImplementation::deploy(provider, network_token_address, DEFAULT_BATCH_LIMIT) + .await + .expect("Could not deploy payment vault implementation contract"); *contract.address() } diff --git a/src/contract/payment_vault/interface.rs b/src/contract/payment_vault/interface.rs index 468e9f8..5eb0e42 100644 --- a/src/contract/payment_vault/interface.rs +++ b/src/contract/payment_vault/interface.rs @@ -1,7 +1,5 @@ -use crate::common::{Address, Amount, QuoteHash, U256}; -use crate::contract::data_type_conversion; -use crate::quoting_metrics::QuotingMetrics; -use alloy::primitives::FixedBytes; +use crate::common::{Address, Amount, QuoteHash}; +use crate::merkle_batch_payment::CANDIDATES_PER_POOL; use alloy::sol; sol!( @@ -9,18 +7,11 @@ sol!( #[derive(Debug)] #[sol(rpc)] IPaymentVault, - "abi/IPaymentVaultV6.json" + "abi/IPaymentVault.json" ); -impl From<(QuoteHash, QuotingMetrics, Address)> for IPaymentVault::PaymentVerification { - fn from(value: (QuoteHash, QuotingMetrics, Address)) -> Self { - Self { - metrics: value.1.into(), - rewardsAddress: value.2, - quoteHash: value.0, - } - } -} +// Re-export PoolHash +pub use crate::merkle_batch_payment::PoolHash; impl From<(QuoteHash, Address, Amount)> for IPaymentVault::DataPayment { fn from(value: (QuoteHash, Address, Amount)) -> Self { @@ -32,26 +23,22 @@ impl From<(QuoteHash, Address, Amount)> for IPaymentVault::DataPayment { } } -impl From for IPaymentVault::QuotingMetrics { - fn from(value: QuotingMetrics) -> Self { +impl From for IPaymentVault::PoolCommitment { + fn from(pool: crate::merkle_batch_payment::PoolCommitment) -> Self { + let candidates_array: [IPaymentVault::CandidateNode; CANDIDATES_PER_POOL] = + pool.candidates.map(|c| c.into()); + Self { + poolHash: pool.pool_hash.into(), + candidates: candidates_array, + } + } +} + +impl From for IPaymentVault::CandidateNode { + fn from(node: crate::merkle_batch_payment::CandidateNode) -> Self { Self { - dataType: data_type_conversion(value.data_type), - dataSize: U256::from(value.data_size), - closeRecordsStored: U256::from(value.close_records_stored), - recordsPerType: value - .records_per_type - .into_iter() - .map(|(data_type, amount)| IPaymentVault::Record { - dataType: data_type_conversion(data_type), - records: U256::from(amount), - }) - .collect(), - maxRecords: U256::from(value.max_records), - receivedPaymentCount: U256::from(value.received_payment_count), - liveTime: U256::from(value.live_time), - networkDensity: FixedBytes::<32>::from(value.network_density.unwrap_or_default()) - .into(), - networkSize: value.network_size.map(U256::from).unwrap_or_default(), + rewardsAddress: node.rewards_address, + amount: node.price, // our internal "price" maps to contract's "amount" } } } diff --git a/src/contract/payment_vault/mod.rs b/src/contract/payment_vault/mod.rs index 87a117a..81503cd 100644 --- a/src/contract/payment_vault/mod.rs +++ b/src/contract/payment_vault/mod.rs @@ -1,7 +1,6 @@ -use crate::Network; -use crate::common::{Address, Amount, QuoteHash}; +use crate::contract::payment_vault::error::Error; use crate::contract::payment_vault::handler::PaymentVaultHandler; -use crate::quoting_metrics::QuotingMetrics; +use crate::merkle_batch_payment::PoolHash; use crate::utils::http_provider; pub mod error; @@ -11,45 +10,22 @@ pub mod interface; pub const MAX_TRANSFERS_PER_TRANSACTION: usize = 256; -/// Helper function to return a quote for the given quoting metrics. -pub async fn get_market_price( - network: &Network, - quoting_metrics: Vec, -) -> Result, error::Error> { - let provider = http_provider(network.rpc_url().clone()); - let payment_vault = PaymentVaultHandler::new(*network.data_payments_address(), provider); - payment_vault.get_quote(quoting_metrics).await -} - -/// Helper function to verify whether a data payment is valid. -/// Returns the amount paid to the owned quote hashes. -pub async fn verify_data_payment( - network: &Network, - owned_quote_hashes: Vec, - payment: Vec<(QuoteHash, QuotingMetrics, Address)>, -) -> Result { - let provider = http_provider(network.rpc_url().clone()); - let payment_vault = PaymentVaultHandler::new(*network.data_payments_address(), provider); +/// Helper function to get completed merkle payment info for verification. +/// Returns the payment info if the payment exists on-chain. +pub async fn get_completed_merkle_payment( + network: &crate::Network, + winner_pool_hash: PoolHash, +) -> Result { + let vault_address = *network.payment_vault_address(); - let mut amount = Amount::ZERO; + debug!( + "get_completed_merkle_payment: contract={:?}, pool_hash={}", + vault_address, + hex::encode(winner_pool_hash) + ); - let payment_verifications: Vec<_> = payment - .into_iter() - .map(interface::IPaymentVault::PaymentVerification::from) - .collect(); - - let payment_verification_results = payment_vault.verify_payment(payment_verifications).await?; - - for payment_verification_result in payment_verification_results { - // TODO we currently fail on a single invalid payment, maybe we should deal with this in a different way - if !payment_verification_result.isValid { - return Err(error::Error::PaymentInvalid); - } - - if owned_quote_hashes.contains(&payment_verification_result.quoteHash) { - amount += payment_verification_result.amountPaid; - } - } + let provider = http_provider(network.rpc_url().clone()); + let handler = PaymentVaultHandler::new(vault_address, provider); - Ok(amount) + handler.get_completed_merkle_payment(winner_pool_hash).await } diff --git a/src/data_payments.rs b/src/data_payments.rs index 477dd96..f55b837 100644 --- a/src/data_payments.rs +++ b/src/data_payments.rs @@ -11,9 +11,8 @@ //! Defines [`EncodedPeerId`], [`PaymentQuote`], and [`ProofOfPayment`] used //! in the EVM payment verification flow. -use crate::common::{Address as RewardsAddress, QuoteHash}; +use crate::common::{Address as RewardsAddress, Amount, QuoteHash}; use crate::cryptography::hash as crypto_hash; -use crate::quoting_metrics::QuotingMetrics; use serde::{Deserialize, Serialize}; use std::fmt; use std::time::SystemTime; @@ -58,11 +57,11 @@ pub struct ProofOfPayment { impl ProofOfPayment { /// Returns a short digest of the proof of payment to use for on-chain verification. - pub fn digest(&self) -> Vec<(QuoteHash, QuotingMetrics, RewardsAddress)> { + pub fn digest(&self) -> Vec<(QuoteHash, Amount, RewardsAddress)> { self.peer_quotes .clone() .into_iter() - .map(|(_, quote)| (quote.hash(), quote.quoting_metrics, quote.rewards_address)) + .map(|(_, quote)| (quote.hash(), quote.price, quote.rewards_address)) .collect() } } @@ -78,8 +77,8 @@ pub struct PaymentQuote { pub content: XorName, /// The local node time when the quote was created pub timestamp: SystemTime, - /// Quoting metrics being used to generate this quote - pub quoting_metrics: QuotingMetrics, + /// The node-calculated price for storing this content + pub price: Amount, /// The node's wallet address pub rewards_address: RewardsAddress, /// The node's public key in bytes (ML-DSA-65) @@ -93,7 +92,7 @@ impl fmt::Debug for PaymentQuote { f.debug_struct("PaymentQuote") .field("content", &self.content) .field("timestamp", &self.timestamp) - .field("quoting_metrics", &self.quoting_metrics) + .field("price", &self.price) .field("rewards_address", &self.rewards_address) .finish_non_exhaustive() } @@ -112,7 +111,7 @@ impl PaymentQuote { pub fn bytes_for_signing( xorname: XorName, timestamp: SystemTime, - quoting_metrics: &QuotingMetrics, + price: &Amount, rewards_address: &RewardsAddress, ) -> Vec { let mut bytes = xorname.to_vec(); @@ -121,8 +120,7 @@ impl PaymentQuote { .unwrap_or_default() .as_secs(); bytes.extend_from_slice(&secs.to_le_bytes()); - let serialised_quoting_metrics = rmp_serde::to_vec(quoting_metrics).unwrap_or_default(); - bytes.extend_from_slice(&serialised_quoting_metrics); + bytes.extend_from_slice(&price.to_le_bytes::<32>()); bytes.extend_from_slice(rewards_address.as_slice()); bytes } @@ -132,7 +130,7 @@ impl PaymentQuote { Self::bytes_for_signing( self.content, self.timestamp, - &self.quoting_metrics, + &self.price, &self.rewards_address, ) } diff --git a/src/external_signer.rs b/src/external_signer.rs index 64b69df..26ffe04 100644 --- a/src/external_signer.rs +++ b/src/external_signer.rs @@ -68,12 +68,12 @@ pub fn pay_for_quotes_calldata>( let total_amount = payments.iter().map(|(_, _, amount)| amount).sum(); - let approve_spender = *network.data_payments_address(); + let approve_spender = *network.payment_vault_address(); let approve_amount = total_amount; let provider = http_provider(network.rpc_url().clone()); let data_payments = crate::contract::payment_vault::handler::PaymentVaultHandler::new( - *network.data_payments_address(), + *network.payment_vault_address(), provider, ); diff --git a/src/lib.rs b/src/lib.rs index 7df98aa..56d1aa8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,10 +12,8 @@ #![allow(clippy::enum_variant_names)] use crate::common::{Address, Amount}; -use crate::contract::merkle_payment_vault::error::Error as MerklePaymentError; -use crate::contract::merkle_payment_vault::handler::MerklePaymentVaultHandler; use crate::merkle_batch_payment::PoolCommitment; -use crate::utils::{get_evm_network, http_provider}; +use crate::utils::get_evm_network; use alloy::primitives::address; use alloy::transports::http::reqwest; use serde::{Deserialize, Serialize}; @@ -69,17 +67,13 @@ const ARBITRUM_ONE_PAYMENT_TOKEN_ADDRESS: Address = const ARBITRUM_SEPOLIA_TEST_PAYMENT_TOKEN_ADDRESS: Address = address!("4bc1aCE0E66170375462cB4E6Af42Ad4D5EC689C"); -const ARBITRUM_ONE_DATA_PAYMENTS_ADDRESS: Address = - address!("B1b5219f8Aaa18037A2506626Dd0406a46f70BcC"); +/// Unified payment vault address (handles both single-node and merkle payments). +const ARBITRUM_ONE_PAYMENT_VAULT_ADDRESS: Address = + address!("9A3EcAc693b699Fc0B2B6A50B5549e50c2320A26"); -const ARBITRUM_SEPOLIA_TEST_DATA_PAYMENTS_ADDRESS: Address = - address!("7f0842a78f7d4085d975ba91d630d680f91b1295"); - -const ARBITRUM_ONE_MERKLE_PAYMENTS_ADDRESS: Address = - address!("0x8c20E9A6e5e2aA038Ed463460E412B669fE712Aa"); - -const ARBITRUM_SEPOLIA_TEST_MERKLE_PAYMENTS_ADDRESS: Address = - address!("0x393F6825C248a29295A7f9Bfa03e475decb44dc0"); +/// Unified payment vault address on Arbitrum Sepolia (proxy contract). +const ARBITRUM_SEPOLIA_TEST_PAYMENT_VAULT_ADDRESS: Address = + address!("d742E8CFEf27A9a884F3EFfA239Ee2F39c276522"); #[serde_as] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -87,25 +81,18 @@ pub struct CustomNetwork { #[serde_as(as = "DisplayFromStr")] pub rpc_url_http: reqwest::Url, pub payment_token_address: Address, - pub data_payments_address: Address, - pub merkle_payments_address: Option
, + /// Unified payment vault handling both single-node and merkle payments. + pub payment_vault_address: Address, } impl CustomNetwork { - pub fn new( - rpc_url: &str, - payment_token_addr: &str, - data_payments_addr: &str, - merkle_payments_addr: Option<&str>, - ) -> Self { + pub fn new(rpc_url: &str, payment_token_addr: &str, payment_vault_addr: &str) -> Self { Self { rpc_url_http: reqwest::Url::parse(rpc_url).expect("Invalid RPC URL"), payment_token_address: Address::from_str(payment_token_addr) .expect("Invalid payment token address"), - data_payments_address: Address::from_str(data_payments_addr) - .expect("Invalid chunk payments address"), - merkle_payments_address: merkle_payments_addr - .map(|addr| Address::from_str(addr).expect("Invalid merkle payments address")), + payment_vault_address: Address::from_str(payment_vault_addr) + .expect("Invalid payment vault address"), } } } @@ -147,17 +134,11 @@ impl Network { }) } - pub fn new_custom( - rpc_url: &str, - payment_token_addr: &str, - chunk_payments_addr: &str, - merkle_payments_addr: Option<&str>, - ) -> Self { + pub fn new_custom(rpc_url: &str, payment_token_addr: &str, payment_vault_addr: &str) -> Self { Self::Custom(CustomNetwork::new( rpc_url, payment_token_addr, - chunk_payments_addr, - merkle_payments_addr, + payment_vault_addr, )) } @@ -185,59 +166,49 @@ impl Network { } } - pub fn data_payments_address(&self) -> &Address { - match self { - Network::ArbitrumOne => &ARBITRUM_ONE_DATA_PAYMENTS_ADDRESS, - Network::ArbitrumSepoliaTest => &ARBITRUM_SEPOLIA_TEST_DATA_PAYMENTS_ADDRESS, - Network::Custom(custom) => &custom.data_payments_address, - } - } - - pub fn merkle_payments_address(&self) -> Option<&Address> { + /// Unified payment vault address (handles both single-node and merkle payments). + pub fn payment_vault_address(&self) -> &Address { match self { - Network::ArbitrumOne => Some(&ARBITRUM_ONE_MERKLE_PAYMENTS_ADDRESS), - Network::ArbitrumSepoliaTest => Some(&ARBITRUM_SEPOLIA_TEST_MERKLE_PAYMENTS_ADDRESS), - Network::Custom(custom) => custom.merkle_payments_address.as_ref(), + Network::ArbitrumOne => &ARBITRUM_ONE_PAYMENT_VAULT_ADDRESS, + Network::ArbitrumSepoliaTest => &ARBITRUM_SEPOLIA_TEST_PAYMENT_VAULT_ADDRESS, + Network::Custom(custom) => &custom.payment_vault_address, } } - /// Estimate the cost of a Merkle tree batch using smart contract view function (0 gas). + /// Estimate the cost of a Merkle tree batch locally. + /// + /// Estimates the worst-case cost of a Merkle tree payment locally. /// - /// This calls the smart contract's view function which runs the exact same - /// pricing logic as the actual payment, ensuring accurate cost estimation. - /// No wallet is needed since view functions don't require signing. + /// The Solidity contract charges `median16(quotes) * (1 << depth)` for the + /// winning pool. The only unknown is which pool wins, so the worst case is + /// the pool with the highest `median(prices) * 2^depth`. No on-chain call + /// is made. /// /// # Arguments /// * `depth` - The Merkle tree depth - /// * `pool_commitments` - Vector of pool commitments with metrics (one per reward pool) - /// * `merkle_payment_timestamp` - Unix timestamp for the payment + /// * `pool_commitments` - Vector of pool commitments with prices (one per reward pool) /// /// # Returns - /// * Estimated total cost in AttoTokens - pub async fn estimate_merkle_payment_cost( + /// * Worst-case cost estimate in AttoTokens + pub fn estimate_merkle_payment_cost( &self, depth: u8, pool_commitments: &[PoolCommitment], - merkle_payment_timestamp: u64, - ) -> Result { + ) -> Amount { if pool_commitments.is_empty() { - return Ok(Amount::ZERO); + return Amount::ZERO; } - // Create provider (no wallet needed for view calls) - let provider = http_provider(self.rpc_url().clone()); - - // Get Merkle payment vault address - let merkle_vault_address = *self - .merkle_payments_address() - .ok_or(MerklePaymentError::MerklePaymentsAddressNotConfigured)?; - - // Create handler and call the contract's view function - let handler = MerklePaymentVaultHandler::new(merkle_vault_address, provider); - let total_amount = handler - .estimate_merkle_tree_cost(depth, pool_commitments.to_vec(), merkle_payment_timestamp) - .await?; - - Ok(total_amount) + let multiplier = Amount::from(1u64 << depth); + pool_commitments + .iter() + .map(|pool| { + let mut prices: Vec = pool.candidates.iter().map(|c| c.price).collect(); + prices.sort_unstable(); // ascending + // Upper median (index 8 of 16) — matches Solidity's median16 (k = 8) + prices[prices.len() / 2] * multiplier + }) + .max() + .unwrap_or(Amount::ZERO) } } diff --git a/src/merkle_batch_payment.rs b/src/merkle_batch_payment.rs index 9320a2d..205ead2 100644 --- a/src/merkle_batch_payment.rs +++ b/src/merkle_batch_payment.rs @@ -12,13 +12,11 @@ //! mock implementation of the smart contract. When the real smart contract is ready, the //! disk contract will be replaced with actual on-chain calls. -use crate::common::{Address as RewardsAddress, U256}; -use crate::contract::data_type_conversion; -use crate::quoting_metrics::QuotingMetrics; -use serde::{Deserialize, Serialize}; +use crate::common::{Address as RewardsAddress, Amount}; #[cfg(test)] -use crate::common::Amount; +use crate::common::U256; +use serde::{Deserialize, Serialize}; #[cfg(test)] use std::path::PathBuf; @@ -26,21 +24,6 @@ use std::path::PathBuf; #[cfg(test)] use thiserror::Error; -/// Error returned when `total_cost_unit` exceeds the 248-bit limit during packing. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct CostUnitOverflow; - -impl std::fmt::Display for CostUnitOverflow { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "total_cost_unit exceeds {TOTAL_COST_UNIT_BITS}-bit limit (top 8 bits reserved for packing)" - ) - } -} - -impl std::error::Error for CostUnitOverflow {} - /// Pool hash type (32 bytes) - compatible with XorName without the dependency pub type PoolHash = [u8; 32]; @@ -50,33 +33,9 @@ pub const CANDIDATES_PER_POOL: usize = 16; /// Maximum supported Merkle tree depth pub const MAX_MERKLE_DEPTH: u8 = 8; -/// Number of bits available for total_cost_unit when packed with data_type (u8 = 8 bits) -const TOTAL_COST_UNIT_BITS: usize = 248; - -/// Cost unit weights per data type, matching the production contract's `costUnitPerDataType` mapping. -/// These weights determine the relative storage cost of each data type. -const COST_UNIT_GRAPH_ENTRY: u64 = 1; -const COST_UNIT_SCRATCHPAD: u64 = 100; -const COST_UNIT_CHUNK: u64 = 10; -const COST_UNIT_POINTER: u64 = 20; - -/// Get the cost unit for a Solidity DataType index. -/// -/// Matches the contract's `costUnitPerDataType` mapping: -/// GraphEntry(0) = 1, Scratchpad(1) = 100, Chunk(2) = 10, Pointer(3) = 20 -fn cost_unit_for_data_type(solidity_data_type: u8) -> U256 { - match solidity_data_type { - 0 => U256::from(COST_UNIT_GRAPH_ENTRY), - 1 => U256::from(COST_UNIT_SCRATCHPAD), - 2 => U256::from(COST_UNIT_CHUNK), - 3 => U256::from(COST_UNIT_POINTER), - _ => U256::ZERO, - } -} - /// Calculate expected number of reward pools for a given tree depth /// -/// Formula: 2^ceil(depth/2) +/// Formula: 2^ceil(depth/2) — must match `MerklePaymentLib.expectedRewardPools` in Solidity pub fn expected_reward_pools(depth: u8) -> usize { let half_depth = depth.div_ceil(2); 1 << half_depth @@ -92,126 +51,34 @@ pub struct PoolCommitment { /// This commits to the midpoint proof and all node signatures pub pool_hash: PoolHash, - /// Candidate nodes with metrics + /// Candidate nodes with prices pub candidates: [CandidateNode; CANDIDATES_PER_POOL], } -/// Candidate node with metrics for pool commitment -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct CandidateNode { - /// Rewards address of the candidate node - pub rewards_address: RewardsAddress, - - /// Metrics of the candidate node - pub metrics: QuotingMetrics, -} - -/// Packed candidate node for compact calldata (v2) +/// Candidate node with price for pool commitment /// -/// This struct packs the data type and total cost unit into a single U256 to reduce calldata size. -/// The packing format is: `packed = (totalCostUnit << 8) | dataType` -/// - dataType occupies bits 0-7 (lower 8 bits) -/// - totalCostUnit occupies bits 8-255 +/// Nodes calculate their own price as `(chunks_stored / 6000)^2`. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct CandidateNodePacked { +pub struct CandidateNode { /// Rewards address of the candidate node pub rewards_address: RewardsAddress, - /// Packed data: (totalCostUnit << 8) | dataType - pub data_type_and_total_cost_unit: U256, + /// Node-calculated price + pub price: Amount, } -/// Packed pool commitment for compact calldata (v2) -/// -/// Uses CandidateNodePacked instead of CandidateNode for smaller calldata. +/// What's stored on-chain (or disk) - indexed by winner_pool_hash #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct PoolCommitmentPacked { - /// Hash of the full MerklePaymentCandidatePool (cryptographic commitment) - pub pool_hash: PoolHash, - - /// Packed candidate nodes - pub candidates: [CandidateNodePacked; CANDIDATES_PER_POOL], -} - -/// Encode data type and total cost unit into a single U256 -/// -/// Format: `packed = (totalCostUnit << 8) | dataType` -/// - dataType occupies bits 0-7 (lower 8 bits) -/// - totalCostUnit occupies bits 8-255 -pub fn encode_data_type_and_cost( - data_type: u8, - total_cost_unit: U256, -) -> Result { - if total_cost_unit >= (U256::from(1) << TOTAL_COST_UNIT_BITS) { - return Err(CostUnitOverflow); - } - Ok((total_cost_unit << 8) | U256::from(data_type)) -} - -/// Decode data type and total cost unit from a packed U256 -/// -/// Returns (data_type, total_cost_unit) -#[cfg(test)] -pub fn decode_data_type_and_cost(packed: U256) -> (u8, U256) { - let data_type = (packed & U256::from(0xFF)).to::(); - let total_cost_unit = packed >> 8; - (data_type, total_cost_unit) -} - -/// Calculate total cost unit from QuotingMetrics -/// -/// Matches the contract's `_getTotalCostUnit`: for each record type, multiplies the record -/// count by that type's `costUnitPerDataType` weight, then sums the results. -/// Falls back to close_records_stored if records_per_type is empty (fresh nodes). -/// Uses a minimum of 1 to ensure non-zero cost unit. -pub fn calculate_total_cost_unit(metrics: &QuotingMetrics) -> U256 { - // Sum: costUnitPerDataType[dataType] * records, matching the contract - let total_from_types: U256 = - metrics - .records_per_type - .iter() - .fold(U256::ZERO, |acc, (data_type, count)| { - let solidity_type = data_type_conversion(*data_type); - acc + cost_unit_for_data_type(solidity_type) * U256::from(*count) - }); - - if total_from_types > U256::ZERO { - total_from_types - } else { - // Use close_records_stored as fallback for fresh nodes, with minimum of 1 - let fallback = std::cmp::max(metrics.close_records_stored as u64, 1); - let solidity_type = data_type_conversion(metrics.data_type); - cost_unit_for_data_type(solidity_type) * U256::from(fallback) - } -} +pub struct OnChainPaymentInfo { + /// Tree depth + pub depth: u8, -impl CandidateNode { - /// Convert to packed format for v2 contract calls - pub fn to_packed(&self) -> Result { - let data_type = data_type_conversion(self.metrics.data_type); - let total_cost_unit = calculate_total_cost_unit(&self.metrics); - Ok(CandidateNodePacked { - rewards_address: self.rewards_address, - data_type_and_total_cost_unit: encode_data_type_and_cost(data_type, total_cost_unit)?, - }) - } -} + /// Merkle payment timestamp provided by client (unix seconds) + /// This is the timestamp that all nodes in the pool used for their quotes + pub merkle_payment_timestamp: u64, -impl PoolCommitment { - /// Convert to packed format for v2 contract calls - pub fn to_packed(&self) -> Result { - let mut packed_candidates = Vec::with_capacity(CANDIDATES_PER_POOL); - for c in &self.candidates { - packed_candidates.push(c.to_packed()?); - } - let candidates: [CandidateNodePacked; CANDIDATES_PER_POOL] = packed_candidates - .try_into() - .expect("Vec length matches CANDIDATES_PER_POOL"); - Ok(PoolCommitmentPacked { - pool_hash: self.pool_hash, - candidates, - }) - } + /// Addresses of the 'depth' nodes that were paid, with their pool index and paid amount + pub paid_node_addresses: Vec<(RewardsAddress, usize, Amount)>, } #[cfg(test)] @@ -237,20 +104,6 @@ pub enum SmartContractError { JsonError(#[from] serde_json::Error), } -/// What's stored on-chain (or disk) - indexed by winner_pool_hash -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -pub struct OnChainPaymentInfo { - /// Tree depth - pub depth: u8, - - /// Merkle payment timestamp provided by client (unix seconds) - /// This is the timestamp that all nodes in the pool used for their quotes - pub merkle_payment_timestamp: u64, - - /// Addresses of the 'depth' nodes that were paid along with their indices in the winner pool - pub paid_node_addresses: Vec<(RewardsAddress, usize)>, -} - #[cfg(test)] /// Disk-based Merkle payment contract (mock for testing) /// @@ -350,16 +203,21 @@ impl DiskMerklePaymentContract { winner_node_indices.len() ); + // Calculate total amount from winner node prices + let mut total_amount = Amount::ZERO; + // Extract paid node addresses, along with their indices let mut paid_node_addresses = Vec::new(); for (i, &node_idx) in winner_node_indices.iter().enumerate() { - let addr = winner_pool.candidates[node_idx].rewards_address; - paid_node_addresses.push((addr, node_idx)); - println!(" Node {}: {addr}", i + 1); + let candidate = &winner_pool.candidates[node_idx]; + let addr = candidate.rewards_address; + paid_node_addresses.push((addr, node_idx, candidate.price)); + total_amount += candidate.price; + println!(" Node {}: {addr} (price: {})", i + 1, candidate.price); } println!( - "\nSimulating payment to {} nodes...", + "\nSimulating payment to {} nodes, total: {total_amount}...", paid_node_addresses.len() ); println!("=========================\n"); @@ -379,10 +237,7 @@ impl DiskMerklePaymentContract { println!("✓ Stored payment info to: {}", file_path.display()); - // placeholder amount based on depth - let placeholder_amount = Amount::from(2_u64.pow(depth as u32)); - - Ok((winner_pool_hash, placeholder_amount)) + Ok((winner_pool_hash, total_amount)) } /// Get payment info by winner pool hash @@ -405,150 +260,30 @@ mod tests { use super::*; #[test] - fn test_encode_decode_data_type_and_cost() { - // Test basic encoding/decoding - let data_type: u8 = 2; // Chunk - let total_cost_unit = U256::from(1000u64); // Record count - - let packed = encode_data_type_and_cost(data_type, total_cost_unit).unwrap(); - let (decoded_type, decoded_cost) = decode_data_type_and_cost(packed); - - assert_eq!(decoded_type, data_type); - assert_eq!(decoded_cost, total_cost_unit); - } - - #[test] - fn test_encode_decode_boundary_values() { - // Test with max u8 data type - let data_type: u8 = 255; - let total_cost_unit = U256::from(100u64); - - let packed = encode_data_type_and_cost(data_type, total_cost_unit).unwrap(); - let (decoded_type, decoded_cost) = decode_data_type_and_cost(packed); - - assert_eq!(decoded_type, data_type); - assert_eq!(decoded_cost, total_cost_unit); - } - - #[test] - fn test_encode_decode_zero_values() { - // Test with zero values - let packed = encode_data_type_and_cost(0, U256::ZERO).unwrap(); - let (decoded_type, decoded_cost) = decode_data_type_and_cost(packed); - - assert_eq!(decoded_type, 0); - assert_eq!(decoded_cost, U256::ZERO); - } - - #[test] - fn test_encode_returns_error_on_overflow() { - // total_cost_unit that uses all 256 bits should fail - let overflow_value = U256::MAX; - let result = encode_data_type_and_cost(0, overflow_value); - assert!(result.is_err()); - } - - #[test] - fn test_encode_decode_large_cost() { - // Test with large cost (U256 max - some value to avoid overflow when shifting) - let data_type: u8 = 1; - let large_cost = U256::from(u128::MAX); - - let packed = encode_data_type_and_cost(data_type, large_cost).unwrap(); - let (decoded_type, decoded_cost) = decode_data_type_and_cost(packed); - - assert_eq!(decoded_type, data_type); - assert_eq!(decoded_cost, large_cost); - } - - #[test] - fn test_calculate_total_cost_unit() { - let metrics = QuotingMetrics { - data_type: 0, - data_size: 1024 * 1024, - close_records_stored: 100, - records_per_type: vec![(0, 10), (1, 20), (2, 5)], - max_records: 1000, - received_payment_count: 50, - live_time: 3600, - network_density: None, - network_size: Some(1000), - }; - - let total_cost = calculate_total_cost_unit(&metrics); - - // Rust type 0 -> Chunk(cost=10): 10 * 10 = 100 - // Rust type 1 -> GraphEntry(cost=1): 20 * 1 = 20 - // Rust type 2 -> Pointer(cost=20): 5 * 20 = 100 - // Total = 220 - assert_eq!(total_cost, U256::from(220u64)); - } - - #[test] - fn test_calculate_total_cost_unit_empty_records() { - let metrics = QuotingMetrics { - data_type: 0, - data_size: 1024, - close_records_stored: 0, - records_per_type: vec![], - max_records: 1000, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - }; - - let total_cost = calculate_total_cost_unit(&metrics); - // Fallback: max(0, 1) = 1 record, data_type 0 -> Chunk(cost=10), total = 10 - assert_eq!(total_cost, U256::from(10u64)); + fn test_expected_reward_pools() { + assert_eq!(expected_reward_pools(1), 2); + assert_eq!(expected_reward_pools(2), 2); + assert_eq!(expected_reward_pools(3), 4); + assert_eq!(expected_reward_pools(4), 4); + assert_eq!(expected_reward_pools(8), 16); } #[test] - fn test_candidate_node_to_packed() { - let metrics = QuotingMetrics { - data_type: 0, // Will be converted to 2 (Chunk) by data_type_conversion - data_size: 1024 * 1024, - close_records_stored: 100, - records_per_type: vec![(0, 10)], - max_records: 1000, - received_payment_count: 50, - live_time: 3600, - network_density: None, - network_size: Some(1000), - }; - + fn test_candidate_node_price() { let candidate = CandidateNode { rewards_address: RewardsAddress::from([0x42; 20]), - metrics, + price: U256::from(1000u64), }; - - let packed = candidate.to_packed().unwrap(); - - assert_eq!(packed.rewards_address, candidate.rewards_address); - - // Decode and verify - let (data_type, total_cost) = - decode_data_type_and_cost(packed.data_type_and_total_cost_unit); - assert_eq!(data_type, 2); // Chunk data type after conversion - assert_eq!(total_cost, U256::from(100u64)); // 10 records * Chunk cost unit (10) + assert_eq!(candidate.price, U256::from(1000u64)); + assert_eq!(candidate.rewards_address, RewardsAddress::from([0x42; 20])); } #[test] - fn test_pool_commitment_to_packed() { + fn test_pool_commitment_structure() { let candidates: [CandidateNode; CANDIDATES_PER_POOL] = std::array::from_fn(|i| CandidateNode { rewards_address: RewardsAddress::from([i as u8; 20]), - metrics: QuotingMetrics { - data_type: 0, - data_size: 1024, - close_records_stored: i * 10, - records_per_type: vec![(0, i as u32)], - max_records: 1000, - received_payment_count: i, - live_time: 3600, - network_density: None, - network_size: None, - }, + price: U256::from((i as u64 + 1) * 100), }); let pool = PoolCommitment { @@ -556,15 +291,9 @@ mod tests { candidates, }; - let packed = pool.to_packed().unwrap(); - - assert_eq!(packed.pool_hash, pool.pool_hash); - assert_eq!(packed.candidates.len(), CANDIDATES_PER_POOL); - - // Verify first candidate - assert_eq!( - packed.candidates[0].rewards_address, - pool.candidates[0].rewards_address - ); + assert_eq!(pool.pool_hash, [0x42; 32]); + assert_eq!(pool.candidates.len(), CANDIDATES_PER_POOL); + assert_eq!(pool.candidates[0].price, U256::from(100u64)); + assert_eq!(pool.candidates[15].price, U256::from(1600u64)); } } diff --git a/src/merkle_payments/merkle_payment.rs b/src/merkle_payments/merkle_payment.rs index a6f27b3..492a120 100644 --- a/src/merkle_payments/merkle_payment.rs +++ b/src/merkle_payments/merkle_payment.rs @@ -2,13 +2,8 @@ // // This SAFE Network Software is licensed to you under The General Public License (GPL), version 3. -use crate::common::Address as RewardsAddress; -use crate::contract::data_type_conversion; -use crate::merkle_batch_payment::{ - CANDIDATES_PER_POOL, CandidateNode, CostUnitOverflow, PoolCommitment, PoolCommitmentPacked, - PoolHash, calculate_total_cost_unit, encode_data_type_and_cost, -}; -use crate::quoting_metrics::QuotingMetrics; +use crate::common::{Address as RewardsAddress, Amount}; +use crate::merkle_batch_payment::{CANDIDATES_PER_POOL, CandidateNode, PoolCommitment, PoolHash}; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use thiserror::Error; @@ -35,12 +30,6 @@ pub enum MerklePaymentVerificationError { expected: u32, got: u32, }, - #[error("Data size mismatch for node {address}: expected {expected}, got {got}")] - DataSizeMismatch { - address: RewardsAddress, - expected: usize, - got: usize, - }, #[error("Commitment does not match pool")] CommitmentDoesNotMatchPool, #[error("Paid node index {index} out of bounds (pool size: {pool_size})")] @@ -51,18 +40,16 @@ pub enum MerklePaymentVerificationError { expected: RewardsAddress, got: RewardsAddress, }, - #[error("Winner pool hash not found in on-chain packed commitments")] + #[error("Winner pool hash not found in on-chain commitments")] WinnerPoolNotInCommitments, #[error( - "Cost unit mismatch at index {index}: on_chain={on_chain_packed}, expected={expected_packed}" + "Price mismatch at index {index}: on_chain={on_chain_price}, expected={expected_price}" )] - CostUnitMismatch { + PriceMismatch { index: usize, - on_chain_packed: String, - expected_packed: String, + on_chain_price: String, + expected_price: String, }, - #[error("Cost unit overflow during packing: {0}")] - CostUnitOverflow(#[from] CostUnitOverflow), } /// A node's signed quote for potential reward eligibility. @@ -75,8 +62,8 @@ pub struct MerklePaymentCandidateNode { /// Node's public key bytes (ML-DSA-65) pub pub_key: Vec, - /// Node's storage metrics at quote time - pub quoting_metrics: QuotingMetrics, + /// Node-calculated price for storing data + pub price: Amount, /// Node's Ethereum address for payment pub reward_address: RewardsAddress, @@ -84,19 +71,19 @@ pub struct MerklePaymentCandidateNode { /// Quote timestamp (provided by the client) pub merkle_payment_timestamp: u64, - /// Signature over hash(quoting_metrics || reward_address || timestamp) + /// Signature over hash(price || reward_address || timestamp) pub signature: Vec, } impl MerklePaymentCandidateNode { /// Get the bytes to sign. pub fn bytes_to_sign( - quoting_metrics: &QuotingMetrics, + price: &Amount, reward_address: &RewardsAddress, timestamp: u64, ) -> Vec { let mut bytes = Vec::new(); - bytes.extend_from_slice("ing_metrics.to_bytes()); + bytes.extend_from_slice(&price.to_le_bytes::<32>()); bytes.extend_from_slice(reward_address.as_slice()); bytes.extend_from_slice(×tamp.to_le_bytes()); bytes @@ -106,7 +93,7 @@ impl MerklePaymentCandidateNode { pub(crate) fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); bytes.extend_from_slice(&self.pub_key); - bytes.extend_from_slice(&self.quoting_metrics.to_bytes()); + bytes.extend_from_slice(&self.price.to_le_bytes::<32>()); bytes.extend_from_slice(self.reward_address.as_slice()); bytes.extend_from_slice(&self.merkle_payment_timestamp.to_le_bytes()); bytes.extend_from_slice(&self.signature); @@ -150,7 +137,7 @@ impl MerklePaymentCandidatePool { let candidates: [CandidateNode; CANDIDATES_PER_POOL] = self.candidate_nodes.clone().map(|node| CandidateNode { rewards_address: node.reward_address, - metrics: node.quoting_metrics.clone(), + price: node.price, }); PoolCommitment { @@ -159,15 +146,10 @@ impl MerklePaymentCandidatePool { } } - /// Convert to packed commitment for compact calldata (v2). - pub fn to_commitment_packed(&self) -> Result { - self.to_commitment().to_packed() - } - - /// Verify that on-chain cost units match what the signed metrics produce. - pub fn verify_cost_units( + /// Verify that on-chain prices match what the signed nodes report. + pub fn verify_prices( &self, - on_chain_commitments: &[PoolCommitmentPacked], + on_chain_commitments: &[PoolCommitment], winner_pool_hash: &PoolHash, ) -> Result<(), MerklePaymentVerificationError> { let on_chain_winner = on_chain_commitments @@ -181,16 +163,11 @@ impl MerklePaymentCandidatePool { .zip(self.candidate_nodes.iter()) .enumerate() { - let expected_data_type = data_type_conversion(signed_node.quoting_metrics.data_type); - let expected_cost_unit = calculate_total_cost_unit(&signed_node.quoting_metrics); - let expected_packed = - encode_data_type_and_cost(expected_data_type, expected_cost_unit)?; - - if on_chain_candidate.data_type_and_total_cost_unit != expected_packed { - return Err(MerklePaymentVerificationError::CostUnitMismatch { + if on_chain_candidate.price != signed_node.price { + return Err(MerklePaymentVerificationError::PriceMismatch { index: i, - on_chain_packed: on_chain_candidate.data_type_and_total_cost_unit.to_string(), - expected_packed: expected_packed.to_string(), + on_chain_price: on_chain_candidate.price.to_string(), + expected_price: signed_node.price.to_string(), }); } } diff --git a/src/merkle_payments/merkle_tree.rs b/src/merkle_payments/merkle_tree.rs index 5e2cf56..b592ea9 100644 --- a/src/merkle_payments/merkle_tree.rs +++ b/src/merkle_payments/merkle_tree.rs @@ -373,7 +373,7 @@ pub fn midpoint_proof_depth(depth: u8) -> u8 { } fn midpoint_level(depth: u8) -> usize { - (depth / 2) as usize + depth.div_ceil(2) as usize } /// Errors for Merkle proof verification. diff --git a/src/quoting_metrics.rs b/src/quoting_metrics.rs index 508a385..a35888b 100644 --- a/src/quoting_metrics.rs +++ b/src/quoting_metrics.rs @@ -21,8 +21,6 @@ pub struct QuotingMetrics { pub close_records_stored: usize, /// each entry to be `(data_type_index, num_of_records_of_that_type)` pub records_per_type: Vec<(u32, u32)>, - /// the max_records configured - pub max_records: usize, /// number of times that got paid pub received_payment_count: usize, /// the duration that node keeps connected to the network, measured in hours @@ -40,12 +38,11 @@ impl Debug for QuotingMetrics { write!( formatter, - "QuotingMetrics {{ data_type: {}, data_size: {}, close_records_stored: {}, records_per_type {:?}, max_records: {}, received_payment_count: {}, live_time: {}, network_density: {density_u256:?}, network_size: {:?} }}", + "QuotingMetrics {{ data_type: {}, data_size: {}, close_records_stored: {}, records_per_type {:?}, received_payment_count: {}, live_time: {}, network_density: {density_u256:?}, network_size: {:?} }}", self.data_type, self.data_size, self.close_records_stored, self.records_per_type, - self.max_records, self.received_payment_count, self.live_time, self.network_size @@ -69,7 +66,6 @@ impl QuotingMetrics { bytes.extend_from_slice(&dtype.to_le_bytes()); bytes.extend_from_slice(&count.to_le_bytes()); } - bytes.extend_from_slice(&(self.max_records as u64).to_le_bytes()); bytes.extend_from_slice(&(self.received_payment_count as u64).to_le_bytes()); bytes.extend_from_slice(&self.live_time.to_le_bytes()); if let Some(density) = &self.network_density { diff --git a/src/testnet.rs b/src/testnet.rs index d6d2255..d6e2080 100644 --- a/src/testnet.rs +++ b/src/testnet.rs @@ -10,9 +10,8 @@ use std::env; use std::num::ParseIntError; use crate::common::Address; -use crate::contract::merkle_payment_vault::handler::MerklePaymentVaultHandler; use crate::contract::payment_vault::handler::PaymentVaultHandler; -use crate::contract::{merkle_payment_vault, network_token::NetworkToken, payment_vault}; +use crate::contract::{network_token::NetworkToken, payment_vault}; use crate::reqwest::Url; use crate::{CustomNetwork, Network}; use alloy::hex::ToHexExt; @@ -41,29 +40,24 @@ pub struct Testnet { anvil: AnvilInstance, rpc_url: Url, network_token_address: Address, - data_payments_address: Address, - merkle_payments_address: Address, + payment_vault_address: Address, } impl Testnet { - /// Starts an Anvil node and automatically deploys the network token and chunk payments smart contracts. + /// Starts an Anvil node and automatically deploys the network token and unified payment vault contracts. pub async fn new() -> Result { let (node, rpc_url) = start_node()?; let network_token = deploy_network_token_contract(&rpc_url, &node).await?; - let data_payments = - deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address()) - .await?; - let merkle_payments = - deploy_merkle_payments_contract(&rpc_url, &node, *network_token.contract.address()) + let payment_vault = + deploy_payment_vault_contract(&rpc_url, &node, *network_token.contract.address()) .await?; Ok(Testnet { anvil: node, rpc_url, network_token_address: *network_token.contract.address(), - data_payments_address: *data_payments.contract.address(), - merkle_payments_address: *merkle_payments.contract.address(), + payment_vault_address: *payment_vault.contract.address(), }) } @@ -71,8 +65,7 @@ impl Testnet { Network::Custom(CustomNetwork { rpc_url_http: self.rpc_url.clone(), payment_token_address: self.network_token_address, - data_payments_address: self.data_payments_address, - merkle_payments_address: Some(self.merkle_payments_address), + payment_vault_address: self.payment_vault_address, }) } @@ -87,8 +80,8 @@ impl Testnet { Ok(signer.to_bytes().encode_hex_with_prefix()) } - pub fn merkle_payments_address(&self) -> Address { - self.merkle_payments_address + pub fn payment_vault_address(&self) -> Address { + self.payment_vault_address } } @@ -164,7 +157,7 @@ pub async fn deploy_network_token_contract( Ok(NetworkToken::deploy(provider).await) } -pub async fn deploy_data_payments_contract( +pub async fn deploy_payment_vault_contract( rpc_url: &Url, anvil: &AnvilInstance, token_address: Address, @@ -201,7 +194,7 @@ pub async fn deploy_data_payments_contract( .wallet(wallet) .connect_http(rpc_url.clone()); - // Deploy the contract. + // Deploy the unified payment vault contract. let payment_vault_contract_address = payment_vault::implementation::deploy(&provider, token_address).await; @@ -212,54 +205,6 @@ pub async fn deploy_data_payments_contract( )) } -pub async fn deploy_merkle_payments_contract( - rpc_url: &Url, - anvil: &AnvilInstance, - token_address: Address, -) -> Result< - MerklePaymentVaultHandler< - FillProvider< - JoinFill< - JoinFill< - JoinFill< - Identity, - JoinFill< - GasFiller, - JoinFill>, - >, - >, - NonceFiller, - >, - WalletFiller, - >, - RootProvider, - Ethereum, - >, - Ethereum, - >, - TestnetError, -> { - // Set up signer from the third default Anvil account (Charlie). - let key = anvil.keys().get(2).ok_or(TestnetError::MissingKey(2))?; - let signer: PrivateKeySigner = key.clone().into(); - let wallet = EthereumWallet::from(signer); - - let provider = ProviderBuilder::new() - .with_simple_nonce_management() - .wallet(wallet) - .connect_http(rpc_url.clone()); - - // Deploy the contract. - let merkle_payment_vault_contract_address = - merkle_payment_vault::implementation::deploy(&provider, token_address).await; - - // Create a handler for the deployed contract - Ok(MerklePaymentVaultHandler::new( - merkle_payment_vault_contract_address, - provider, - )) -} - #[cfg(test)] mod tests { use crate::testnet::Testnet; diff --git a/src/utils.rs b/src/utils.rs index 940ce48..4d7a3bc 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -27,10 +27,8 @@ pub const RPC_URL: &str = "RPC_URL"; const RPC_URL_BUILD_TIME_VAL: Option<&str> = option_env!("RPC_URL"); pub const PAYMENT_TOKEN_ADDRESS: &str = "PAYMENT_TOKEN_ADDRESS"; const PAYMENT_TOKEN_ADDRESS_BUILD_TIME_VAL: Option<&str> = option_env!("PAYMENT_TOKEN_ADDRESS"); -pub const DATA_PAYMENTS_ADDRESS: &str = "DATA_PAYMENTS_ADDRESS"; -const DATA_PAYMENTS_ADDRESS_BUILD_TIME_VAL: Option<&str> = option_env!("DATA_PAYMENTS_ADDRESS"); -pub const MERKLE_PAYMENTS_ADDRESS: &str = "MERKLE_PAYMENTS_ADDRESS"; -const MERKLE_PAYMENTS_ADDRESS_BUILD_TIME_VAL: Option<&str> = option_env!("MERKLE_PAYMENTS_ADDRESS"); +pub const PAYMENT_VAULT_ADDRESS: &str = "PAYMENT_VAULT_ADDRESS"; +const PAYMENT_VAULT_ADDRESS_BUILD_TIME_VAL: Option<&str> = option_env!("PAYMENT_VAULT_ADDRESS"); #[derive(thiserror::Error, Debug)] pub enum Error { @@ -122,14 +120,14 @@ fn get_evm_network_from_env() -> Result { env::var(PAYMENT_TOKEN_ADDRESS) .ok() .or_else(|| PAYMENT_TOKEN_ADDRESS_BUILD_TIME_VAL.map(|s| s.to_string())), - env::var(DATA_PAYMENTS_ADDRESS) + env::var(PAYMENT_VAULT_ADDRESS) .ok() - .or_else(|| DATA_PAYMENTS_ADDRESS_BUILD_TIME_VAL.map(|s| s.to_string())), + .or_else(|| PAYMENT_VAULT_ADDRESS_BUILD_TIME_VAL.map(|s| s.to_string())), ] .into_iter() .map(|var| { var.ok_or(Error::FailedToGetEvmNetwork(format!( - "missing env var, make sure to set all of: {RPC_URL}, {PAYMENT_TOKEN_ADDRESS}, {DATA_PAYMENTS_ADDRESS}" + "missing env var, make sure to set all of: {RPC_URL}, {PAYMENT_TOKEN_ADDRESS}, {PAYMENT_VAULT_ADDRESS}" ))) }) .collect::, Error>>(); @@ -159,16 +157,7 @@ fn get_evm_network_from_env() -> Result { Ok(Network::ArbitrumSepoliaTest) } else if let Ok(evm_vars) = evm_vars { info!("Using custom EVM network from environment variables"); - let merkle_addr = env::var(MERKLE_PAYMENTS_ADDRESS) - .ok() - .or_else(|| MERKLE_PAYMENTS_ADDRESS_BUILD_TIME_VAL.map(|s| s.to_string())); - - let network = CustomNetwork::new( - &evm_vars[0], - &evm_vars[1], - &evm_vars[2], - merkle_addr.as_deref(), - ); + let network = CustomNetwork::new(&evm_vars[0], &evm_vars[1], &evm_vars[2]); Ok(Network::Custom(network)) } else if use_local_evm { Ok(local_evm_network_hardcoded()) @@ -182,13 +171,11 @@ fn get_evm_network_from_env() -> Result { /// Get the `Network::Custom` from the hardcoded values. fn local_evm_network_hardcoded() -> Network { - // Merkle payments address is deterministic when deployed by Anvil's third default account (Charlie) - // Deployed at nonce 0 by account 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 + // Payment vault address is deterministic when deployed by Anvil's second default account (Bob) let network = CustomNetwork::new( "http://localhost:61611", "0x5FbDB2315678afecb367f032d93F642f64180aa3", "0x8464135c8F25Da09e49BC8782676a84730C318bC", - Some("0x663F3ad617193148711d28f5334eE4Ed07016602"), ); Network::Custom(network) } diff --git a/src/wallet.rs b/src/wallet.rs index 734cac5..5e70613 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -7,14 +7,11 @@ // permissions and limitations relating to use of the SAFE Network Software. use crate::common::{Address, Amount, QuoteHash, QuotePayment, TxHash, U256}; -use crate::contract::merkle_payment_vault::error::Error as MerkleHandlerError; -use crate::contract::merkle_payment_vault::handler::MerklePaymentVaultHandler; -use crate::contract::merkle_payment_vault::interface::PoolHash; use crate::contract::network_token::NetworkToken; use crate::contract::payment_vault::MAX_TRANSFERS_PER_TRANSACTION; use crate::contract::payment_vault::handler::PaymentVaultHandler; use crate::contract::{network_token, payment_vault}; -use crate::merkle_batch_payment::{CostUnitOverflow, PoolCommitment}; +use crate::merkle_batch_payment::{PoolCommitment, PoolHash}; use crate::retry::GasInfo; use crate::transaction_config::TransactionConfig; use crate::utils::http_provider; @@ -43,12 +40,8 @@ pub enum Error { RpcError(#[from] RpcError), #[error("Network token contract error: {0}")] NetworkTokenContract(#[from] network_token::Error), - #[error("Chunk payments contract error: {0}")] - ChunkPaymentsContract(#[from] payment_vault::error::Error), - #[error("Merkle payment vault contract error: {0}")] - MerklePaymentVaultContract(#[from] MerkleHandlerError), - #[error("Cost unit packing overflow: {0}")] - CostUnitOverflow(#[from] CostUnitOverflow), + #[error("Payment vault contract error: {0}")] + PaymentVaultContract(#[from] payment_vault::error::Error), } #[derive(Clone, Debug)] @@ -162,33 +155,24 @@ impl Wallet { .await } - /// Pay for a Merkle tree batch using packed calldata. + /// Pay for a Merkle tree batch. /// - /// Estimates the cost via the contract's view function, validates balance - /// and allowance, packs commitments for compact calldata, then submits. + /// Estimates the cost locally from candidate prices, validates balance + /// and allowance, then submits pool commitments on-chain. pub async fn pay_for_merkle_tree( &self, depth: u8, pool_commitments: Vec, merkle_payment_timestamp: u64, ) -> Result<(PoolHash, Amount, GasInfo), Error> { - let merkle_vault_address = *self - .network - .merkle_payments_address() - .ok_or(MerkleHandlerError::MerklePaymentsAddressNotConfigured)?; - - let provider = self.to_provider(); - let handler = MerklePaymentVaultHandler::new(merkle_vault_address, provider); + let vault_address = *self.network.payment_vault_address(); - let packed: Vec<_> = pool_commitments - .iter() - .map(|c| c.to_packed()) - .collect::>()?; - - let estimated_cost = handler - .estimate_merkle_tree_cost(depth, pool_commitments, merkle_payment_timestamp) - .await?; - info!("Estimated Merkle tree cost: {estimated_cost}"); + // Worst-case estimate: median(pool_prices) * 2^depth, take max across pools. + // Matches the Solidity formula; the only unknown is which pool wins. + let estimated_cost = self + .network + .estimate_merkle_payment_cost(depth, &pool_commitments); + info!("Estimated Merkle tree cost (local): {estimated_cost}"); let wallet_balance = self.balance_of_tokens().await?; if wallet_balance < estimated_cost { @@ -198,17 +182,20 @@ impl Wallet { )); } - let allowance = self.token_allowance(merkle_vault_address).await?; + let allowance = self.token_allowance(vault_address).await?; if allowance < estimated_cost { - info!("Approving Merkle payment vault to spend tokens"); - self.approve_to_spend_tokens(merkle_vault_address, U256::MAX) + info!("Approving payment vault to spend tokens"); + self.approve_to_spend_tokens(vault_address, U256::MAX) .await?; } + let provider = self.to_provider(); + let handler = PaymentVaultHandler::new(vault_address, provider); + let (winner_pool_hash, actual_amount, gas_info) = handler .pay_for_merkle_tree( depth, - packed, + pool_commitments, merkle_payment_timestamp, &self.transaction_config, ) @@ -417,14 +404,12 @@ pub async fn pay_for_quotes>( )); } + let vault_address = *network.payment_vault_address(); + // Get current allowance - let allowance = token_allowance( - network, - wallet_address(&wallet), - *network.data_payments_address(), - ) - .await - .map_err(|err| PayForQuotesError(Error::from(err), Default::default()))?; + let allowance = token_allowance(network, wallet_address(&wallet), vault_address) + .await + .map_err(|err| PayForQuotesError(Error::from(err), Default::default()))?; // TODO: Get rid of approvals altogether, by using permits or whatever.. if allowance < total_amount_to_be_paid { @@ -432,7 +417,7 @@ pub async fn pay_for_quotes>( approve_to_spend_tokens( wallet.clone(), network, - *network.data_payments_address(), + vault_address, U256::MAX, transaction_config, ) @@ -441,7 +426,7 @@ pub async fn pay_for_quotes>( } let provider = http_provider_with_wallet(network.rpc_url().clone(), wallet); - let data_payments = PaymentVaultHandler::new(*network.data_payments_address(), provider); + let data_payments = PaymentVaultHandler::new(vault_address, provider); // remove payments with 0 amount as they don't need to be paid for let payment_for_batch: Vec = payments @@ -521,6 +506,70 @@ mod tests { use alloy::network::{Ethereum, EthereumWallet, NetworkWallet}; use alloy::primitives::address; + use crate::Network; + use crate::merkle_batch_payment::{CANDIDATES_PER_POOL, CandidateNode, PoolCommitment}; + + fn make_pool(prices: [u64; CANDIDATES_PER_POOL]) -> PoolCommitment { + let candidates = std::array::from_fn(|i| CandidateNode { + rewards_address: alloy::primitives::Address::new([i as u8; 20]), + price: Amount::from(prices[i]), + }); + PoolCommitment { + pool_hash: [0u8; 32], + candidates, + } + } + + #[test] + fn test_estimate_uniform_prices() { + // All candidates quote 100, depth=4 + // median=100, total = 100 * 2^4 = 1600 + let network = Network::ArbitrumOne; + let pool = make_pool([100; CANDIDATES_PER_POOL]); + let cost = network.estimate_merkle_payment_cost(4, &[pool]); + assert_eq!(cost, Amount::from(1600u64)); + } + + #[test] + fn test_estimate_varying_prices() { + // Prices 1..=16, sorted ascending: [1,2,...,16] + // Upper median at index 8 = 9 + // total = 9 * 2^3 = 72 + let network = Network::ArbitrumOne; + let prices: [u64; CANDIDATES_PER_POOL] = std::array::from_fn(|i| (i + 1) as u64); + let pool = make_pool(prices); + let cost = network.estimate_merkle_payment_cost(3, &[pool]); + assert_eq!(cost, Amount::from(72u64)); + } + + #[test] + fn test_estimate_picks_worst_pool() { + // Pool A: all quote 100 → median=100, total=100*4=400 + // Pool B: all quote 200 → median=200, total=200*4=800 + // Worst case = 800 + let network = Network::ArbitrumOne; + let pool_a = make_pool([100; CANDIDATES_PER_POOL]); + let pool_b = make_pool([200; CANDIDATES_PER_POOL]); + let cost = network.estimate_merkle_payment_cost(2, &[pool_a, pool_b]); + assert_eq!(cost, Amount::from(800u64)); + } + + #[test] + fn test_estimate_empty_pools() { + let network = Network::ArbitrumOne; + let cost = network.estimate_merkle_payment_cost(4, &[]); + assert_eq!(cost, Amount::ZERO); + } + + #[test] + fn test_estimate_depth_1() { + // depth=1: total = median * 2^1 = median * 2 + let network = Network::ArbitrumOne; + let pool = make_pool([50; CANDIDATES_PER_POOL]); + let cost = network.estimate_merkle_payment_cost(1, &[pool]); + assert_eq!(cost, Amount::from(100u64)); + } + #[tokio::test] async fn test_from_private_key() { let private_key = "bf210844fa5463e373974f3d6fbedf451350c3e72b81b3c5b1718cb91f49c33d"; // DevSkim: ignore DS117838 diff --git a/tests/payment_vault.rs b/tests/payment_vault.rs index 9290ed8..90b2883 100644 --- a/tests/payment_vault.rs +++ b/tests/payment_vault.rs @@ -13,15 +13,15 @@ use alloy::providers::fillers::{ }; use alloy::providers::{Identity, ProviderBuilder, RootProvider, WalletProvider}; use alloy::signers::local::{LocalSigner, PrivateKeySigner}; -use evmlib::Network; -use evmlib::common::U256; +use evmlib::common::{Amount, U256}; use evmlib::contract::network_token::NetworkToken; +use evmlib::contract::payment_vault::MAX_TRANSFERS_PER_TRANSACTION; use evmlib::contract::payment_vault::handler::PaymentVaultHandler; -use evmlib::contract::payment_vault::{MAX_TRANSFERS_PER_TRANSACTION, interface}; -use evmlib::quoting_metrics::QuotingMetrics; -use evmlib::testnet::{deploy_data_payments_contract, deploy_network_token_contract, start_node}; +use evmlib::merkle_batch_payment::{ + CANDIDATES_PER_POOL, CandidateNode, PoolCommitment, expected_reward_pools, +}; +use evmlib::testnet::{deploy_network_token_contract, deploy_payment_vault_contract, start_node}; use evmlib::transaction_config::TransactionConfig; -use evmlib::utils::http_provider; use evmlib::wallet::wallet_address; async fn setup() -> ( @@ -73,12 +73,12 @@ async fn setup() -> ( .await .unwrap(); - let data_payments = - deploy_data_payments_contract(&rpc_url, &node, *network_token.contract.address()) + let payment_vault = + deploy_payment_vault_contract(&rpc_url, &node, *network_token.contract.address()) .await .unwrap(); - (node, network_token, data_payments) + (node, network_token, payment_vault) } #[allow(clippy::unwrap_used)] @@ -126,32 +126,6 @@ async fn test_deploy() { setup().await; } -#[tokio::test] -async fn test_get_quote_on_arb_sepolia() { - let network = Network::ArbitrumSepoliaTest; - let provider = http_provider(network.rpc_url().clone()); - let payment_vault = PaymentVaultHandler::new(*network.data_payments_address(), provider); - - let quoting_metrics = QuotingMetrics { - data_type: 1, // a GraphEntry record - data_size: 100, - close_records_stored: 10, - records_per_type: vec![(0, 5), (1, 5)], - max_records: 16 * 1024, - received_payment_count: 0, - live_time: 1400, - network_density: Some([ - 4, 4, 224, 228, 247, 252, 14, 44, 67, 21, 153, 47, 244, 18, 232, 1, 152, 195, 44, 43, - 29, 135, 19, 217, 240, 129, 64, 245, 240, 227, 129, 162, - ]), - network_size: Some(240), - }; - - let result = payment_vault.get_quote(vec![quoting_metrics]).await; - - assert!(result.is_ok(), "Failed with error: {:?}", result.err()); -} - #[tokio::test] async fn test_pay_for_quotes_on_local() { let (_anvil, network_token, mut payment_vault) = setup().await; @@ -185,18 +159,114 @@ async fn test_pay_for_quotes_on_local() { assert!(result.is_ok(), "Failed with error: {:?}", result.err()); } +fn make_pool_commitment(price: u64) -> PoolCommitment { + let candidates: [CandidateNode; CANDIDATES_PER_POOL] = std::array::from_fn(|i| CandidateNode { + rewards_address: alloy::primitives::Address::new([(i + 1) as u8; 20]), + price: Amount::from(price), + }); + PoolCommitment { + pool_hash: { + let mut hash = [0u8; 32]; + hash[0] = rand::random(); + hash[1] = rand::random(); + hash + }, + candidates, + } +} + #[tokio::test] -async fn test_verify_payment_on_local() { +async fn test_pay_for_merkle_tree_on_local() { let (_anvil, network_token, mut payment_vault) = setup().await; let transaction_config = TransactionConfig::default(); - let mut quote_payments = vec![]; + // Use depth=2 → expected pools = 2^ceil(2/2) = 2 + let depth: u8 = 2; + let num_pools = expected_reward_pools(depth); + assert_eq!(num_pools, 2); - for _ in 0..5 { - let quote_payment = random_quote_payment(); - quote_payments.push(quote_payment); + let pool_commitments: Vec = + (0..num_pools).map(|_| make_pool_commitment(100)).collect(); + + let merkle_payment_timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); + + // Approve the payment vault to spend tokens + let _ = network_token + .approve( + *payment_vault.contract.address(), + U256::MAX, + &transaction_config, + ) + .await + .unwrap(); + + // Use the same provider as the network token (funded account) + payment_vault.set_provider(network_token.contract.provider().clone()); + + let (winner_pool_hash, total_amount, _gas_info) = payment_vault + .pay_for_merkle_tree( + depth, + pool_commitments.clone(), + merkle_payment_timestamp, + &transaction_config, + ) + .await + .expect("pay_for_merkle_tree should succeed"); + + // Verify winner pool hash is one of the submitted pools + assert!( + pool_commitments + .iter() + .any(|pc| pc.pool_hash == winner_pool_hash), + "Winner pool hash should match one of the submitted pools" + ); + + // Verify total amount: median(100) * 2^2 = 400 + assert_eq!(total_amount, Amount::from(400u64)); + + // Query on-chain payment info and verify + let completed = payment_vault + .get_completed_merkle_payment(winner_pool_hash) + .await + .expect("get_completed_merkle_payment should succeed"); + + assert_eq!(completed.depth, depth); + assert_eq!(completed.merklePaymentTimestamp, merkle_payment_timestamp); + assert_eq!( + completed.paidNodeAddresses.len(), + depth as usize, + "Should have paid exactly {depth} nodes" + ); + + // Each node should receive totalAmount / depth + let expected_per_node = total_amount / Amount::from(depth as u64); + for paid_node in &completed.paidNodeAddresses { + assert_eq!(paid_node.amount, expected_per_node); } +} + +#[tokio::test] +async fn test_pay_for_merkle_tree_depth_4() { + let (_anvil, network_token, mut payment_vault) = setup().await; + + let transaction_config = TransactionConfig::default(); + + // Use depth=4 → expected pools = 2^ceil(4/2) = 4 + let depth: u8 = 4; + let num_pools = expected_reward_pools(depth); + assert_eq!(num_pools, 4); + + let pool_commitments: Vec = + (0..num_pools).map(|_| make_pool_commitment(50)).collect(); + + let merkle_payment_timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); let _ = network_token .approve( @@ -207,42 +277,96 @@ async fn test_verify_payment_on_local() { .await .unwrap(); - // Contract provider has a different account coupled to it, - // so we set it to the same as the network token contract payment_vault.set_provider(network_token.contract.provider().clone()); - let result = payment_vault - .pay_for_quotes(quote_payments.clone(), &transaction_config) - .await; + let (winner_pool_hash, total_amount, _gas_info) = payment_vault + .pay_for_merkle_tree( + depth, + pool_commitments, + merkle_payment_timestamp, + &transaction_config, + ) + .await + .expect("pay_for_merkle_tree depth=4 should succeed"); - assert!(result.is_ok(), "Failed with error: {:?}", result.err()); + // median(50) * 2^4 = 800 + assert_eq!(total_amount, Amount::from(800u64)); + + let completed = payment_vault + .get_completed_merkle_payment(winner_pool_hash) + .await + .expect("get_completed_merkle_payment should succeed"); + + assert_eq!(completed.depth, depth); + assert_eq!(completed.paidNodeAddresses.len(), 4); - let payment_verifications: Vec<_> = quote_payments - .into_iter() - .map(|v| interface::IPaymentVault::PaymentVerification { - metrics: QuotingMetrics { - data_size: 0, - data_type: 0, - close_records_stored: 0, - records_per_type: vec![], - max_records: 0, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - } - .into(), - rewardsAddress: v.1, - quoteHash: v.0, - }) + // Verify all paid node indices are unique and within bounds + let paid_indices: Vec = completed + .paidNodeAddresses + .iter() + .map(|n| n.poolIndex) .collect(); + let unique_indices: std::collections::HashSet = paid_indices.iter().copied().collect(); + assert_eq!( + unique_indices.len(), + depth as usize, + "All paid node indices should be unique" + ); + for idx in &paid_indices { + assert!( + (*idx as usize) < CANDIDATES_PER_POOL, + "Paid node index {idx} should be < {CANDIDATES_PER_POOL}" + ); + } +} + +#[tokio::test] +async fn test_pay_for_merkle_tree_duplicate_rejected() { + let (_anvil, network_token, mut payment_vault) = setup().await; + + let transaction_config = TransactionConfig::default(); + + let depth: u8 = 2; + let num_pools = expected_reward_pools(depth); + let pool_commitments: Vec = + (0..num_pools).map(|_| make_pool_commitment(100)).collect(); + + let merkle_payment_timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs(); - let results = payment_vault - .verify_payment(payment_verifications) + let _ = network_token + .approve( + *payment_vault.contract.address(), + U256::MAX, + &transaction_config, + ) .await - .expect("Verify payment failed"); + .unwrap(); - for result in results { - assert!(result.isValid); - } + payment_vault.set_provider(network_token.contract.provider().clone()); + + // First payment should succeed + let _ = payment_vault + .pay_for_merkle_tree( + depth, + pool_commitments.clone(), + merkle_payment_timestamp, + &transaction_config, + ) + .await + .expect("first payment should succeed"); + + // Second payment with same pools and timestamp should fail (PaymentAlreadyExists) + let result = payment_vault + .pay_for_merkle_tree( + depth, + pool_commitments, + merkle_payment_timestamp, + &transaction_config, + ) + .await; + + assert!(result.is_err(), "Duplicate payment should be rejected"); } diff --git a/tests/wallet.rs b/tests/wallet.rs index e7c02e7..9a96e65 100644 --- a/tests/wallet.rs +++ b/tests/wallet.rs @@ -10,9 +10,8 @@ use alloy::providers::ext::AnvilApi; use alloy::providers::{ProviderBuilder, WalletProvider}; use alloy::signers::local::{LocalSigner, PrivateKeySigner}; use evmlib::common::{Amount, TxHash}; -use evmlib::contract::payment_vault::{MAX_TRANSFERS_PER_TRANSACTION, verify_data_payment}; -use evmlib::quoting_metrics::QuotingMetrics; -use evmlib::testnet::{deploy_data_payments_contract, deploy_network_token_contract, start_node}; +use evmlib::contract::payment_vault::MAX_TRANSFERS_PER_TRANSACTION; +use evmlib::testnet::{deploy_network_token_contract, deploy_payment_vault_contract, start_node}; use evmlib::transaction_config::TransactionConfig; use evmlib::wallet::{Wallet, transfer_tokens, wallet_address}; use evmlib::{CustomNetwork, Network}; @@ -26,21 +25,16 @@ async fn local_testnet() -> (AnvilInstance, Network, EthereumWallet) { .await .unwrap(); let payment_token_address = *network_token.contract.address(); - let data_payments = deploy_data_payments_contract(&rpc_url, &node, payment_token_address) + let payment_vault = deploy_payment_vault_contract(&rpc_url, &node, payment_token_address) .await .unwrap(); - let merkle_payments = - evmlib::testnet::deploy_merkle_payments_contract(&rpc_url, &node, payment_token_address) - .await - .unwrap(); ( node, Network::Custom(CustomNetwork { rpc_url_http: rpc_url, payment_token_address, - data_payments_address: *data_payments.contract.address(), - merkle_payments_address: Some(*merkle_payments.contract.address()), + payment_vault_address: *payment_vault.contract.address(), }), network_token.contract.provider().wallet().clone(), ) @@ -80,7 +74,7 @@ async fn funded_wallet(network: &Network, genesis_wallet: EthereumWallet) -> Wal } #[tokio::test] -async fn test_pay_for_quotes_and_data_payment_verification() { +async fn test_pay_for_quotes() { const CHUNK_PAYMENTS: usize = 600; const QUOTES_PER_CHUNK: usize = 5; @@ -113,34 +107,4 @@ async fn test_pay_for_quotes_and_data_payment_verification() { .mul(QUOTES_PER_CHUNK) .div_ceil(MAX_TRANSFERS_PER_TRANSACTION) ); - - for quotes in quote_payments.iter() { - let mut payments_to_verify = vec![]; - - for (quote_hash, reward_addr, _) in quotes { - payments_to_verify.push(( - *quote_hash, - QuotingMetrics { - data_size: 0, - data_type: 0, - close_records_stored: 0, - records_per_type: vec![], - max_records: 0, - received_payment_count: 0, - live_time: 0, - network_density: None, - network_size: None, - }, - *reward_addr, - )); - } - - let result = verify_data_payment(&network, vec![], payments_to_verify.clone()).await; - - assert!( - result.is_ok(), - "Verification failed for: {payments_to_verify:?}. Error: {:?}", - result.err() - ); - } }