polkamarkets-js 3.4.3 → 3.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/abis/AccessControlUpgradeable.json +1 -0
  2. package/abis/ERC165Upgradeable.json +1 -0
  3. package/abis/Hashes.json +1 -0
  4. package/abis/Math.json +1 -1
  5. package/abis/MerkleProof.json +1 -0
  6. package/abis/MerkleRewardsDistributor.json +1 -0
  7. package/abis/MerkleRewardsDistributorTest.json +1 -0
  8. package/abis/Panic.json +1 -0
  9. package/abis/SafeCast.json +1 -0
  10. package/abis/SignedMath.json +1 -0
  11. package/abis/Strings.json +1 -1
  12. package/contracts/MerkleRewardsDistributor.sol +194 -0
  13. package/package.json +1 -1
  14. package/scripts/UpdateMarket.s.sol +55 -0
  15. package/src/Application.js +16 -0
  16. package/src/interfaces/index.js +1 -0
  17. package/src/models/IContract.js +14 -0
  18. package/src/models/MerkleRewardsDistributorContract.js +107 -0
  19. package/src/models/index.js +3 -1
  20. package/test/MerkleRewardsDistributor.t.sol +316 -0
  21. package/script/UpdateMarket.s.sol +0 -55
  22. /package/abis/{test.json → Test.json} +0 -0
  23. /package/{script → scripts}/AddAdminToLand.s.sol +0 -0
  24. /package/{script → scripts}/ClaimMerkleRoot.s.sol +0 -0
  25. /package/{script → scripts}/CreateLand.s.sol +0 -0
  26. /package/{script → scripts}/CreateMarkets.s.sol +0 -0
  27. /package/{script → scripts}/DeployContracts.s.sol +0 -0
  28. /package/{script → scripts}/DeployMerkleRewardsDistributor.s.sol +0 -0
  29. /package/{script → scripts}/DeployToken.s.sol +0 -0
  30. /package/{script → scripts}/DeployUSDT.s.sol +0 -0
  31. /package/{script → scripts}/MintTokens.s.sol +0 -0
  32. /package/{script → scripts}/PublishMerkleRoot.s.sol +0 -0
  33. /package/{script → scripts}/ResolveMarket.s.sol +0 -0
  34. /package/{script → scripts}/TradeMarket.s.sol +0 -0
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]}]}
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]}]}
@@ -0,0 +1 @@
1
+ {"abi":[]}
package/abis/Math.json CHANGED
@@ -1 +1 @@
1
- {"contractName":"Math","abi":[],"bytecode":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220e2a3a9807fcfd4dcac76d271fa7e42a70a4174853a13409b86f8dafd86e24b1c64736f6c63430008120033"}
1
+ {"abi":[]}
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"error","name":"MerkleProofInvalidMultiproof","inputs":[]}]}
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"constructor","inputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"DEFAULT_ADMIN_ROLE","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"UPGRADE_INTERFACE_VERSION","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"acceptOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"addAdmin","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"claim","inputs":[{"name":"contestId","type":"string","internalType":"string"},{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"index","type":"uint256","internalType":"uint256"},{"name":"user","type":"address","internalType":"address"},{"name":"amount","type":"uint256","internalType":"uint256"},{"name":"merkleProof","type":"bytes32[]","internalType":"bytes32[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"claimMany","inputs":[{"name":"contestIds","type":"string[]","internalType":"string[]"},{"name":"tokens","type":"address[]","internalType":"contract IERC20[]"},{"name":"indices","type":"uint256[]","internalType":"uint256[]"},{"name":"users","type":"address[]","internalType":"address[]"},{"name":"amounts","type":"uint256[]","internalType":"uint256[]"},{"name":"merkleProofs","type":"bytes32[][]","internalType":"bytes32[][]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getRoleAdmin","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"getRoot","inputs":[{"name":"contestId","type":"string","internalType":"string"},{"name":"token","type":"address","internalType":"contract IERC20"}],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"grantRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"hasRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"initialOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"isAdmin","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isClaimed","inputs":[{"name":"contestId","type":"string","internalType":"string"},{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"index","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"isClaimedMany","inputs":[{"name":"contestIds","type":"string[]","internalType":"string[]"},{"name":"tokens","type":"address[]","internalType":"contract IERC20[]"},{"name":"indices","type":"uint256[]","internalType":"uint256[]"}],"outputs":[{"name":"","type":"bool[]","internalType":"bool[]"}],"stateMutability":"view"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"pendingOwner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"proxiableUUID","inputs":[],"outputs":[{"name":"","type":"bytes32","internalType":"bytes32"}],"stateMutability":"view"},{"type":"function","name":"publishRoot","inputs":[{"name":"contestId","type":"string","internalType":"string"},{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"root","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeAdmin","inputs":[{"name":"user","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"renounceRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"callerConfirmation","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"revokeRole","inputs":[{"name":"role","type":"bytes32","internalType":"bytes32"},{"name":"account","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"supportsInterface","inputs":[{"name":"interfaceId","type":"bytes4","internalType":"bytes4"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"upgradeToAndCall","inputs":[{"name":"newImplementation","type":"address","internalType":"address"},{"name":"data","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"withdraw","inputs":[{"name":"token","type":"address","internalType":"contract IERC20"},{"name":"amount","type":"uint256","internalType":"uint256"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"Claim","inputs":[{"name":"contestId","type":"string","indexed":false,"internalType":"string"},{"name":"token","type":"address","indexed":true,"internalType":"contract IERC20"},{"name":"index","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"user","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint64","indexed":false,"internalType":"uint64"}],"anonymous":false},{"type":"event","name":"OwnershipTransferStarted","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleAdminChanged","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"previousAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"newAdminRole","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"RoleGranted","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RoleRevoked","inputs":[{"name":"role","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"account","type":"address","indexed":true,"internalType":"address"},{"name":"sender","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"RootPublished","inputs":[{"name":"contestId","type":"string","indexed":false,"internalType":"string"},{"name":"token","type":"address","indexed":true,"internalType":"contract IERC20"},{"name":"root","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"rootKey","type":"bytes32","indexed":true,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"TokensWithdrawn","inputs":[{"name":"token","type":"address","indexed":true,"internalType":"address"},{"name":"to","type":"address","indexed":true,"internalType":"address"},{"name":"amount","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"Upgraded","inputs":[{"name":"implementation","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"AccessControlBadConfirmation","inputs":[]},{"type":"error","name":"AccessControlUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"},{"name":"neededRole","type":"bytes32","internalType":"bytes32"}]},{"type":"error","name":"AddressEmptyCode","inputs":[{"name":"target","type":"address","internalType":"address"}]},{"type":"error","name":"ERC1967InvalidImplementation","inputs":[{"name":"implementation","type":"address","internalType":"address"}]},{"type":"error","name":"ERC1967NonPayable","inputs":[]},{"type":"error","name":"FailedCall","inputs":[]},{"type":"error","name":"InvalidInitialization","inputs":[]},{"type":"error","name":"NotInitializing","inputs":[]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"SafeERC20FailedOperation","inputs":[{"name":"token","type":"address","internalType":"address"}]},{"type":"error","name":"UUPSUnauthorizedCallContext","inputs":[]},{"type":"error","name":"UUPSUnsupportedProxiableUUID","inputs":[{"name":"slot","type":"bytes32","internalType":"bytes32"}]}]}
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"function","name":"IS_TEST","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"excludeArtifacts","inputs":[],"outputs":[{"name":"excludedArtifacts_","type":"string[]","internalType":"string[]"}],"stateMutability":"view"},{"type":"function","name":"excludeContracts","inputs":[],"outputs":[{"name":"excludedContracts_","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"excludeSelectors","inputs":[],"outputs":[{"name":"excludedSelectors_","type":"tuple[]","internalType":"struct StdInvariant.FuzzSelector[]","components":[{"name":"addr","type":"address","internalType":"address"},{"name":"selectors","type":"bytes4[]","internalType":"bytes4[]"}]}],"stateMutability":"view"},{"type":"function","name":"excludeSenders","inputs":[],"outputs":[{"name":"excludedSenders_","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"failed","inputs":[],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"setUp","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"targetArtifactSelectors","inputs":[],"outputs":[{"name":"targetedArtifactSelectors_","type":"tuple[]","internalType":"struct StdInvariant.FuzzArtifactSelector[]","components":[{"name":"artifact","type":"string","internalType":"string"},{"name":"selectors","type":"bytes4[]","internalType":"bytes4[]"}]}],"stateMutability":"view"},{"type":"function","name":"targetArtifacts","inputs":[],"outputs":[{"name":"targetedArtifacts_","type":"string[]","internalType":"string[]"}],"stateMutability":"view"},{"type":"function","name":"targetContracts","inputs":[],"outputs":[{"name":"targetedContracts_","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"targetInterfaces","inputs":[],"outputs":[{"name":"targetedInterfaces_","type":"tuple[]","internalType":"struct StdInvariant.FuzzInterface[]","components":[{"name":"addr","type":"address","internalType":"address"},{"name":"artifacts","type":"string[]","internalType":"string[]"}]}],"stateMutability":"view"},{"type":"function","name":"targetSelectors","inputs":[],"outputs":[{"name":"targetedSelectors_","type":"tuple[]","internalType":"struct StdInvariant.FuzzSelector[]","components":[{"name":"addr","type":"address","internalType":"address"},{"name":"selectors","type":"bytes4[]","internalType":"bytes4[]"}]}],"stateMutability":"view"},{"type":"function","name":"targetSenders","inputs":[],"outputs":[{"name":"targetedSenders_","type":"address[]","internalType":"address[]"}],"stateMutability":"view"},{"type":"function","name":"test_AdminRoleCanPublishRoot","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_AdminWithdraw","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_ClaimMany","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_ClaimMany_DuplicateIndexReverts","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_ClaimMany_LengthMismatchReverts","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_ClaimWithWrongTokenOrContest","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_IsClaimedReflectsState","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_OnlyOwnerGuards","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_PublishRootAndSingleClaim","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_RevertOnDoubleClaim","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_RevertOnInvalidProof","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_RootNotSet_RevertOnClaim","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"test_RootUpdateInvalidatesOldProof","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"log","inputs":[{"name":"","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"log_address","inputs":[{"name":"","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"log_array","inputs":[{"name":"val","type":"uint256[]","indexed":false,"internalType":"uint256[]"}],"anonymous":false},{"type":"event","name":"log_array","inputs":[{"name":"val","type":"int256[]","indexed":false,"internalType":"int256[]"}],"anonymous":false},{"type":"event","name":"log_array","inputs":[{"name":"val","type":"address[]","indexed":false,"internalType":"address[]"}],"anonymous":false},{"type":"event","name":"log_bytes","inputs":[{"name":"","type":"bytes","indexed":false,"internalType":"bytes"}],"anonymous":false},{"type":"event","name":"log_bytes32","inputs":[{"name":"","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"log_int","inputs":[{"name":"","type":"int256","indexed":false,"internalType":"int256"}],"anonymous":false},{"type":"event","name":"log_named_address","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"address","indexed":false,"internalType":"address"}],"anonymous":false},{"type":"event","name":"log_named_array","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"uint256[]","indexed":false,"internalType":"uint256[]"}],"anonymous":false},{"type":"event","name":"log_named_array","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"int256[]","indexed":false,"internalType":"int256[]"}],"anonymous":false},{"type":"event","name":"log_named_array","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"address[]","indexed":false,"internalType":"address[]"}],"anonymous":false},{"type":"event","name":"log_named_bytes","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"bytes","indexed":false,"internalType":"bytes"}],"anonymous":false},{"type":"event","name":"log_named_bytes32","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"event","name":"log_named_decimal_int","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"int256","indexed":false,"internalType":"int256"},{"name":"decimals","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"log_named_decimal_uint","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"uint256","indexed":false,"internalType":"uint256"},{"name":"decimals","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"log_named_int","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"int256","indexed":false,"internalType":"int256"}],"anonymous":false},{"type":"event","name":"log_named_string","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"log_named_uint","inputs":[{"name":"key","type":"string","indexed":false,"internalType":"string"},{"name":"val","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"log_string","inputs":[{"name":"","type":"string","indexed":false,"internalType":"string"}],"anonymous":false},{"type":"event","name":"log_uint","inputs":[{"name":"","type":"uint256","indexed":false,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"logs","inputs":[{"name":"","type":"bytes","indexed":false,"internalType":"bytes"}],"anonymous":false}]}
@@ -0,0 +1 @@
1
+ {"abi":[]}
@@ -0,0 +1 @@
1
+ {"abi":[{"type":"error","name":"SafeCastOverflowedIntDowncast","inputs":[{"name":"bits","type":"uint8","internalType":"uint8"},{"name":"value","type":"int256","internalType":"int256"}]},{"type":"error","name":"SafeCastOverflowedIntToUint","inputs":[{"name":"value","type":"int256","internalType":"int256"}]},{"type":"error","name":"SafeCastOverflowedUintDowncast","inputs":[{"name":"bits","type":"uint8","internalType":"uint8"},{"name":"value","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"SafeCastOverflowedUintToInt","inputs":[{"name":"value","type":"uint256","internalType":"uint256"}]}]}
@@ -0,0 +1 @@
1
+ {"abi":[]}
package/abis/Strings.json CHANGED
@@ -1 +1 @@
1
- {"contractName":"Strings","abi":[],"bytecode":"0x60566037600b82828239805160001a607314602a57634e487b7160e01b600052600060045260246000fd5b30600052607381538281f3fe73000000000000000000000000000000000000000030146080604052600080fdfea2646970667358221220f4041dd1e7e51c7b6b519f1d71be36e965981c92abb0e7ce860a92219f0a8dcb64736f6c63430008120033"}
1
+ {"abi":[{"type":"error","name":"StringsInsufficientHexLength","inputs":[{"name":"value","type":"uint256","internalType":"uint256"},{"name":"length","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"StringsInvalidAddressFormat","inputs":[]},{"type":"error","name":"StringsInvalidChar","inputs":[]}]}
@@ -0,0 +1,194 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.26;
3
+
4
+ // OpenZeppelin upgradeable
5
+ import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
6
+ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
7
+ import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
8
+ import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
9
+ import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol";
10
+
11
+ // OpenZeppelin non-upgradeable utils
12
+ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
13
+ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
14
+ import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
15
+
16
+ /// @title Merkle-based rewards distributor for contests
17
+ /// @notice Owner publishes one Merkle root per (contestId, token). Users claim with proof.
18
+ contract MerkleRewardsDistributor is Initializable, UUPSUpgradeable, Ownable2StepUpgradeable, AccessControlUpgradeable, ReentrancyGuardUpgradeable {
19
+ using SafeERC20 for IERC20;
20
+
21
+ bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
22
+
23
+ // rootKey => merkleRoot
24
+ mapping(bytes32 => bytes32) private _roots;
25
+ // rootKey => claimed bitmap wordIndex => word
26
+ mapping(bytes32 => mapping(uint256 => uint256)) private _claimedBitMap;
27
+
28
+ event RootPublished(string contestId, IERC20 indexed token, bytes32 indexed root, bytes32 indexed rootKey);
29
+ event Claim(
30
+ string contestId,
31
+ IERC20 indexed token,
32
+ uint256 indexed index,
33
+ address indexed user,
34
+ uint256 amount
35
+ );
36
+ event TokensWithdrawn(address indexed token, address indexed to, uint256 amount);
37
+
38
+ /// @custom:oz-upgrades-unsafe-allow constructor
39
+ constructor() {
40
+ _disableInitializers();
41
+ }
42
+
43
+ function initialize(address initialOwner) public initializer {
44
+ __ReentrancyGuard_init();
45
+ __Ownable_init(initialOwner);
46
+ __UUPSUpgradeable_init();
47
+ __AccessControl_init();
48
+
49
+ _grantRole(ADMIN_ROLE, initialOwner);
50
+ _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE);
51
+ }
52
+
53
+ function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
54
+
55
+ // ------ Admin ------
56
+
57
+ /// @notice Publishes/updates the root for a given (contestId, token)
58
+ function publishRoot(string calldata contestId, IERC20 token, bytes32 root) external onlyAdmin {
59
+ bytes32 key = _rootKey(contestId, token);
60
+ _roots[key] = root;
61
+ emit RootPublished(contestId, token, root, key);
62
+ }
63
+
64
+ function withdraw(IERC20 token, uint256 amount) external onlyOwner nonReentrant {
65
+ token.safeTransfer(msg.sender, amount);
66
+ emit TokensWithdrawn(address(token), msg.sender, amount);
67
+ }
68
+
69
+ // role management
70
+ modifier onlyAdmin() {
71
+ require(hasRole(ADMIN_ROLE, msg.sender), "MerkleRewards: must have admin role");
72
+ _;
73
+ }
74
+
75
+ function addAdmin(address user) external onlyAdmin {
76
+ grantRole(ADMIN_ROLE, user);
77
+ }
78
+
79
+ function removeAdmin(address user) external onlyAdmin {
80
+ revokeRole(ADMIN_ROLE, user);
81
+ }
82
+
83
+ function isAdmin(address user) external view returns (bool) {
84
+ return hasRole(ADMIN_ROLE, user);
85
+ }
86
+
87
+ // ------ Claims ------
88
+
89
+ /// @notice Claims `amount` for `user` with a Merkle proof. Funds are sent to `user`.
90
+ /// @dev Leaf is keccak256(abi.encode(index, user, token, amount, contestId))
91
+ function claim(
92
+ string calldata contestId,
93
+ IERC20 token,
94
+ uint256 index,
95
+ address user,
96
+ uint256 amount,
97
+ bytes32[] calldata merkleProof
98
+ ) external nonReentrant {
99
+ bytes32 key = _rootKey(contestId, token);
100
+ bytes32 root = _roots[key];
101
+ require(root != bytes32(0), "MerkleRewards: root not set");
102
+ require(!_isClaimed(key, index), "MerkleRewards: already claimed");
103
+
104
+ bytes32 leaf = keccak256(abi.encode(index, user, token, amount, contestId));
105
+ require(MerkleProof.verify(merkleProof, root, leaf), "MerkleRewards: invalid proof");
106
+
107
+ _setClaimed(key, index);
108
+
109
+ token.safeTransfer(user, amount);
110
+
111
+ emit Claim(contestId, token, index, user, amount);
112
+ }
113
+
114
+ /// @notice Claims multiple entries in one transaction. Funds are sent to each entry's `user`.
115
+ function claimMany(
116
+ string[] calldata contestIds,
117
+ IERC20[] calldata tokens,
118
+ uint256[] calldata indices,
119
+ address[] calldata users,
120
+ uint256[] calldata amounts,
121
+ bytes32[][] calldata merkleProofs
122
+ ) external nonReentrant {
123
+ uint256 len = contestIds.length;
124
+ require(
125
+ len == tokens.length &&
126
+ len == indices.length &&
127
+ len == users.length &&
128
+ len == amounts.length &&
129
+ len == merkleProofs.length,
130
+ "MerkleRewards: arrays length mismatch"
131
+ );
132
+
133
+ for (uint256 i = 0; i < len; i++) {
134
+ bytes32 key = _rootKey(contestIds[i], tokens[i]);
135
+ bytes32 root = _roots[key];
136
+ require(root != bytes32(0), "MerkleRewards: root not set");
137
+ require(!_isClaimed(key, indices[i]), "MerkleRewards: already claimed");
138
+
139
+ bytes32 leaf = keccak256(abi.encode(indices[i], users[i], tokens[i], amounts[i], contestIds[i]));
140
+ require(MerkleProof.verify(merkleProofs[i], root, leaf), "MerkleRewards: invalid proof");
141
+
142
+ _setClaimed(key, indices[i]);
143
+ tokens[i].safeTransfer(users[i], amounts[i]);
144
+
145
+ emit Claim(contestIds[i], tokens[i], indices[i], users[i], amounts[i]);
146
+ }
147
+ }
148
+
149
+ // ------ Views ------
150
+
151
+ function getRoot(string calldata contestId, IERC20 token) external view returns (bytes32) {
152
+ return _roots[_rootKey(contestId, token)];
153
+ }
154
+
155
+ function isClaimed(
156
+ string calldata contestId,
157
+ IERC20 token,
158
+ uint256 index
159
+ ) external view returns (bool) {
160
+ return _isClaimed(_rootKey(contestId, token), index);
161
+ }
162
+
163
+ function isClaimedMany(
164
+ string[] calldata contestIds,
165
+ IERC20[] calldata tokens,
166
+ uint256[] calldata indices
167
+ ) external view returns (bool[] memory) {
168
+ bool[] memory claims = new bool[](contestIds.length);
169
+ for (uint256 i = 0; i < contestIds.length; i++) {
170
+ claims[i] = _isClaimed(_rootKey(contestIds[i], tokens[i]), indices[i]);
171
+ }
172
+ return claims;
173
+ }
174
+
175
+ // ------ Internal utils ------
176
+
177
+ function _rootKey(string calldata contestId, IERC20 token) private pure returns (bytes32) {
178
+ return keccak256(abi.encode(contestId, token));
179
+ }
180
+
181
+ function _isClaimed(bytes32 key, uint256 index) private view returns (bool) {
182
+ uint256 wordIndex = index >> 8; // divide by 256
183
+ uint256 bitIndex = index & 255; // modulo 256
184
+ uint256 word = _claimedBitMap[key][wordIndex];
185
+ uint256 mask = (1 << bitIndex);
186
+ return word & mask == mask;
187
+ }
188
+
189
+ function _setClaimed(bytes32 key, uint256 index) private {
190
+ uint256 wordIndex = index >> 8;
191
+ uint256 bitIndex = index & 255;
192
+ _claimedBitMap[key][wordIndex] = _claimedBitMap[key][wordIndex] | (1 << bitIndex);
193
+ }
194
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polkamarkets-js",
3
- "version": "3.4.3",
3
+ "version": "3.4.4",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -0,0 +1,55 @@
1
+ // // SPDX-License-Identifier: GPL-2.0
2
+ // pragma solidity ^0.8.26;
3
+
4
+ // import {Script} from "forge-std/Script.sol";
5
+ // import {console} from "forge-std/console.sol";
6
+ // // Import your contracts here
7
+ // import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8
+ // import {PredictionMarketV3_3, IWETH} from "../contracts/PredictionMarketV3_3.sol";
9
+ // import {IPredictionMarketV3Manager} from "../contracts/IPredictionMarketV3Manager.sol";
10
+ // import {PredictionMarketV3Manager, FantasyERC20} from "../contracts/PredictionMarketV3Manager.sol";
11
+ // import {IRealityETH_ERC20} from "../contracts/IRealityETH_ERC20.sol";
12
+
13
+ // contract UpdateMarket is Script {
14
+ // function run() public {
15
+
16
+ // vm.startBroadcast();
17
+
18
+ // address payable PREDICTION_MARKET_V3_3 = payable(address(0x3e0F5F8F5Fb043aBFA475C0308417Bf72c463289));
19
+ // address PREDICTION_MARKET_V3_MANAGER = address(0x68dDc91CCC06e63d74905D901A30edEA7C77EebE);
20
+
21
+ // PredictionMarketV3_3.MarketState state = PredictionMarketV3_3(PREDICTION_MARKET_V3_3).getMarketState(486);
22
+ // console2.log("state: ", state);
23
+
24
+ // PredictionMarketV3_3.MarketUpdateDescription memory description = PredictionMarketV3_3.MarketUpdateDescription({
25
+ // closesAtTimestamp: 1762066800,
26
+ // balance: 456538064447348172635074428,
27
+ // liquidity: 120000000000000000000000000,
28
+ // sharesAvailable: 1272835104225672313766121101,
29
+ // state: PredictionMarketV3_3.MarketState.open,
30
+ // resolution: PredictionMarketV3_3.MarketResolution({
31
+ // resolved: false,
32
+ // realitio: IRealityETH_ERC20(0x71D76Ff3C2729071B2c52A667197d9f715029F2c),
33
+ // outcomeId: 115792089237316195423570985008687907853269984665640564039457584007913129639935,
34
+ // questionId: 0x21d6c6c195572d82a7d0a7ba677251dfd3839b21e09df9b39ae36ec99fa6744d,
35
+ // realitioTimeout: 3600
36
+ // }),
37
+ // feesPoolWeight: 0,
38
+ // feesTreasury: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
39
+ // feesDistributor: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
40
+ // buyFees: PredictionMarketV3_3.Fees({fee: 0, treasuryFee: 0, distributorFee: 0}),
41
+ // sellFees: PredictionMarketV3_3.Fees({fee: 0, treasuryFee: 0, distributorFee: 0}),
42
+ // outcomeCount: 5,
43
+ // token: IERC20(0x0b07cf011B6e2b7E0803b892d97f751659940F23),
44
+ // manager: IPredictionMarketV3Manager(0x2EbD002d755bdFadb9e5549aA00a80bd7fa408AE),
45
+ // creator: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
46
+ // paused: false
47
+ // });
48
+
49
+ // PredictionMarketV3_3(PREDICTION_MARKET_V3_3).updateMarket(486, description);
50
+ // PredictionMarketV3_3(PREDICTION_MARKET_V3_3).updateMarketResolution(486, description);
51
+
52
+ // state = PredictionMarketV3_3(PREDICTION_MARKET_V3_3).getMarketState(486);
53
+ // console2.log("new state: ", state);
54
+ // }
55
+ // }
@@ -17,6 +17,7 @@ const FantasyERC20Contract = require("./models/index").FantasyERC20Contract;
17
17
  const WETH9Contract = require("./models/index").WETH9Contract;
18
18
  const ArbitrationContract = require("./models/index").ArbitrationContract;
19
19
  const ArbitrationProxyContract = require("./models/index").ArbitrationProxyContract;
20
+ const MerkleRewardsDistributorContract = require("./models/index").MerkleRewardsDistributorContract;
20
21
 
21
22
  const DualProvider = require("./utils/DualProvider");
22
23
  const Account = require('./utils/Account');
@@ -412,6 +413,21 @@ class Application {
412
413
  }
413
414
  };
414
415
 
416
+ /**
417
+ * @name getMerkleRewardsDistributorContract
418
+ * @param {Address} ContractAddress (Opt) If it is deployed
419
+ * @description Create a Merkle Rewards Distributor Contract
420
+ */
421
+ getMerkleRewardsDistributorContract({ contractAddress = null }) {
422
+ try {
423
+ return new MerkleRewardsDistributorContract({
424
+ ...this.contractDefaultParams(contractAddress)
425
+ });
426
+ } catch (err) {
427
+ throw err;
428
+ }
429
+ }
430
+
415
431
  /***********/
416
432
  /** UTILS **/
417
433
  /***********/
@@ -15,6 +15,7 @@ let index = {
15
15
  predictionMarketV3Factory: require("../../abis/PredictionMarketV3Factory.json"),
16
16
  predictionV3Querier: require("../../abis/PredictionMarketV3Querier.json"),
17
17
  realitio: require("../../abis/RealitioERC20.json"),
18
+ merkleRewardsDistributor: require("../../abis/MerkleRewardsDistributor.json"),
18
19
  voting: require("../../abis/Voting.json"),
19
20
  weth: require("../../abis/WETH9.json"),
20
21
  };
@@ -1138,6 +1138,20 @@ class IContract {
1138
1138
  async getBlock(blockNumber) {
1139
1139
  return await this.params.web3.eth.getBlock(blockNumber);
1140
1140
  }
1141
+
1142
+ async soliditySha3(args) {
1143
+ return this.params.web3.utils.soliditySha3(...args)
1144
+ }
1145
+
1146
+ async signMessage(message) {
1147
+ if (this.acc) {
1148
+ return await this.acc.account.sign(message);
1149
+ } else {
1150
+ // TODO need to test with forntend
1151
+ // return await this.params.web3.eth.sign(message, await this.getMyAccount());
1152
+ return await this.params.web3.eth.personal.sign(message, await this.getMyAccount());
1153
+ }
1154
+ }
1141
1155
  }
1142
1156
 
1143
1157
  module.exports = IContract;
@@ -0,0 +1,107 @@
1
+ // const _ = require("lodash");
2
+
3
+ const merkleRewardsDistributor = require("../interfaces").merkleRewardsDistributor;
4
+
5
+ const Numbers = require("../utils/Numbers");
6
+ const IContract = require('./IContract');
7
+
8
+ class MerkleRewardsDistributorContract extends IContract {
9
+ constructor(params) {
10
+ super({ abi: merkleRewardsDistributor, ...params });
11
+ this.contractName = 'merkleRewardsDistributor';
12
+ }
13
+
14
+ // ----- Views -----
15
+ async getRoot({ contestId, tokenAddress }) {
16
+ return await this.params.contract
17
+ .getContract()
18
+ .methods
19
+ .getRoot(contestId, tokenAddress)
20
+ .call();
21
+ }
22
+
23
+ async isClaimed({ contestId, tokenAddress, index }) {
24
+ return await this.params.contract
25
+ .getContract()
26
+ .methods
27
+ .isClaimed(contestId, tokenAddress, index)
28
+ .call();
29
+ }
30
+
31
+ async isClaimedMany({ contestIds, tokenAddresses, indices }) {
32
+ return await this.params.contract
33
+ .getContract()
34
+ .methods
35
+ .isClaimedMany(contestIds, tokenAddresses, indices)
36
+ .call();
37
+ }
38
+
39
+ async isAdmin({ user }) {
40
+ return await this.params.contract
41
+ .getContract()
42
+ .methods
43
+ .isAdmin(user)
44
+ .call();
45
+ }
46
+
47
+ async getClaims({ user, tokenAddress, index }) {
48
+ return await this.getEvents(
49
+ 'Claim',
50
+ {
51
+ user,
52
+ tokenAddress,
53
+ index
54
+ }
55
+ );
56
+ }
57
+
58
+ // ----- Admin -----
59
+ async publishRoot({ contestId, tokenAddress, root }) {
60
+ return await this.__sendTx(
61
+ this.getContract().methods.publishRoot(contestId, tokenAddress, root)
62
+ );
63
+ }
64
+
65
+ async addAdmin({ user }) {
66
+ return await this.__sendTx(
67
+ this.getContract().methods.addAdmin(user)
68
+ );
69
+ }
70
+
71
+ async removeAdmin({ user }) {
72
+ return await this.__sendTx(
73
+ this.getContract().methods.removeAdmin(user)
74
+ );
75
+ }
76
+
77
+ async withdraw({ tokenAddress, amount }) {
78
+ const amountDecimals = Numbers.toSmartContractDecimals(amount, 18);
79
+ return await this.__sendTx(
80
+ this.getContract().methods.withdraw(tokenAddress, amountDecimals)
81
+ );
82
+ }
83
+
84
+ // ----- Claims -----
85
+ async claim({ contestId, tokenAddress, index, user, amount, proof }) {
86
+ const amountDecimals = Numbers.toSmartContractDecimals(amount, 18);
87
+ return await this.__sendTx(
88
+ this.getContract().methods.claim(contestId, tokenAddress, index, user, amountDecimals, proof)
89
+ );
90
+ }
91
+
92
+ async claimMany({ entries }) {
93
+ // entries: array of { contestId, tokenAddress, index, user, amount, proof }
94
+ const contestIds = entries.map(e => e.contestId);
95
+ const tokens = entries.map(e => e.tokenAddress);
96
+ const indices = entries.map(e => e.index);
97
+ const users = entries.map(e => e.user);
98
+ const amounts = entries.map(e => Numbers.toSmartContractDecimals(e.amount, 18));
99
+ const proofs = entries.map(e => e.proof);
100
+
101
+ return await this.__sendTx(
102
+ this.getContract().methods.claimMany(contestIds, tokens, indices, users, amounts, proofs)
103
+ );
104
+ }
105
+ }
106
+
107
+ module.exports = MerkleRewardsDistributorContract;
@@ -14,6 +14,7 @@ const FantasyERC20Contract = require('./FantasyERC20Contract');
14
14
  const WETH9Contract = require('./WETH9Contract');
15
15
  const ArbitrationContract = require('./ArbitrationContract');
16
16
  const ArbitrationProxyContract = require('./ArbitrationProxyContract');
17
+ const MerkleRewardsDistributorContract = require('./MerkleRewardsDistributorContract');
17
18
 
18
19
  module.exports = {
19
20
  ERC20Contract,
@@ -31,5 +32,6 @@ module.exports = {
31
32
  ArbitrationContract,
32
33
  ArbitrationProxyContract,
33
34
  PredictionMarketV3FactoryContract,
34
- PredictionMarketV3ControllerContract
35
+ PredictionMarketV3ControllerContract,
36
+ MerkleRewardsDistributorContract
35
37
  }
@@ -0,0 +1,316 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import "forge-std/Test.sol";
5
+ import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
6
+ import {MerkleProof} from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
7
+
8
+ import {MerkleRewardsDistributor} from "../contracts/MerkleRewardsDistributor.sol";
9
+ import {ERC20MinterPauser} from "../contracts/ERC20MinterPauser.sol";
10
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
11
+
12
+ contract MerkleRewardsDistributorTest is Test {
13
+ MerkleRewardsDistributor internal distributor;
14
+ ERC20MinterPauser internal token;
15
+
16
+ address internal deployer = address(this);
17
+ address internal alice;
18
+ address internal bob;
19
+ address internal carol;
20
+
21
+ function setUp() public {
22
+ alice = makeAddr("ALICE");
23
+ bob = makeAddr("BOB");
24
+ carol = makeAddr("CAROL");
25
+
26
+ address implementation = address(new MerkleRewardsDistributor());
27
+ bytes memory initData = abi.encodeCall(MerkleRewardsDistributor.initialize, (deployer));
28
+ ERC1967Proxy proxy = new ERC1967Proxy(implementation, initData);
29
+ distributor = MerkleRewardsDistributor(address(proxy));
30
+
31
+ token = new ERC20MinterPauser("Test Token", "TEST");
32
+ token.mint(address(distributor), 1_000_000 ether);
33
+ }
34
+
35
+ // local Merkle builder (keccak abi.encode)
36
+ function _leaf(uint256 index, address user, address tokenAddr, uint256 amount, string memory contestId)
37
+ internal pure returns (bytes32)
38
+ {
39
+ return keccak256(abi.encode(index, user, tokenAddr, amount, contestId));
40
+ }
41
+
42
+ function _buildRoot(bytes32[] memory leaves) internal pure returns (bytes32 root) {
43
+ if (leaves.length == 0) return bytes32(0);
44
+ while (leaves.length > 1) {
45
+ uint256 n = (leaves.length + 1) / 2;
46
+ bytes32[] memory next = new bytes32[](n);
47
+ for (uint256 i = 0; i < leaves.length; i += 2) {
48
+ bytes32 left = leaves[i];
49
+ bytes32 right = (i + 1 < leaves.length) ? leaves[i + 1] : left;
50
+ next[i/2] = _parent(left, right);
51
+ }
52
+ leaves = next;
53
+ }
54
+ return leaves[0];
55
+ }
56
+
57
+ function _parent(bytes32 a, bytes32 b) internal pure returns (bytes32) {
58
+ (bytes32 left, bytes32 right) = a <= b ? (a,b) : (b,a);
59
+ return keccak256(abi.encodePacked(left, right));
60
+ }
61
+
62
+ function _proof(bytes32[] memory leaves, uint256 index) internal pure returns (bytes32[] memory p) {
63
+ // Build layers copying logic from _buildRoot to also record siblings
64
+ bytes32[] memory current = leaves;
65
+ bytes32[] memory proofTmp = new bytes32[](64);
66
+ uint256 proofLen = 0;
67
+ uint256 idx = index;
68
+ while (current.length > 1) {
69
+ uint256 n = (current.length + 1) / 2;
70
+ bytes32[] memory next = new bytes32[](n);
71
+ for (uint256 i = 0; i < current.length; i += 2) {
72
+ bytes32 left = current[i];
73
+ bytes32 right = (i + 1 < current.length) ? current[i + 1] : left;
74
+ next[i/2] = _parent(left, right);
75
+ if (i == (idx ^ 1) - 1 || i == (idx & ~uint256(1))) {
76
+ // sibling for this level
77
+ bytes32 sib = (idx % 2 == 0) ? right : left;
78
+ if (i + 1 >= current.length && idx % 2 == 1) {
79
+ // when paired with itself, skip duplicate sibling
80
+ } else {
81
+ proofTmp[proofLen++] = sib;
82
+ }
83
+ }
84
+ }
85
+ current = next;
86
+ idx >>= 1;
87
+ }
88
+ p = new bytes32[](proofLen);
89
+ for (uint256 i = 0; i < proofLen; i++) p[i] = proofTmp[i];
90
+ }
91
+
92
+ function test_PublishRootAndSingleClaim() public {
93
+ string memory cid = "daily:2025-09-30";
94
+
95
+ bytes32[] memory leaves = new bytes32[](2);
96
+ leaves[0] = _leaf(0, alice, address(token), 100 ether, cid);
97
+ leaves[1] = _leaf(1, bob, address(token), 200 ether, cid);
98
+ bytes32 root = _buildRoot(leaves);
99
+
100
+ distributor.publishRoot(cid, IERC20(address(token)), root);
101
+ assertEq(distributor.getRoot(cid, IERC20(address(token))), root);
102
+
103
+ bytes32[] memory proofAlice = _proof(leaves, 0);
104
+ assertFalse(distributor.isClaimed(cid, IERC20(address(token)), 0));
105
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 100 ether, proofAlice);
106
+ assertTrue(distributor.isClaimed(cid, IERC20(address(token)), 0));
107
+ assertEq(token.balanceOf(alice), 100 ether);
108
+ }
109
+
110
+ function test_RevertOnInvalidProof() public {
111
+ string memory cid = "weekly:2025-W40";
112
+
113
+ bytes32[] memory leaves = new bytes32[](1);
114
+ leaves[0] = _leaf(0, alice, address(token), 50 ether, cid);
115
+ bytes32 root = _buildRoot(leaves);
116
+ distributor.publishRoot(cid, IERC20(address(token)), root);
117
+
118
+ bytes32[] memory wrongProof = new bytes32[](0);
119
+ vm.expectRevert(bytes("MerkleRewards: invalid proof"));
120
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 51 ether, wrongProof);
121
+ }
122
+
123
+ function test_RevertOnDoubleClaim() public {
124
+ string memory cid = "monthly:2025-09";
125
+
126
+ bytes32[] memory leaves = new bytes32[](1);
127
+ leaves[0] = _leaf(0, alice, address(token), 10 ether, cid);
128
+ bytes32 root = _buildRoot(leaves);
129
+ distributor.publishRoot(cid, IERC20(address(token)), root);
130
+
131
+ bytes32[] memory proof = _proof(leaves, 0);
132
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 10 ether, proof);
133
+
134
+ vm.expectRevert(bytes("MerkleRewards: already claimed"));
135
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 10 ether, proof);
136
+ }
137
+
138
+ function test_ClaimMany() public {
139
+ string memory cid1 = "daily:2025-09-30";
140
+ string memory cid2 = "weekly:2025-W40";
141
+
142
+ bytes32[] memory leaves1 = new bytes32[](1);
143
+ leaves1[0] = _leaf(0, alice, address(token), 100 ether, cid1);
144
+ bytes32 root1 = _buildRoot(leaves1);
145
+ distributor.publishRoot(cid1, IERC20(address(token)), root1);
146
+
147
+ bytes32[] memory leaves2 = new bytes32[](2);
148
+ leaves2[0] = _leaf(0, alice, address(token), 5 ether, cid2);
149
+ leaves2[1] = _leaf(1, bob, address(token), 7 ether, cid2);
150
+ bytes32 root2 = _buildRoot(leaves2);
151
+ distributor.publishRoot(cid2, IERC20(address(token)), root2);
152
+
153
+ string[] memory cids = new string[](2);
154
+ cids[0] = cid1; cids[1] = cid2;
155
+ IERC20[] memory tokens = new IERC20[](2);
156
+ tokens[0] = IERC20(address(token)); tokens[1] = IERC20(address(token));
157
+ uint256[] memory idxs = new uint256[](2);
158
+ idxs[0] = 0; idxs[1] = 0;
159
+ address[] memory users = new address[](2);
160
+ users[0] = alice; users[1] = alice;
161
+ uint256[] memory amts = new uint256[](2);
162
+ amts[0] = 100 ether; amts[1] = 5 ether;
163
+ bytes32[][] memory proofs = new bytes32[][](2);
164
+ proofs[0] = _proof(leaves1, 0);
165
+ proofs[1] = _proof(leaves2, 0);
166
+
167
+ distributor.claimMany(cids, tokens, idxs, users, amts, proofs);
168
+ assertTrue(distributor.isClaimed(cid1, IERC20(address(token)), 0));
169
+ assertTrue(distributor.isClaimed(cid2, IERC20(address(token)), 0));
170
+ assertEq(token.balanceOf(alice), 105 ether);
171
+ }
172
+
173
+ function test_AdminWithdraw() public {
174
+ uint256 balBefore = token.balanceOf(address(distributor));
175
+ distributor.withdraw(IERC20(address(token)), 1 ether);
176
+ assertEq(token.balanceOf(address(distributor)), balBefore - 1 ether);
177
+ assertEq(token.balanceOf(deployer), 1 ether);
178
+ }
179
+
180
+ function test_OnlyOwnerGuards() public {
181
+ string memory cid = "daily:2025-10-01";
182
+ bytes32 dummyRoot = bytes32(uint256(1));
183
+
184
+ vm.prank(alice);
185
+ vm.expectRevert(bytes("MerkleRewards: must have admin role"));
186
+ distributor.publishRoot(cid, IERC20(address(token)), dummyRoot);
187
+
188
+ vm.prank(alice);
189
+ vm.expectRevert();
190
+ distributor.withdraw(IERC20(address(token)), 1);
191
+ }
192
+
193
+ function test_AdminRoleCanPublishRoot() public {
194
+ // grant admin to alice and publish
195
+ distributor.addAdmin(alice);
196
+ assertTrue(distributor.isAdmin(alice));
197
+
198
+ string memory cid = "daily:2025-10-05";
199
+ bytes32[] memory leaves = new bytes32[](1);
200
+ leaves[0] = _leaf(0, alice, address(token), 1 ether, cid);
201
+ bytes32 root = _buildRoot(leaves);
202
+
203
+ vm.prank(alice);
204
+ distributor.publishRoot(cid, IERC20(address(token)), root);
205
+ assertEq(distributor.getRoot(cid, IERC20(address(token))), root);
206
+
207
+ // remove admin and ensure access revoked
208
+ distributor.removeAdmin(alice);
209
+ assertFalse(distributor.isAdmin(alice));
210
+ vm.prank(alice);
211
+ vm.expectRevert(bytes("MerkleRewards: must have admin role"));
212
+ distributor.publishRoot(cid, IERC20(address(token)), root);
213
+ }
214
+
215
+ function test_RootNotSet_RevertOnClaim() public {
216
+ string memory cid = "weekly:2025-W41";
217
+ bytes32[] memory proof = new bytes32[](0);
218
+ vm.expectRevert(bytes("MerkleRewards: root not set"));
219
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 1, proof);
220
+ }
221
+
222
+ function test_RootUpdateInvalidatesOldProof() public {
223
+ string memory cid = "monthly:2025-10";
224
+
225
+ // first root
226
+ bytes32[] memory leaves1 = new bytes32[](1);
227
+ leaves1[0] = _leaf(0, alice, address(token), 10 ether, cid);
228
+ bytes32 root1 = _buildRoot(leaves1);
229
+ distributor.publishRoot(cid, IERC20(address(token)), root1);
230
+ bytes32[] memory proof1 = _proof(leaves1, 0);
231
+
232
+ // update root with different allocation
233
+ bytes32[] memory leaves2 = new bytes32[](1);
234
+ leaves2[0] = _leaf(0, alice, address(token), 20 ether, cid);
235
+ bytes32 root2 = _buildRoot(leaves2);
236
+ distributor.publishRoot(cid, IERC20(address(token)), root2);
237
+
238
+ // old proof must fail now
239
+ vm.expectRevert(bytes("MerkleRewards: invalid proof"));
240
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 10 ether, proof1);
241
+
242
+ // new proof succeeds
243
+ bytes32[] memory proof2 = _proof(leaves2, 0);
244
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 20 ether, proof2);
245
+ assertEq(token.balanceOf(alice), 20 ether);
246
+ }
247
+
248
+ function test_ClaimWithWrongTokenOrContest() public {
249
+ string memory cid = "daily:2025-10-02";
250
+ bytes32[] memory leaves = new bytes32[](1);
251
+ leaves[0] = _leaf(0, alice, address(token), 5 ether, cid);
252
+ distributor.publishRoot(cid, IERC20(address(token)), _buildRoot(leaves));
253
+ bytes32[] memory proof = _proof(leaves, 0);
254
+
255
+ // wrong token -> root not set for that key
256
+ ERC20MinterPauser other = new ERC20MinterPauser("Other", "OTH");
257
+ vm.expectRevert(bytes("MerkleRewards: root not set"));
258
+ distributor.claim(cid, IERC20(address(other)), 0, alice, 5 ether, proof);
259
+
260
+ // wrong amount -> invalid proof
261
+ vm.expectRevert(bytes("MerkleRewards: invalid proof"));
262
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 6 ether, proof);
263
+ }
264
+
265
+ function test_IsClaimedReflectsState() public {
266
+ string memory cid = "daily:2025-10-03";
267
+ bytes32[] memory leaves = new bytes32[](1);
268
+ leaves[0] = _leaf(0, alice, address(token), 1 ether, cid);
269
+ distributor.publishRoot(cid, IERC20(address(token)), _buildRoot(leaves));
270
+ assertFalse(distributor.isClaimed(cid, IERC20(address(token)), 0));
271
+ distributor.claim(cid, IERC20(address(token)), 0, alice, 1 ether, _proof(leaves, 0));
272
+ assertTrue(distributor.isClaimed(cid, IERC20(address(token)), 0));
273
+ }
274
+
275
+ function test_ClaimMany_LengthMismatchReverts() public {
276
+ string[] memory cid = new string[](1);
277
+ cid[0] = "daily:2025-10-04";
278
+ IERC20[] memory toks = new IERC20[](1);
279
+ toks[0] = IERC20(address(token));
280
+ uint256[] memory idx = new uint256[](1);
281
+ idx[0] = 0;
282
+ address[] memory users = new address[](1);
283
+ users[0] = alice;
284
+ // amounts empty to force mismatch
285
+ uint256[] memory amts = new uint256[](0);
286
+ bytes32[][] memory proofs = new bytes32[][](1);
287
+ proofs[0] = new bytes32[](0);
288
+
289
+ vm.expectRevert(bytes("MerkleRewards: arrays length mismatch"));
290
+ distributor.claimMany(cid, toks, idx, users, amts, proofs);
291
+ }
292
+
293
+ function test_ClaimMany_DuplicateIndexReverts() public {
294
+ string memory cid = "weekly:2025-W42";
295
+ bytes32[] memory leaves = new bytes32[](1);
296
+ leaves[0] = _leaf(0, alice, address(token), 3 ether, cid);
297
+ distributor.publishRoot(cid, IERC20(address(token)), _buildRoot(leaves));
298
+
299
+ string[] memory cids = new string[](2);
300
+ cids[0] = cid; cids[1] = cid;
301
+ IERC20[] memory toks = new IERC20[](2);
302
+ toks[0] = IERC20(address(token)); toks[1] = IERC20(address(token));
303
+ uint256[] memory idxs = new uint256[](2);
304
+ idxs[0] = 0; idxs[1] = 0; // duplicate same index
305
+ address[] memory users = new address[](2);
306
+ users[0] = alice; users[1] = alice;
307
+ uint256[] memory amts = new uint256[](2);
308
+ amts[0] = 3 ether; amts[1] = 3 ether;
309
+ bytes32[][] memory proofs = new bytes32[][](2);
310
+ proofs[0] = _proof(leaves, 0);
311
+ proofs[1] = _proof(leaves, 0);
312
+
313
+ vm.expectRevert(bytes("MerkleRewards: already claimed"));
314
+ distributor.claimMany(cids, toks, idxs, users, amts, proofs);
315
+ }
316
+ }
@@ -1,55 +0,0 @@
1
- // SPDX-License-Identifier: GPL-2.0
2
- pragma solidity ^0.8.26;
3
-
4
- import {Script} from "forge-std/Script.sol";
5
- import {console} from "forge-std/console.sol";
6
- // Import your contracts here
7
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8
- import {PredictionMarketV3_3, IWETH} from "../contracts/PredictionMarketV3_3.sol";
9
- import {IPredictionMarketV3Manager} from "../contracts/IPredictionMarketV3Manager.sol";
10
- import {PredictionMarketV3Manager, FantasyERC20} from "../contracts/PredictionMarketV3Manager.sol";
11
- import {IRealityETH_ERC20} from "../contracts/IRealityETH_ERC20.sol";
12
-
13
- contract UpdateMarket is Script {
14
- function run() public {
15
-
16
- vm.startBroadcast();
17
-
18
- address payable PREDICTION_MARKET_V3_3 = payable(address(0x3e0F5F8F5Fb043aBFA475C0308417Bf72c463289));
19
- address PREDICTION_MARKET_V3_MANAGER = address(0x68dDc91CCC06e63d74905D901A30edEA7C77EebE);
20
-
21
- PredictionMarketV3_3.MarketState state = PredictionMarketV3_3(PREDICTION_MARKET_V3_3).getMarketState(486);
22
- console2.log("state: ", state);
23
-
24
- PredictionMarketV3_3.MarketUpdateDescription memory description = PredictionMarketV3_3.MarketUpdateDescription({
25
- closesAtTimestamp: 1762066800,
26
- balance: 456538064447348172635074428,
27
- liquidity: 120000000000000000000000000,
28
- sharesAvailable: 1272835104225672313766121101,
29
- state: PredictionMarketV3_3.MarketState.open,
30
- resolution: PredictionMarketV3_3.MarketResolution({
31
- resolved: false,
32
- realitio: IRealityETH_ERC20(0x71D76Ff3C2729071B2c52A667197d9f715029F2c),
33
- outcomeId: 115792089237316195423570985008687907853269984665640564039457584007913129639935,
34
- questionId: 0x21d6c6c195572d82a7d0a7ba677251dfd3839b21e09df9b39ae36ec99fa6744d,
35
- realitioTimeout: 3600
36
- }),
37
- feesPoolWeight: 0,
38
- feesTreasury: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
39
- feesDistributor: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
40
- buyFees: PredictionMarketV3_3.Fees({fee: 0, treasuryFee: 0, distributorFee: 0}),
41
- sellFees: PredictionMarketV3_3.Fees({fee: 0, treasuryFee: 0, distributorFee: 0}),
42
- outcomeCount: 5,
43
- token: IERC20(0x0b07cf011B6e2b7E0803b892d97f751659940F23),
44
- manager: IPredictionMarketV3Manager(0x2EbD002d755bdFadb9e5549aA00a80bd7fa408AE),
45
- creator: address(0xBc30e9765Dc8c735206c76DE96d369754eBbcc1f),
46
- paused: false
47
- });
48
-
49
- PredictionMarketV3_3(PREDICTION_MARKET_V3_3).updateMarket(486, description);
50
- PredictionMarketV3_3(PREDICTION_MARKET_V3_3).updateMarketResolution(486, description);
51
-
52
- state = PredictionMarketV3_3(PREDICTION_MARKET_V3_3).getMarketState(486);
53
- console2.log("new state: ", state);
54
- }
55
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes