@coti-io/coti-contracts 1.1.0 → 1.3.0

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 (433) hide show
  1. package/.github/workflows/npm-publish.yml +19 -15
  2. package/README.md +3 -1
  3. package/contracts/disperse/coinByRatio/FixedRatioCoinDisperser.sol +452 -0
  4. package/contracts/disperse/coinByRatio/FixedRatioCoinDisperserLeftoverS1.sol +490 -0
  5. package/contracts/disperse/coinByRatio/FixedRatioCoinDisperserUnlimitedWindow.sol +432 -0
  6. package/contracts/disperse/disperseToken/MintDisperser.sol +120 -0
  7. package/contracts/disperse/disperseToken/TokenDisperser.sol +139 -0
  8. package/contracts/messaging/PrivateMessaging.sol +456 -0
  9. package/contracts/mocks/messaging/PrivateMessagingHarness.sol +54 -0
  10. package/contracts/mocks/oracle/CotiPriceConsumerMock.sol +31 -0
  11. package/contracts/mocks/token/ERC20DecimalsMock.sol +21 -0
  12. package/contracts/mocks/token/ERC20Mock.sol +19 -0
  13. package/contracts/mocks/token/PrivateERC20/EncryptedTokenReceiverMock.sol +18 -0
  14. package/contracts/mocks/token/PrivateERC20/PrivacyBridgeERC20Mock.sol +18 -0
  15. package/contracts/mocks/token/PrivateERC20/PrivateERC20AuditorSupplyMock.sol +44 -0
  16. package/contracts/mocks/token/PrivateERC20/PrivateERC20CappedMock.sol +23 -0
  17. package/contracts/mocks/token/PrivateERC20/PrivateERC20GtCallerMock.sol +29 -0
  18. package/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.sol +9 -4
  19. package/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol +39 -0
  20. package/contracts/mocks/token/PrivateERC20/PublicReentrantTokenReceiverMock.sol +29 -0
  21. package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverBoolMock.sol +18 -0
  22. package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverMock.sol +12 -0
  23. package/contracts/mocks/utils/mpc/Arithmetic128TestsContract.sol +123 -0
  24. package/contracts/mocks/utils/mpc/Arithmetic256TestsContract.sol +122 -0
  25. package/contracts/mocks/utils/mpc/Bitwise128TestsContract.sol +65 -0
  26. package/contracts/mocks/utils/mpc/Bitwise256TestsContract.sol +65 -0
  27. package/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.sol +64 -0
  28. package/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.sol +64 -0
  29. package/contracts/mocks/utils/mpc/Comparison128TestsContract.sol +98 -0
  30. package/contracts/mocks/utils/mpc/Comparison256TestsContract.sol +98 -0
  31. package/contracts/mocks/utils/mpc/MinMax128TestsContract.sol +44 -0
  32. package/contracts/mocks/utils/mpc/MinMax256TestsContract.sol +44 -0
  33. package/contracts/mocks/utils/mpc/MinimalImplementation.sol +55 -0
  34. package/contracts/mocks/utils/mpc/MinimalProxy.sol +27 -0
  35. package/contracts/mocks/utils/mpc/MpcOperations128TestContract.sol +267 -0
  36. package/contracts/mocks/utils/mpc/MpcOperationsTestContract.sol +334 -0
  37. package/contracts/mocks/utils/mpc/Mux128TestsContract.sol +24 -0
  38. package/contracts/mocks/utils/mpc/Mux256TestsContract.sol +24 -0
  39. package/contracts/mocks/utils/mpc/OnBoard128TestsContract.sol +93 -0
  40. package/contracts/mocks/utils/mpc/OnBoard256TestsContract.sol +116 -0
  41. package/contracts/mocks/utils/mpc/PrivacyImplementationV1.sol +157 -0
  42. package/contracts/mocks/utils/mpc/PrivacyImplementationV2.sol +133 -0
  43. package/contracts/mocks/utils/mpc/PrivacyProxy.sol +137 -0
  44. package/contracts/mocks/utils/mpc/Random128TestsContract.sol +32 -0
  45. package/contracts/mocks/utils/mpc/Random256TestsContract.sol +32 -0
  46. package/contracts/mocks/utils/mpc/Shift128TestsContract.sol +36 -0
  47. package/contracts/mocks/utils/mpc/Shift256TestsContract.sol +36 -0
  48. package/contracts/mocks/utils/mpc/Transfer128TestsContract.sol +29 -0
  49. package/contracts/mocks/utils/mpc/Transfer256TestsContract.sol +29 -0
  50. package/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.sol +32 -0
  51. package/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.sol +32 -0
  52. package/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.sol +59 -0
  53. package/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.sol +63 -0
  54. package/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.sol +99 -0
  55. package/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock.sol +7 -6
  56. package/contracts/node/CotiNodeRewards.sol +199 -0
  57. package/contracts/node/SoulboundNodeNFT.sol +519 -0
  58. package/contracts/oracle/CotiPriceConsumer.sol +103 -0
  59. package/contracts/oracle/ICotiPriceConsumer.sol +15 -0
  60. package/contracts/oracle/IStdReference.sol +26 -0
  61. package/contracts/privacyBridge/PrivacyBridge.sol +600 -0
  62. package/contracts/privacyBridge/PrivacyBridgeCotiNative.sol +316 -0
  63. package/contracts/privacyBridge/PrivacyBridgeERC20.sol +333 -0
  64. package/contracts/privacyBridge/PrivacyBridgeUSDCe.sol +22 -0
  65. package/contracts/privacyBridge/PrivacyBridgeUSDT.sol +23 -0
  66. package/contracts/privacyBridge/PrivacyBridgeWADA.sol +23 -0
  67. package/contracts/privacyBridge/PrivacyBridgeWBTC.sol +23 -0
  68. package/contracts/privacyBridge/PrivacyBridgeWETH.sol +23 -0
  69. package/contracts/privacyBridge/PrivacyBridgegCoti.sol +23 -0
  70. package/contracts/token/PrivateERC20/IPrivateERC20.sol +273 -40
  71. package/contracts/token/PrivateERC20/ITokenReceiver.sol +17 -0
  72. package/contracts/token/PrivateERC20/ITokenReceiverEncrypted.sol +20 -0
  73. package/contracts/token/PrivateERC20/PrivateERC20.sol +861 -152
  74. package/contracts/token/PrivateERC20/tokens/PrivateBridgedUSDC.sol +17 -0
  75. package/contracts/token/PrivateERC20/tokens/PrivateCOTI.sol +13 -0
  76. package/contracts/token/PrivateERC20/tokens/PrivateCOTITreasuryGovernanceToken.sol +17 -0
  77. package/contracts/token/PrivateERC20/tokens/PrivateTetherUSD.sol +17 -0
  78. package/contracts/token/PrivateERC20/tokens/PrivateWrappedADA.sol +17 -0
  79. package/contracts/token/PrivateERC20/tokens/PrivateWrappedBTC.sol +17 -0
  80. package/contracts/token/PrivateERC20/tokens/PrivateWrappedEther.sol +13 -0
  81. package/contracts/utils/mpc/MpcCore.sol +10780 -4962
  82. package/contracts/utils/mpc/MpcInterface.sol +7 -2
  83. package/hardhat/gasPriceBump.ts +60 -0
  84. package/hardhat.config.ts +63 -12
  85. package/package.json +22 -4
  86. package/scripts/add-bridge-operators.cjs +87 -0
  87. package/scripts/deploy-coti-price-consumer-mainnet.cjs +49 -0
  88. package/scripts/deployCotiNodeRewards.ts +51 -0
  89. package/scripts/deploySoulboundNodeNFT.ts +57 -0
  90. package/scripts/redeploy-private-and-bridges.cjs +201 -0
  91. package/scripts/set-bridge-limits-mainnet.cjs +177 -0
  92. package/scripts/verify-mainnet-private-bridges.cjs +289 -0
  93. package/sonar-project.properties +11 -0
  94. package/test/messaging/PrivateMessaging.test.ts +158 -0
  95. package/test/privacyBridge/UnifiedPrivacyBridges.test.cjs +1663 -0
  96. package/test/token/PrivateERC20/PrivacyBridge.fees.test.ts +250 -0
  97. package/test/token/PrivateERC20/PrivateERC20.test.ts +1109 -268
  98. package/test/utils/mpc/Bitwise128.test.ts +92 -0
  99. package/test/utils/mpc/Bitwise256.test.ts +101 -0
  100. package/test/utils/mpc/BuildInputText128.test.ts +398 -0
  101. package/test/utils/mpc/CheckedArithmetic128.test.ts +45 -0
  102. package/test/utils/mpc/CheckedArithmetic256.test.ts +45 -0
  103. package/test/utils/mpc/Comparison128.test.ts +48 -0
  104. package/test/utils/mpc/Comparison256.test.ts +48 -0
  105. package/test/utils/mpc/InvalidCiphertext.test.ts +179 -0
  106. package/test/utils/mpc/InvalidCiphertext128.test.ts +179 -0
  107. package/test/utils/mpc/InvalidCiphertext256.test.ts +201 -0
  108. package/test/utils/mpc/InvalidTransfer128.test.ts +166 -0
  109. package/test/utils/mpc/InvalidTransfer256.test.ts +158 -0
  110. package/test/utils/mpc/MinMax128.test.ts +44 -0
  111. package/test/utils/mpc/MinMax256.test.ts +44 -0
  112. package/test/utils/mpc/MinimalProxyDemo.test.ts +280 -0
  113. package/test/utils/mpc/MpcOperations.test.ts +589 -0
  114. package/test/utils/mpc/MpcOperations128.test.ts +632 -0
  115. package/test/utils/mpc/Mux128.test.ts +41 -0
  116. package/test/utils/mpc/Mux256.test.ts +41 -0
  117. package/test/utils/mpc/NegativeCases.test.ts +478 -0
  118. package/test/utils/mpc/NegativeCases128.test.ts +237 -0
  119. package/test/utils/mpc/NegativeCases256.test.ts +236 -0
  120. package/test/utils/mpc/OnBoard128.test.ts +90 -0
  121. package/test/utils/mpc/OnBoard256.test.ts +114 -0
  122. package/test/utils/mpc/Precompile128.test.ts +51 -0
  123. package/test/utils/mpc/Precompile256.test.ts +115 -0
  124. package/test/utils/mpc/ProxyValidation.test.ts +199 -0
  125. package/test/utils/mpc/Random128.test.ts +47 -0
  126. package/test/utils/mpc/Random256.test.ts +47 -0
  127. package/test/utils/mpc/Shift128.test.ts +41 -0
  128. package/test/utils/mpc/Shift256.test.ts +41 -0
  129. package/test/utils/mpc/Transfer128.test.ts +46 -0
  130. package/test/utils/mpc/Transfer256.test.ts +46 -0
  131. package/test/utils/mpc/TransferWithAllowance128.test.ts +48 -0
  132. package/test/utils/mpc/TransferWithAllowance256.test.ts +48 -0
  133. package/test/utils/mpc/UpgradeablePrivacyProxy.test.ts +307 -0
  134. package/test/utils/mpc/ValidateCiphertext.test.ts +221 -0
  135. package/test/utils/mpc/ValidateCiphertext256.test.ts +161 -0
  136. package/test/utils/mpc/ValidateCiphertext256Decryption.test.ts +383 -0
  137. package/test/utils/mpc/WalletEncryptionFailures.test.ts +494 -0
  138. package/test/utils/privateErc20Helpers.ts +97 -0
  139. package/typechain-types/@openzeppelin/contracts/access/AccessControl.ts +324 -0
  140. package/typechain-types/@openzeppelin/contracts/access/AccessControlEnumerable.ts +361 -0
  141. package/typechain-types/@openzeppelin/contracts/access/IAccessControl.ts +292 -0
  142. package/typechain-types/@openzeppelin/contracts/access/IAccessControlEnumerable.ts +329 -0
  143. package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
  144. package/typechain-types/@openzeppelin/contracts/access/index.ts +8 -0
  145. package/typechain-types/@openzeppelin/contracts/index.ts +4 -0
  146. package/typechain-types/@openzeppelin/contracts/security/Pausable.ts +150 -0
  147. package/typechain-types/@openzeppelin/contracts/security/index.ts +4 -0
  148. package/typechain-types/@openzeppelin/contracts/token/ERC20/ERC20.ts +330 -0
  149. package/typechain-types/@openzeppelin/contracts/token/ERC20/IERC20.ts +262 -0
  150. package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.ts +286 -0
  151. package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.ts +143 -0
  152. package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/index.ts +5 -0
  153. package/typechain-types/@openzeppelin/contracts/token/ERC20/index.ts +7 -0
  154. package/typechain-types/@openzeppelin/contracts/token/ERC721/ERC721.ts +420 -0
  155. package/typechain-types/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.ts +420 -0
  156. package/typechain-types/@openzeppelin/contracts/token/ERC721/extensions/index.ts +4 -0
  157. package/typechain-types/@openzeppelin/contracts/token/ERC721/index.ts +3 -0
  158. package/typechain-types/@openzeppelin/contracts/token/index.ts +2 -0
  159. package/typechain-types/contracts/disperse/coinByRatio/FixedRatioCoinDisperser.ts +679 -0
  160. package/typechain-types/contracts/disperse/coinByRatio/FixedRatioCoinDisperserLeftoverS1.ts +735 -0
  161. package/typechain-types/contracts/disperse/coinByRatio/FixedRatioCoinDisperserUnlimitedWindow.ts +734 -0
  162. package/typechain-types/contracts/disperse/coinByRatio/index.ts +6 -0
  163. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20.ts +97 -0
  164. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/IOwnable.ts +95 -0
  165. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser.ts +228 -0
  166. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +6 -0
  167. package/typechain-types/contracts/disperse/disperseToken/TokenDisperser.ts +191 -0
  168. package/typechain-types/contracts/disperse/disperseToken/index.ts +6 -0
  169. package/typechain-types/contracts/disperse/index.ts +7 -0
  170. package/typechain-types/contracts/index.ts +10 -0
  171. package/typechain-types/contracts/messaging/PrivateMessaging.ts +871 -0
  172. package/typechain-types/contracts/messaging/index.ts +4 -0
  173. package/typechain-types/contracts/mocks/index.ts +4 -0
  174. package/typechain-types/contracts/mocks/messaging/PrivateMessagingHarness.ts +951 -0
  175. package/typechain-types/contracts/mocks/messaging/index.ts +4 -0
  176. package/typechain-types/contracts/mocks/oracle/CotiPriceConsumerMock.ts +163 -0
  177. package/typechain-types/contracts/mocks/oracle/index.ts +4 -0
  178. package/typechain-types/contracts/mocks/token/ERC20DecimalsMock.ts +349 -0
  179. package/typechain-types/contracts/mocks/token/ERC20Mock.ts +368 -0
  180. package/typechain-types/contracts/mocks/token/PrivateERC20/EncryptedTokenReceiverMock.ts +108 -0
  181. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivacyBridgeERC20Mock.ts +1892 -0
  182. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20AuditorSupplyMock.ts +1296 -0
  183. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20CappedMock.ts +1270 -0
  184. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20GtCallerMock.ts +166 -0
  185. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.ts +827 -78
  186. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/IPrivateERC20TransferAndCallEncrypted.ts +102 -0
  187. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/PrivateERC20ReentrantReceiverMock.ts +154 -0
  188. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/index.ts +5 -0
  189. package/typechain-types/contracts/mocks/token/PrivateERC20/PublicReentrantTokenReceiverMock.ts +155 -0
  190. package/typechain-types/contracts/mocks/token/PrivateERC20/PublicTokenReceiverBoolMock.ts +107 -0
  191. package/typechain-types/contracts/mocks/token/PrivateERC20/PublicTokenReceiverMock.ts +100 -0
  192. package/typechain-types/contracts/mocks/token/PrivateERC20/index.ts +10 -0
  193. package/typechain-types/contracts/mocks/token/index.ts +2 -0
  194. package/typechain-types/contracts/mocks/utils/mpc/{Arithmetic128BitTestsContract.ts → Arithmetic128TestsContract.ts} +96 -126
  195. package/typechain-types/contracts/mocks/utils/mpc/{Arithmetic256BitTestsContract.ts → Arithmetic256TestsContract.ts} +97 -133
  196. package/typechain-types/contracts/mocks/utils/mpc/{Bitwise128BitTestsContract.ts → Bitwise128TestsContract.ts} +52 -59
  197. package/typechain-types/contracts/mocks/utils/mpc/{Bitwise256BitTestsContract.ts → Bitwise256TestsContract.ts} +52 -59
  198. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.ts +206 -0
  199. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.ts +206 -0
  200. package/typechain-types/contracts/mocks/utils/mpc/{Comparison128BitTestsContract.ts → Comparison128TestsContract.ts} +115 -97
  201. package/typechain-types/contracts/mocks/utils/mpc/{Comparison256BitTestsContract.ts → Comparison256TestsContract.ts} +115 -97
  202. package/typechain-types/contracts/mocks/utils/mpc/MinMax128TestsContract.ts +142 -0
  203. package/typechain-types/contracts/mocks/utils/mpc/MinMax256TestsContract.ts +142 -0
  204. package/typechain-types/contracts/mocks/utils/mpc/MinimalImplementation.ts +259 -0
  205. package/typechain-types/contracts/mocks/utils/mpc/MinimalProxy.ts +90 -0
  206. package/typechain-types/contracts/mocks/utils/mpc/MpcOperations128TestContract.ts +388 -0
  207. package/typechain-types/contracts/mocks/utils/mpc/MpcOperationsTestContract.ts +455 -0
  208. package/typechain-types/contracts/mocks/utils/mpc/Mux128TestsContract.ts +109 -0
  209. package/typechain-types/contracts/mocks/utils/mpc/Mux256TestsContract.ts +109 -0
  210. package/typechain-types/contracts/mocks/utils/mpc/OnBoard128TestsContract.ts +166 -0
  211. package/typechain-types/contracts/mocks/utils/mpc/OnBoard256TestsContract.ts +194 -0
  212. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV1.ts +385 -0
  213. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV2.ts +441 -0
  214. package/typechain-types/contracts/mocks/utils/mpc/PrivacyProxy.ts +201 -0
  215. package/typechain-types/contracts/mocks/utils/mpc/Random128TestsContract.ts +131 -0
  216. package/typechain-types/contracts/mocks/utils/mpc/Random256TestsContract.ts +131 -0
  217. package/typechain-types/contracts/mocks/utils/mpc/Shift128TestsContract.ts +142 -0
  218. package/typechain-types/contracts/mocks/utils/mpc/Shift256TestsContract.ts +142 -0
  219. package/typechain-types/contracts/mocks/utils/mpc/Transfer128TestsContract.ts +109 -0
  220. package/typechain-types/contracts/mocks/utils/mpc/Transfer256TestsContract.ts +109 -0
  221. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.ts +128 -0
  222. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.ts +128 -0
  223. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.ts +198 -0
  224. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.ts +182 -0
  225. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.ts +257 -0
  226. package/typechain-types/contracts/mocks/utils/mpc/index.ts +32 -8
  227. package/typechain-types/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock.ts +6 -6
  228. package/typechain-types/contracts/node/CotiNodeRewards.ts +566 -0
  229. package/typechain-types/contracts/node/SoulboundNodeNFT.sol/IERC5192.ts +123 -0
  230. package/typechain-types/contracts/node/SoulboundNodeNFT.sol/SoulboundNodeNFT.ts +975 -0
  231. package/typechain-types/contracts/node/SoulboundNodeNFT.sol/index.ts +5 -0
  232. package/typechain-types/contracts/node/index.ts +6 -0
  233. package/typechain-types/contracts/oracle/CotiPriceConsumer.ts +256 -0
  234. package/typechain-types/contracts/oracle/ICotiPriceConsumer.ts +119 -0
  235. package/typechain-types/contracts/oracle/IStdReference.ts +136 -0
  236. package/typechain-types/contracts/oracle/index.ts +6 -0
  237. package/typechain-types/contracts/privacyBridge/PrivacyBridge.ts +1628 -0
  238. package/typechain-types/contracts/privacyBridge/PrivacyBridgeCotiNative.ts +1870 -0
  239. package/typechain-types/contracts/privacyBridge/PrivacyBridgeERC20.sol/IHasDecimals.ts +84 -0
  240. package/typechain-types/contracts/privacyBridge/PrivacyBridgeERC20.sol/PrivacyBridgeERC20.ts +1892 -0
  241. package/typechain-types/contracts/privacyBridge/PrivacyBridgeERC20.sol/index.ts +5 -0
  242. package/typechain-types/contracts/privacyBridge/PrivacyBridgeUSDCe.ts +1892 -0
  243. package/typechain-types/contracts/privacyBridge/PrivacyBridgeUSDT.ts +1892 -0
  244. package/typechain-types/contracts/privacyBridge/PrivacyBridgeWADA.ts +1892 -0
  245. package/typechain-types/contracts/privacyBridge/PrivacyBridgeWBTC.ts +1892 -0
  246. package/typechain-types/contracts/privacyBridge/PrivacyBridgeWETH.ts +1892 -0
  247. package/typechain-types/contracts/privacyBridge/PrivacyBridgegCoti.ts +1892 -0
  248. package/typechain-types/contracts/privacyBridge/index.ts +13 -0
  249. package/typechain-types/contracts/token/PrivateERC20/IPrivateERC20.ts +507 -70
  250. package/typechain-types/contracts/token/PrivateERC20/ITokenReceiver.ts +100 -0
  251. package/typechain-types/contracts/token/PrivateERC20/ITokenReceiverEncrypted.ts +99 -0
  252. package/typechain-types/contracts/token/PrivateERC20/PrivateERC20.ts +833 -68
  253. package/typechain-types/contracts/token/PrivateERC20/index.ts +4 -0
  254. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateBridgedUSDC.ts +1270 -0
  255. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateCOTI.ts +1270 -0
  256. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateCOTITreasuryGovernanceToken.ts +1270 -0
  257. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateTetherUSD.ts +1270 -0
  258. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateWrappedADA.ts +1270 -0
  259. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateWrappedBTC.ts +1270 -0
  260. package/typechain-types/contracts/token/PrivateERC20/tokens/PrivateWrappedEther.ts +1270 -0
  261. package/typechain-types/contracts/token/PrivateERC20/tokens/index.ts +10 -0
  262. package/typechain-types/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations.ts +111 -10
  263. package/typechain-types/factories/@openzeppelin/contracts/access/AccessControlEnumerable__factory.ts +276 -0
  264. package/typechain-types/factories/@openzeppelin/contracts/access/AccessControl__factory.ts +229 -0
  265. package/typechain-types/factories/@openzeppelin/contracts/access/IAccessControlEnumerable__factory.ts +244 -0
  266. package/typechain-types/factories/@openzeppelin/contracts/access/IAccessControl__factory.ts +197 -0
  267. package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +74 -0
  268. package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +8 -0
  269. package/typechain-types/factories/@openzeppelin/contracts/index.ts +2 -0
  270. package/typechain-types/factories/@openzeppelin/contracts/security/Pausable__factory.ts +61 -0
  271. package/typechain-types/factories/@openzeppelin/contracts/security/index.ts +4 -0
  272. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/ERC20__factory.ts +356 -0
  273. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/IERC20__factory.ts +205 -0
  274. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata__factory.ts +247 -0
  275. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit__factory.ts +100 -0
  276. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/index.ts +5 -0
  277. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/index.ts +6 -0
  278. package/typechain-types/factories/@openzeppelin/contracts/token/ERC721/ERC721__factory.ts +416 -0
  279. package/typechain-types/factories/@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata__factory.ts +355 -0
  280. package/typechain-types/factories/@openzeppelin/contracts/token/ERC721/extensions/index.ts +4 -0
  281. package/typechain-types/factories/@openzeppelin/contracts/token/ERC721/index.ts +2 -0
  282. package/typechain-types/factories/@openzeppelin/contracts/token/index.ts +1 -0
  283. package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperserLeftoverS1__factory.ts +791 -0
  284. package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperserUnlimitedWindow__factory.ts +711 -0
  285. package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperser__factory.ts +759 -0
  286. package/typechain-types/factories/contracts/disperse/coinByRatio/index.ts +6 -0
  287. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20__factory.ts +43 -0
  288. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/IOwnable__factory.ts +35 -0
  289. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser__factory.ts +203 -0
  290. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +6 -0
  291. package/typechain-types/factories/contracts/disperse/disperseToken/TokenDisperser__factory.ts +198 -0
  292. package/typechain-types/factories/contracts/disperse/disperseToken/index.ts +5 -0
  293. package/typechain-types/factories/contracts/disperse/index.ts +5 -0
  294. package/typechain-types/factories/contracts/index.ts +5 -0
  295. package/typechain-types/factories/contracts/messaging/PrivateMessaging__factory.ts +1035 -0
  296. package/typechain-types/factories/contracts/messaging/index.ts +4 -0
  297. package/typechain-types/factories/contracts/mocks/access/DataPrivacyFramework/DataPrivacyFrameworkMock__factory.ts +1 -1
  298. package/typechain-types/factories/contracts/mocks/index.ts +2 -0
  299. package/typechain-types/factories/contracts/mocks/messaging/PrivateMessagingHarness__factory.ts +1161 -0
  300. package/typechain-types/factories/contracts/mocks/messaging/index.ts +4 -0
  301. package/typechain-types/factories/contracts/mocks/oracle/CotiPriceConsumerMock__factory.ts +159 -0
  302. package/typechain-types/factories/contracts/mocks/oracle/index.ts +4 -0
  303. package/typechain-types/factories/contracts/mocks/token/ERC20DecimalsMock__factory.ts +394 -0
  304. package/typechain-types/factories/contracts/mocks/token/ERC20Mock__factory.ts +392 -0
  305. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/EncryptedTokenReceiverMock__factory.ts +125 -0
  306. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivacyBridgeERC20Mock__factory.ts +1797 -0
  307. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20AuditorSupplyMock__factory.ts +1514 -0
  308. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20CappedMock__factory.ts +1466 -0
  309. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20GtCallerMock__factory.ts +165 -0
  310. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20Mock__factory.ts +975 -127
  311. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/IPrivateERC20TransferAndCallEncrypted__factory.ts +54 -0
  312. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/PrivateERC20ReentrantReceiverMock__factory.ts +159 -0
  313. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol/index.ts +5 -0
  314. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PublicReentrantTokenReceiverMock__factory.ts +164 -0
  315. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PublicTokenReceiverBoolMock__factory.ts +130 -0
  316. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PublicTokenReceiverMock__factory.ts +102 -0
  317. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/index.ts +9 -0
  318. package/typechain-types/factories/contracts/mocks/token/PrivateERC721/PrivateERC721URIStorageMock__factory.ts +1 -1
  319. package/typechain-types/factories/contracts/mocks/token/index.ts +2 -0
  320. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128TestsContract__factory.ts +330 -0
  321. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256TestsContract__factory.ts +330 -0
  322. package/typechain-types/factories/contracts/mocks/utils/mpc/ArithmeticTestsContract__factory.ts +1 -1
  323. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128TestsContract__factory.ts +184 -0
  324. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256TestsContract__factory.ts +184 -0
  325. package/typechain-types/factories/contracts/mocks/utils/mpc/BitwiseTestsContract__factory.ts +1 -1
  326. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract__factory.ts +203 -0
  327. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract__factory.ts +203 -0
  328. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmeticWIthOverflowBitTestsContract.sol/CheckedArithmeticWithOverflowBitTestsContract__factory.ts +1 -1
  329. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128TestsContract__factory.ts +295 -0
  330. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison1TestsContract__factory.ts +1 -1
  331. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256TestsContract__factory.ts +295 -0
  332. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison2TestsContract__factory.ts +1 -1
  333. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax128TestsContract__factory.ts +147 -0
  334. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax256TestsContract__factory.ts +147 -0
  335. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMaxTestsContract__factory.ts +1 -1
  336. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalImplementation__factory.ts +263 -0
  337. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalProxy__factory.ts +104 -0
  338. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous1TestsContract__factory.ts +1 -1
  339. package/typechain-types/factories/contracts/mocks/utils/mpc/MiscellaneousTestsContract__factory.ts +1 -1
  340. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperations128TestContract__factory.ts +723 -0
  341. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperationsTestContract__factory.ts +1410 -0
  342. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux128TestsContract__factory.ts +115 -0
  343. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux256TestsContract__factory.ts +115 -0
  344. package/typechain-types/factories/contracts/mocks/utils/mpc/OffboardToUserKeyTestContract__factory.ts +1 -1
  345. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard128TestsContract__factory.ts +175 -0
  346. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard256TestsContract__factory.ts +205 -0
  347. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV1__factory.ts +521 -0
  348. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV2__factory.ts +648 -0
  349. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyProxy__factory.ts +179 -0
  350. package/typechain-types/factories/contracts/mocks/utils/mpc/Random128TestsContract__factory.ts +131 -0
  351. package/typechain-types/factories/contracts/mocks/utils/mpc/Random256TestsContract__factory.ts +131 -0
  352. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift128TestsContract__factory.ts +147 -0
  353. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift256TestsContract__factory.ts +147 -0
  354. package/typechain-types/factories/contracts/mocks/utils/mpc/ShiftTestsContract__factory.ts +1 -1
  355. package/typechain-types/factories/contracts/mocks/utils/mpc/StringTestsContract__factory.ts +1 -1
  356. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer128TestsContract__factory.ts +135 -0
  357. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer256TestsContract__factory.ts +135 -0
  358. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferScalarTestsContract__factory.ts +1 -1
  359. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferTestsContract__factory.ts +1 -1
  360. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract__factory.ts +154 -0
  361. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract__factory.ts +154 -0
  362. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_16TestsContract__factory.ts +1 -1
  363. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_32TestsContract__factory.ts +1 -1
  364. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_64TestsContract__factory.ts +1 -1
  365. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_8TestsContract__factory.ts +1 -1
  366. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceScalarTestsContract__factory.ts +1 -1
  367. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceTestsContract__factory.ts +1 -1
  368. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract__factory.ts +190 -0
  369. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract__factory.ts +210 -0
  370. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract__factory.ts +260 -0
  371. package/typechain-types/factories/contracts/mocks/utils/mpc/index.ts +32 -8
  372. package/typechain-types/factories/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock__factory.ts +10 -28
  373. package/typechain-types/factories/contracts/node/CotiNodeRewards__factory.ts +549 -0
  374. package/typechain-types/factories/contracts/node/SoulboundNodeNFT.sol/IERC5192__factory.ts +54 -0
  375. package/typechain-types/factories/contracts/node/SoulboundNodeNFT.sol/SoulboundNodeNFT__factory.ts +947 -0
  376. package/typechain-types/factories/contracts/node/SoulboundNodeNFT.sol/index.ts +5 -0
  377. package/typechain-types/factories/contracts/node/index.ts +5 -0
  378. package/typechain-types/factories/contracts/onboard/AccountOnboard__factory.ts +1 -1
  379. package/typechain-types/factories/contracts/oracle/CotiPriceConsumer__factory.ts +295 -0
  380. package/typechain-types/factories/contracts/oracle/ICotiPriceConsumer__factory.ts +73 -0
  381. package/typechain-types/factories/contracts/oracle/IStdReference__factory.ts +107 -0
  382. package/typechain-types/factories/contracts/oracle/index.ts +6 -0
  383. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeCotiNative__factory.ts +1676 -0
  384. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeERC20.sol/IHasDecimals__factory.ts +38 -0
  385. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeERC20.sol/PrivacyBridgeERC20__factory.ts +1678 -0
  386. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeERC20.sol/index.ts +5 -0
  387. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeUSDCe__factory.ts +1782 -0
  388. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeUSDT__factory.ts +1782 -0
  389. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeWADA__factory.ts +1782 -0
  390. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeWBTC__factory.ts +1782 -0
  391. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgeWETH__factory.ts +1782 -0
  392. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridge__factory.ts +1367 -0
  393. package/typechain-types/factories/contracts/privacyBridge/PrivacyBridgegCoti__factory.ts +1782 -0
  394. package/typechain-types/factories/contracts/privacyBridge/index.ts +12 -0
  395. package/typechain-types/factories/contracts/token/PrivateERC20/IPrivateERC20__factory.ts +639 -61
  396. package/typechain-types/factories/contracts/token/PrivateERC20/ITokenReceiverEncrypted__factory.ts +53 -0
  397. package/typechain-types/factories/contracts/token/PrivateERC20/ITokenReceiver__factory.ts +54 -0
  398. package/typechain-types/factories/contracts/token/PrivateERC20/PrivateERC20__factory.ts +986 -120
  399. package/typechain-types/factories/contracts/token/PrivateERC20/index.ts +3 -0
  400. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateBridgedUSDC__factory.ts +1445 -0
  401. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateCOTITreasuryGovernanceToken__factory.ts +1451 -0
  402. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateCOTI__factory.ts +1442 -0
  403. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateTetherUSD__factory.ts +1445 -0
  404. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateWrappedADA__factory.ts +1445 -0
  405. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateWrappedBTC__factory.ts +1445 -0
  406. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/PrivateWrappedEther__factory.ts +1451 -0
  407. package/typechain-types/factories/contracts/token/PrivateERC20/tokens/index.ts +10 -0
  408. package/typechain-types/factories/contracts/utils/mpc/MpcCore__factory.ts +1 -1
  409. package/typechain-types/factories/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations__factory.ts +126 -0
  410. package/typechain-types/hardhat.d.ts +1772 -278
  411. package/typechain-types/index.ts +182 -16
  412. package/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract.sol +0 -260
  413. package/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.sol +0 -264
  414. package/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.sol +0 -91
  415. package/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.sol +0 -91
  416. package/contracts/mocks/utils/mpc/Comparison128BitTestsContract.sol +0 -158
  417. package/contracts/mocks/utils/mpc/Comparison256BitTestsContract.sol +0 -158
  418. package/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.sol +0 -185
  419. package/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.sol +0 -190
  420. package/contracts/mocks/utils/mpc/README.md +0 -486
  421. package/test/utils/mpc/Unsigned128BitIntegers.test.ts +0 -945
  422. package/test/utils/mpc/Unsigned256BitIntegers.test.ts +0 -1124
  423. package/test/utils/mpc/helpers.ts +0 -77
  424. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.ts +0 -302
  425. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.ts +0 -322
  426. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract__factory.ts +0 -349
  427. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract__factory.ts +0 -364
  428. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128BitTestsContract__factory.ts +0 -182
  429. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256BitTestsContract__factory.ts +0 -182
  430. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128BitTestsContract__factory.ts +0 -255
  431. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256BitTestsContract__factory.ts +0 -255
  432. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract__factory.ts +0 -332
  433. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract__factory.ts +0 -380
@@ -0,0 +1,432 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.20;
3
+
4
+ import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
5
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
+ import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
8
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
9
+
10
+ /**
11
+ * @title FixedRatioCoinDisperserUnlimitedWindow
12
+ * @notice Redeem ERC20 "points" for native ETH payout pool
13
+ * - Fixed-index redemption set at finalize()
14
+ * - Pull-based withdrawals (DoS safe)
15
+ * - Optional push for EOAs only
16
+ * - Native ETH payouts
17
+ *
18
+ */
19
+ contract FixedRatioCoinDisperserUnlimitedWindow is ReentrancyGuard, Ownable {
20
+ using SafeERC20 for IERC20;
21
+
22
+ // --- Custom Errors ---
23
+ error TokenZeroAddress();
24
+ error OwnerZeroAddress();
25
+ error AlreadyFinalized();
26
+ error PoolZero();
27
+ error SupplyZero();
28
+ error NotFinalized();
29
+ error NoDust();
30
+ error ContractMustPull();
31
+ error NothingToWithdraw();
32
+ error ContractPaused();
33
+ error AmountZero();
34
+ error SnapshotZero();
35
+ error NoPointsLeft();
36
+ error OverCapacity();
37
+ error BalanceManipulation();
38
+ error PayoutZero();
39
+ error InvalidAddress();
40
+ error InsufficientEthBalance();
41
+ error EthTransferFailed();
42
+
43
+ // --- Events ---
44
+ event Funded(address indexed from, uint256 amount);
45
+ event Finalized(uint256 totalPayoutPool, uint256 totalRedeemablePoints, uint256 accPayoutPerPoint);
46
+ event Redeemed(address indexed user, uint256 pointsRequested, uint256 pointsReceived, uint256 payout, bool pushed);
47
+ event Paused(bool isPaused);
48
+ event DustWithdrawn(uint256 amount);
49
+ event Withdrawal(address indexed user, uint256 amount);
50
+
51
+ /// @notice Emitted when the payout index is updated via external source sync.
52
+ event AccPayoutPerPointUpdated(uint256 previousAccPayoutPerPoint, uint256 newAccPayoutPerPoint);
53
+
54
+ // --- Config ---
55
+ IERC20 public immutable POINTS_TOKEN;
56
+
57
+ // --- Lifecycle ---
58
+ bool public finalized;
59
+ bool public paused;
60
+
61
+ // --- Accounting ---
62
+ uint256 public totalPayoutPool; // ETH reserved for redemptions (snapshot at finalize)
63
+ uint256 public totalRedeemablePoints; // points supply snapshot at finalize
64
+ uint256 public accPayoutPerPoint; // index = totalPayoutPool * 1e18 / totalRedeemablePoints
65
+ uint256 public totalPointsRedeemed; // cumulative points locked
66
+ uint256 public totalPayoutSent; // cumulative ETH sent
67
+
68
+ // --- Pull ledger ---
69
+ mapping(address => uint256) public pendingWithdrawals;
70
+ uint256 public pendingTotal;
71
+
72
+ uint256 private constant ONE = 1e18;
73
+
74
+ /**
75
+ * @dev Constructor
76
+ * @param _pointsToken Address of the ERC20 points token that can be redeemed
77
+ * @param _owner Address that will have admin privileges (can pause, finalize, etc.)
78
+ */
79
+ constructor(address _pointsToken, address _owner) {
80
+ if (_pointsToken == address(0)) revert TokenZeroAddress();
81
+ if (_owner == address(0)) revert OwnerZeroAddress();
82
+
83
+ POINTS_TOKEN = IERC20(_pointsToken);
84
+ _transferOwnership(_owner);
85
+ }
86
+
87
+ // --- Admin ---
88
+
89
+ /**
90
+ * @dev Pause or unpause the redemption system
91
+ * @notice Only the owner can call this function
92
+ * @notice When paused, users cannot redeem points but can still withdraw pending amounts
93
+ */
94
+ function setPaused(bool _paused) external onlyOwner {
95
+ paused = _paused;
96
+ emit Paused(_paused);
97
+ }
98
+
99
+ /**
100
+ * @dev Fund the contract with ETH for redemptions
101
+ * @notice Only the owner can call this function
102
+ * @notice The ETH sent will be used to pay out redemptions
103
+ * @notice Can be called multiple times to add more ETH
104
+ * @notice Cannot be called after finalization.
105
+ */
106
+ function fundEth() external payable onlyOwner {
107
+ if (finalized) revert AlreadyFinalized();
108
+ if (msg.value == 0) revert AmountZero();
109
+
110
+ totalPayoutPool += msg.value;
111
+ emit Funded(msg.sender, msg.value);
112
+ }
113
+
114
+ /**
115
+ * @dev Move existing contract balance to the payout pool (pre-finalize only)
116
+ * @notice Only owner can call this function
117
+ * @notice Prevents double-counting by only adding (balance - totalPayoutPool) if positive
118
+ */
119
+ function moveBalanceToPool() external onlyOwner {
120
+ if (finalized) revert AlreadyFinalized();
121
+
122
+ uint256 balance = address(this).balance;
123
+ if (balance == 0) revert PoolZero();
124
+
125
+ uint256 currentPool = totalPayoutPool;
126
+ if (balance > currentPool) {
127
+ uint256 difference = balance - currentPool;
128
+ totalPayoutPool += difference;
129
+ emit Funded(msg.sender, difference);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * @dev Finalize the redemption system and calculate payout rates
135
+ * @notice Only the owner can call this function
136
+ * @notice Can only be called once
137
+ * @notice Snapshot the current ETH balance and points token supply
138
+ * @notice Calculate the payout rate per point (ETH per point)
139
+ * @notice After finalization, redemptions can begin (window no longer enforced)
140
+ */
141
+ function finalize() external onlyOwner {
142
+ if (finalized) revert AlreadyFinalized();
143
+
144
+ uint256 pool = address(this).balance;
145
+ uint256 supply = POINTS_TOKEN.totalSupply();
146
+ if (pool == 0) revert PoolZero();
147
+ if (supply == 0) revert SupplyZero();
148
+
149
+ uint256 index = Math.mulDiv(pool, ONE, supply);
150
+
151
+ totalPayoutPool = pool;
152
+ totalRedeemablePoints = supply;
153
+ accPayoutPerPoint = index;
154
+ finalized = true;
155
+
156
+ emit Finalized(pool, supply, index);
157
+ }
158
+
159
+ // --- Owner utilities ---
160
+
161
+ /**
162
+ * @dev Withdraw excess ETH that is not needed for redemptions
163
+ * @notice Only the owner can call this function
164
+ * @notice Can only be called after finalization and redemption window closes
165
+ * @notice Withdraws ETH in excess of what's needed for pending withdrawals
166
+ * @notice ETH is sent to the owner address
167
+ *
168
+ * @dev NOTE: Redemption window is no longer enforced; this can be called any time after finalization.
169
+ */
170
+ function withdrawDust() external onlyOwner nonReentrant {
171
+ if (!finalized) revert NotFinalized();
172
+ // NOTE: redemption window no longer enforced
173
+
174
+ uint256 requiredReserve = pendingTotal;
175
+ uint256 bal = address(this).balance;
176
+ if (bal <= requiredReserve) revert NoDust();
177
+
178
+ uint256 amount = bal - requiredReserve;
179
+ (bool success,) = owner().call{value: amount}("");
180
+ if (!success) revert EthTransferFailed();
181
+
182
+ emit DustWithdrawn(amount);
183
+ }
184
+
185
+ // --- Redeem API ---
186
+
187
+ /**
188
+ * @dev Redeem points for ETH using pull pattern (user must call withdraw later)
189
+ * @param amount Amount of points to redeem
190
+ * @notice User must have approved the contract to spend their points
191
+ * @notice ETH payout is added to user's pending withdrawals
192
+ * @notice User must call withdraw() later to receive the ETH
193
+ * @notice Can only be called when finalized and not paused (window no longer enforced)
194
+ */
195
+ function redeemPull(uint256 amount) external nonReentrant {
196
+ (uint256 payout, uint256 received) = _redeemCore(msg.sender, amount);
197
+ pendingWithdrawals[msg.sender] += payout;
198
+ pendingTotal += payout;
199
+
200
+ emit Redeemed(msg.sender, amount, received, payout, false);
201
+ }
202
+
203
+ /**
204
+ * @dev Redeem points for ETH using push pattern (immediate ETH transfer)
205
+ * @param amount Amount of points to redeem
206
+ * @notice User must have approved the contract to spend their points
207
+ * @notice ETH is sent directly to the user's address
208
+ * @notice Only works for EOA (externally owned accounts), not contracts
209
+ * @notice Can only be called when finalized and not paused (window no longer enforced)
210
+ */
211
+ function redeemPush(uint256 amount) external nonReentrant {
212
+ if (!_isEoa(msg.sender)) revert ContractMustPull();
213
+
214
+ (uint256 payout, uint256 received) = _redeemCore(msg.sender, amount);
215
+
216
+ (bool success,) = msg.sender.call{value: payout}("");
217
+ if (!success) revert EthTransferFailed();
218
+
219
+ emit Redeemed(msg.sender, amount, received, payout, true);
220
+ }
221
+
222
+ /**
223
+ * @dev Withdraw pending ETH from pull redemptions
224
+ * @notice Withdraws all pending ETH for the caller
225
+ * @notice ETH is sent directly to the caller's address
226
+ * @notice Can be called anytime after a pull redemption
227
+ * @notice Sets caller's pending balance to zero after withdrawal
228
+ */
229
+ function withdraw() external nonReentrant {
230
+ uint256 amount = pendingWithdrawals[msg.sender];
231
+ if (amount == 0) revert NothingToWithdraw();
232
+
233
+ pendingWithdrawals[msg.sender] = 0;
234
+ pendingTotal -= amount;
235
+
236
+ (bool success,) = msg.sender.call{value: amount}("");
237
+ if (!success) revert EthTransferFailed();
238
+
239
+ emit Withdrawal(msg.sender, amount);
240
+ }
241
+
242
+ // --- Core math ---
243
+
244
+ /**
245
+ * @dev Internal function that handles the core redemption logic
246
+ * @param user Address of the user redeeming points
247
+ * @param amount Amount of points to redeem
248
+ * @return payout Amount of ETH to be paid out
249
+ * @return received Actual amount of points received (transferred to contract)
250
+ * @notice Handles all validation, point transfer, and payout calculation
251
+ * @notice Points are transferred from user to contract and locked forever
252
+ * @notice Updates global accounting (totalPointsRedeemed, totalPayoutSent)
253
+ */
254
+ function _redeemCore(address user, uint256 amount)
255
+ internal
256
+ returns (uint256 payout, uint256 received)
257
+ {
258
+ if (!finalized) revert NotFinalized();
259
+ if (paused) revert ContractPaused();
260
+ if (amount == 0) revert AmountZero();
261
+
262
+ uint256 trp = totalRedeemablePoints;
263
+ uint256 tpr = totalPointsRedeemed;
264
+ if (trp == 0) revert SnapshotZero();
265
+
266
+ uint256 remaining = trp - tpr;
267
+ if (remaining == 0) revert NoPointsLeft();
268
+
269
+ // Pull points token (requires allowance from user)
270
+ uint256 beforeBal = POINTS_TOKEN.balanceOf(address(this));
271
+ POINTS_TOKEN.safeTransferFrom(user, address(this), amount);
272
+ uint256 afterBal = POINTS_TOKEN.balanceOf(address(this));
273
+ received = afterBal - beforeBal;
274
+
275
+ if (received == 0) revert PayoutZero();
276
+ if (received > amount) revert BalanceManipulation();
277
+ if (received > remaining) revert OverCapacity();
278
+
279
+ payout = Math.mulDiv(received, accPayoutPerPoint, ONE);
280
+
281
+ // Cap payout by safe reserve / balance
282
+ uint256 reserve = _safeRemainingReserve();
283
+ if (payout > reserve) payout = reserve;
284
+ if (payout == 0) revert PayoutZero();
285
+
286
+ // Check if contract has enough ETH (defensive; reserve already caps by balance)
287
+ if (payout > address(this).balance) revert InsufficientEthBalance();
288
+
289
+ unchecked {
290
+ totalPointsRedeemed = tpr + received;
291
+ totalPayoutSent += payout;
292
+ }
293
+ }
294
+
295
+ // --- Views ---
296
+
297
+ /**
298
+ * @dev Check if redemptions are currently open
299
+ * @return True if redemptions are open (finalized and not paused)
300
+ */
301
+ function isRedemptionOpen() public view returns (bool) {
302
+ return finalized && !paused;
303
+ }
304
+
305
+ /**
306
+ * @dev Get the remaining ETH reserve available for redemptions
307
+ * @return Amount of ETH still available for redemptions
308
+ * @notice Returns 0 if not finalized or if all ETH has been paid out
309
+ */
310
+ function remainingReserve() public view returns (uint256) {
311
+ if (!finalized) return 0;
312
+ if (totalPayoutSent >= totalPayoutPool) return 0;
313
+ return totalPayoutPool - totalPayoutSent;
314
+ }
315
+
316
+ /**
317
+ * @dev Internal function to get the safe remaining reserve
318
+ * @return The minimum of theoretical reserve and actual ETH balance
319
+ * @notice Ensures we don't try to pay out more ETH than we actually have
320
+ */
321
+ function _safeRemainingReserve() internal view returns (uint256) {
322
+ if (!finalized) return 0;
323
+ uint256 theoreticalReserve =
324
+ totalPayoutSent >= totalPayoutPool ? 0 : (totalPayoutPool - totalPayoutSent);
325
+ uint256 actualBalance = address(this).balance;
326
+ return theoreticalReserve > actualBalance ? actualBalance : theoreticalReserve;
327
+ }
328
+
329
+ /**
330
+ * @dev Preview the ETH payout for a given amount of points
331
+ * @param amount Amount of points to redeem
332
+ * @return Expected ETH payout amount
333
+ * @notice This is an estimate and actual payout may be lower due to reserve limits
334
+ * @notice Returns 0 if not finalized, no points left, or amount exceeds remaining points
335
+ */
336
+ function previewPayout(uint256 amount) external view returns (uint256) {
337
+ if (!finalized || amount == 0) return 0;
338
+
339
+ uint256 trp = totalRedeemablePoints;
340
+ if (trp == 0) return 0;
341
+
342
+ uint256 tpr = totalPointsRedeemed;
343
+ if (tpr >= trp) return 0;
344
+
345
+ uint256 remaining = trp - tpr;
346
+ if (amount > remaining) return 0;
347
+
348
+ uint256 est = Math.mulDiv(amount, accPayoutPerPoint, ONE);
349
+ uint256 reserve = remainingReserve();
350
+ return est > reserve ? reserve : est;
351
+ }
352
+
353
+ // --- Governance / Admin override ---
354
+
355
+ /**
356
+ * @dev Owner can directly update the payout rate per point.
357
+ * @param newAccPayoutPerPoint New payout index (ETH per point, scaled by 1e18)
358
+ * @notice Only callable after finalization.
359
+ * @notice Does NOT retroactively adjust past payouts; only affects future redemptions.
360
+ */
361
+ function setAccPayoutPerPoint(uint256 newAccPayoutPerPoint) external onlyOwner {
362
+ if (!finalized) revert NotFinalized();
363
+ if (newAccPayoutPerPoint == 0) revert PayoutZero();
364
+
365
+ uint256 prev = accPayoutPerPoint;
366
+ accPayoutPerPoint = newAccPayoutPerPoint;
367
+
368
+ emit AccPayoutPerPointUpdated(prev, newAccPayoutPerPoint);
369
+ }
370
+
371
+ // Rescue (window no longer enforced)
372
+
373
+ /**
374
+ * @dev Rescue ERC20 tokens sent to the contract after redemption window closes
375
+ * @param tokenAddr Address of the ERC20 token to rescue
376
+ * @param to Address to send the tokens to
377
+ * @param amount Amount of tokens to rescue
378
+ * @notice Only the owner can call this function
379
+ * @notice Can only be called after finalization and redemption window closes
380
+ * @notice Used to recover accidentally sent tokens
381
+ *
382
+ * @dev NOTE: Redemption window is no longer enforced; this can be called any time after finalization.
383
+ */
384
+ function rescueTokens(address tokenAddr, address to, uint256 amount) external onlyOwner {
385
+ if (!finalized) revert NotFinalized();
386
+ if (to == address(0)) revert InvalidAddress();
387
+ if (amount == 0) revert AmountZero();
388
+ IERC20(tokenAddr).safeTransfer(to, amount);
389
+ }
390
+
391
+ /**
392
+ * @dev Rescue ETH sent to the contract after redemption window closes
393
+ * @param to Address to send the ETH to
394
+ * @param amount Amount of ETH to rescue
395
+ * @notice Only the owner can call this function
396
+ * @notice Can only be called after finalization and redemption window closes
397
+ * @notice Used to recover accidentally sent ETH
398
+ *
399
+ * @dev NOTE: Redemption window is no longer enforced; this can be called any time after finalization.
400
+ */
401
+ function rescueEth(address to, uint256 amount) external onlyOwner {
402
+ if (!finalized) revert NotFinalized();
403
+ if (to == address(0)) revert InvalidAddress();
404
+ if (amount == 0) revert AmountZero();
405
+ if (amount > address(this).balance) revert InsufficientEthBalance();
406
+
407
+ (bool success,) = to.call{value: amount}("");
408
+ if (!success) revert EthTransferFailed();
409
+ }
410
+
411
+ // --- Utils ---
412
+
413
+ /**
414
+ * @dev Check if an address is an externally owned account (EOA)
415
+ * @param a Address to check
416
+ * @return True if the address is an EOA (no code), false if it's a contract
417
+ * @notice Used to determine if push redemptions are allowed
418
+ */
419
+ function _isEoa(address a) internal view returns (bool) {
420
+ return a.code.length == 0;
421
+ }
422
+
423
+ /**
424
+ * @dev Allow the contract to receive ETH from any sender.
425
+ * @notice This is triggered automatically when ETH is sent directly (no data).
426
+ * @notice The ETH is NOT automatically added to the redemption pool.
427
+ * @notice Use fundEth() or moveBalanceToPool() to add ETH to the pool.
428
+ */
429
+ receive() external payable {
430
+ if (msg.value == 0) return; // ignore zero-value sends to save gas
431
+ }
432
+ }
@@ -0,0 +1,120 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity 0.8.20;
3
+
4
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
5
+
6
+ interface IMintableERC20 {
7
+ function mint(address to, uint256 amount) external;
8
+ }
9
+
10
+ interface IOwnable {
11
+ function transferOwnership(address newOwner) external;
12
+ }
13
+
14
+ /**
15
+ * @title MintDisperser
16
+ * @notice Owner-controlled utility contract for minting ERC20 tokens to multiple recipients in a single transaction
17
+ * @dev The target token must expose a mint(address,uint256) function and the
18
+ * MintDisperser contract must have permission to mint (e.g., MINTER_ROLE/owner).
19
+ * @dev Supports two distribution methods:
20
+ * - mintSameAmount: Mints the same amount to all recipients
21
+ * - mint: Mints different amounts to each recipient
22
+ */
23
+ contract MintDisperser is Ownable {
24
+ /**
25
+ * @notice Initialize the MintDisperser contract
26
+ * @param initialOwner Address that will have owner privileges to execute minting operations
27
+ * @dev Sets the initial owner of the contract using OpenZeppelin's Ownable pattern
28
+ */
29
+ constructor(address initialOwner) {
30
+ _transferOwnership(initialOwner);
31
+ }
32
+
33
+ /**
34
+ * @notice Mint the same amount of tokens to multiple recipients
35
+ * @param token Address of the ERC20 token contract that supports minting
36
+ * @param recipients Array of recipient addresses that will receive tokens
37
+ * @param amountPerRecipient Amount of tokens to mint to each recipient
38
+ * @dev Only the contract owner can call this function
39
+ * @dev Reverts if:
40
+ * - token is the zero address
41
+ * - amountPerRecipient is zero
42
+ * - recipients array is empty
43
+ * - any recipient address is the zero address
44
+ * @dev The token contract must allow this contract to mint tokens (typically by being the owner or having MINTER_ROLE)
45
+ * @dev All recipients receive the same amount of tokens
46
+ */
47
+ function mintSameAmount(
48
+ address token,
49
+ address[] calldata recipients,
50
+ uint256 amountPerRecipient
51
+ ) external onlyOwner {
52
+ require(token != address(0), "token=0");
53
+ require(amountPerRecipient > 0, "amount=0");
54
+ uint256 len = recipients.length;
55
+ require(len > 0, "no recipients");
56
+
57
+ for (uint256 i = 0; i < len; ++i) {
58
+ address to = recipients[i];
59
+ require(to != address(0), "recipient=0");
60
+ IMintableERC20(token).mint(to, amountPerRecipient);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * @notice Mint different amounts of tokens to multiple recipients
66
+ * @param token Address of the ERC20 token contract that supports minting
67
+ * @param recipients Array of recipient addresses that will receive tokens
68
+ * @param amounts Array of token amounts to mint to each recipient (must match recipients array length)
69
+ * @dev Only the contract owner can call this function
70
+ * @dev Reverts if:
71
+ * - token is the zero address
72
+ * - recipients array is empty
73
+ * - recipients and amounts arrays have different lengths
74
+ * - any recipient address is the zero address
75
+ * - any amount is zero
76
+ * @dev The token contract must allow this contract to mint tokens (typically by being the owner or having MINTER_ROLE)
77
+ * @dev Each recipient receives the corresponding amount from the amounts array at the same index
78
+ */
79
+ function mint(
80
+ address token,
81
+ address[] calldata recipients,
82
+ uint256[] calldata amounts
83
+ ) external onlyOwner {
84
+ require(token != address(0), "token=0");
85
+ uint256 len = recipients.length;
86
+ require(len > 0, "no recipients");
87
+ require(len == amounts.length, "length mismatch");
88
+
89
+ for (uint256 i = 0; i < len; ++i) {
90
+ address to = recipients[i];
91
+ uint256 amt = amounts[i];
92
+ require(to != address(0), "recipient=0");
93
+ require(amt > 0, "amount=0");
94
+ IMintableERC20(token).mint(to, amt);
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @notice Transfers ownership of an Ownable ERC20 token from this contract to a new owner
100
+ * @param token Address of the Ownable token contract whose ownership will be transferred
101
+ * @param newOwner Address that will become the new owner of the token contract
102
+ * @dev Only the contract owner can call this function
103
+ * @dev Reverts if:
104
+ * - token is the zero address
105
+ * - newOwner is the zero address
106
+ * - this contract is not the current owner of the token contract
107
+ * @dev This function is useful for transferring token ownership after the MintDisperser has been granted ownership
108
+ * to enable minting operations. After minting is complete, ownership can be transferred back or to another address.
109
+ * @dev The token contract must implement the IOwnable interface with a transferOwnership function
110
+ */
111
+ function transferTokenOwnership(
112
+ address token,
113
+ address newOwner
114
+ ) external onlyOwner {
115
+ require(token != address(0), "token=0");
116
+ require(newOwner != address(0), "newOwner=0");
117
+
118
+ IOwnable(token).transferOwnership(newOwner);
119
+ }
120
+ }
@@ -0,0 +1,139 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ pragma solidity 0.8.19;
4
+
5
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
6
+ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
7
+
8
+ /**
9
+ * @title TokenDisperser
10
+ * @notice Owner-controlled utility contract for distributing ERC20 tokens to multiple recipients
11
+ * @dev Supports two distribution methods:
12
+ * - disperseFunds: Pulls tokens from a sender's balance (requires approval)
13
+ * - disperseFundsFromContract: Distributes from this contract's own token balance
14
+ * @dev Tracks distributions by ID to prevent duplicate payouts to same recipient
15
+ * @dev Uses OpenZeppelin's SafeERC20 for secure token transfers
16
+ */
17
+ contract TokenDisperser {
18
+ using SafeERC20 for IERC20;
19
+
20
+ /// @notice Contract owner with permissions to execute distributions
21
+ address public immutable owner;
22
+
23
+ /// @notice ERC20 token that will be distributed
24
+ IERC20 public immutable token;
25
+
26
+ /// @notice Mapping: distributionId => recipient => amount received
27
+ /// @dev Used to prevent duplicate distributions to same recipient per distributionId
28
+ mapping(uint256 => mapping(address => uint256)) private _fundsReceived;
29
+
30
+ /**
31
+ * @notice Reverts if caller is not the owner
32
+ */
33
+ modifier onlyOwner {
34
+ require(msg.sender == owner, "Caller is not owner");
35
+ _;
36
+ }
37
+
38
+ /**
39
+ * @notice Initialize the TokenDisperser contract
40
+ * @param token_ Address of the ERC20 token to distribute
41
+ * @dev Sets deployer as owner and stores the token address
42
+ */
43
+ constructor(address token_) {
44
+ owner = msg.sender;
45
+ token = IERC20(token_);
46
+ }
47
+
48
+ /**
49
+ * @notice Query how much each recipient received in a distribution
50
+ * @param distributionId The distribution ID to query
51
+ * @param recipients Array of recipient addresses to check
52
+ * @return Array of amounts received (same order as recipients)
53
+ * @dev Returns 0 for addresses that haven't received funds in this distribution
54
+ */
55
+ function fundsReceived(uint256 distributionId, address[] calldata recipients) external view returns (uint256[] memory) {
56
+ uint256[] memory fundsReceived_ = new uint256[](recipients.length);
57
+
58
+ for (uint256 i = 0; i < recipients.length; ++i) {
59
+ fundsReceived_[i] = _fundsReceived[distributionId][recipients[i]];
60
+ }
61
+
62
+ return fundsReceived_;
63
+ }
64
+
65
+ /**
66
+ * @notice Distribute tokens from a sender's balance to multiple recipients
67
+ * @param distributionId Unique ID for this distribution batch
68
+ * @param totalAmount Sum of all amounts (used for validation)
69
+ * @param sender Address that has tokens and approved this contract to spend them
70
+ * @param recipients Array of recipient addresses
71
+ * @param amounts Array of token amounts (must match recipients length)
72
+ * @dev Only owner can call this function
73
+ * @dev Requires sender to have approved this contract
74
+ * @dev Prevents duplicate distributions to same recipient within same distributionId
75
+ * @dev Reverts if amounts don't sum to totalAmount
76
+ * @dev Uses safeTransferFrom to pull tokens from sender
77
+ */
78
+ function disperseFunds(
79
+ uint256 distributionId,
80
+ uint256 totalAmount,
81
+ address sender,
82
+ address[] calldata recipients,
83
+ uint256[] calldata amounts
84
+ )
85
+ external
86
+ onlyOwner
87
+ {
88
+ uint256 currentAmount = 0;
89
+
90
+ for (uint256 i = 0; i < recipients.length; ++i) {
91
+ require(_fundsReceived[distributionId][recipients[i]] == 0, "Duplicate recipient");
92
+
93
+ token.safeTransferFrom(sender, recipients[i], amounts[i]);
94
+
95
+ currentAmount += amounts[i];
96
+
97
+ _fundsReceived[distributionId][recipients[i]] = amounts[i];
98
+ }
99
+
100
+ require(currentAmount == totalAmount, "Incorrect amount");
101
+ }
102
+
103
+ /**
104
+ * @notice Distribute tokens from this contract's own balance to multiple recipients
105
+ * @param distributionId Unique ID for this distribution batch
106
+ * @param totalAmount Sum of all amounts (used for validation)
107
+ * @param recipients Array of recipient addresses
108
+ * @param amounts Array of token amounts (must match recipients length)
109
+ * @dev Only owner can call this function
110
+ * @dev Requires contract to have sufficient token balance
111
+ * @dev Prevents duplicate distributions to same recipient within same distributionId
112
+ * @dev Reverts if amounts don't sum to totalAmount or contract lacks balance
113
+ * @dev Uses safeTransfer to send from contract's own balance
114
+ */
115
+ function disperseFundsFromContract(
116
+ uint256 distributionId,
117
+ uint256 totalAmount,
118
+ address[] calldata recipients,
119
+ uint256[] calldata amounts
120
+ )
121
+ external
122
+ onlyOwner
123
+ {
124
+ uint256 currentAmount = 0;
125
+
126
+ for (uint256 i = 0; i < recipients.length; ++i) {
127
+ require(_fundsReceived[distributionId][recipients[i]] == 0, "Duplicate recipient");
128
+
129
+ // Transfer from this contract's own balance directly
130
+ token.safeTransfer(recipients[i], amounts[i]);
131
+
132
+ currentAmount += amounts[i];
133
+
134
+ _fundsReceived[distributionId][recipients[i]] = amounts[i];
135
+ }
136
+
137
+ require(currentAmount == totalAmount, "Incorrect amount");
138
+ }
139
+ }