@coti-io/coti-contracts 1.1.0 → 1.2.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 (265) hide show
  1. package/.github/workflows/npm-publish.yml +18 -14
  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/mocks/token/ERC20Mock.sol +19 -0
  9. package/contracts/mocks/token/PrivateERC20/EncryptedTokenReceiverMock.sol +18 -0
  10. package/contracts/mocks/token/PrivateERC20/PrivacyBridgeERC20Mock.sol +13 -0
  11. package/contracts/mocks/token/PrivateERC20/PrivateERC20AuditorSupplyMock.sol +44 -0
  12. package/contracts/mocks/token/PrivateERC20/PrivateERC20CappedMock.sol +23 -0
  13. package/contracts/mocks/token/PrivateERC20/PrivateERC20GtCallerMock.sol +29 -0
  14. package/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.sol +9 -4
  15. package/contracts/mocks/token/PrivateERC20/PrivateERC20ReentrantReceiverMock.sol +39 -0
  16. package/contracts/mocks/token/PrivateERC20/PublicReentrantTokenReceiverMock.sol +29 -0
  17. package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverBoolMock.sol +18 -0
  18. package/contracts/mocks/token/PrivateERC20/PublicTokenReceiverMock.sol +12 -0
  19. package/contracts/mocks/utils/mpc/Arithmetic128TestsContract.sol +123 -0
  20. package/contracts/mocks/utils/mpc/Arithmetic256TestsContract.sol +122 -0
  21. package/contracts/mocks/utils/mpc/Bitwise128TestsContract.sol +65 -0
  22. package/contracts/mocks/utils/mpc/Bitwise256TestsContract.sol +65 -0
  23. package/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.sol +64 -0
  24. package/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.sol +64 -0
  25. package/contracts/mocks/utils/mpc/Comparison128TestsContract.sol +98 -0
  26. package/contracts/mocks/utils/mpc/Comparison256TestsContract.sol +98 -0
  27. package/contracts/mocks/utils/mpc/MinMax128TestsContract.sol +44 -0
  28. package/contracts/mocks/utils/mpc/MinMax256TestsContract.sol +44 -0
  29. package/contracts/mocks/utils/mpc/MinimalImplementation.sol +55 -0
  30. package/contracts/mocks/utils/mpc/MinimalProxy.sol +27 -0
  31. package/contracts/mocks/utils/mpc/MpcOperations128TestContract.sol +267 -0
  32. package/contracts/mocks/utils/mpc/MpcOperationsTestContract.sol +334 -0
  33. package/contracts/mocks/utils/mpc/Mux128TestsContract.sol +24 -0
  34. package/contracts/mocks/utils/mpc/Mux256TestsContract.sol +24 -0
  35. package/contracts/mocks/utils/mpc/OnBoard128TestsContract.sol +93 -0
  36. package/contracts/mocks/utils/mpc/OnBoard256TestsContract.sol +116 -0
  37. package/contracts/mocks/utils/mpc/PrivacyImplementationV1.sol +157 -0
  38. package/contracts/mocks/utils/mpc/PrivacyImplementationV2.sol +133 -0
  39. package/contracts/mocks/utils/mpc/PrivacyProxy.sol +137 -0
  40. package/contracts/mocks/utils/mpc/Random128TestsContract.sol +32 -0
  41. package/contracts/mocks/utils/mpc/Random256TestsContract.sol +32 -0
  42. package/contracts/mocks/utils/mpc/Shift128TestsContract.sol +36 -0
  43. package/contracts/mocks/utils/mpc/Shift256TestsContract.sol +36 -0
  44. package/contracts/mocks/utils/mpc/Transfer128TestsContract.sol +29 -0
  45. package/contracts/mocks/utils/mpc/Transfer256TestsContract.sol +29 -0
  46. package/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.sol +32 -0
  47. package/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.sol +32 -0
  48. package/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.sol +59 -0
  49. package/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.sol +63 -0
  50. package/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.sol +99 -0
  51. package/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock.sol +7 -6
  52. package/contracts/node/CotiNodeRewards.sol +199 -0
  53. package/contracts/node/SoulboundNodeNFT.sol +519 -0
  54. package/contracts/privacyBridge/PrivacyBridge.sol +318 -0
  55. package/contracts/privacyBridge/PrivacyBridgeCotiNative.sol +268 -0
  56. package/contracts/privacyBridge/PrivacyBridgeERC20.sol +251 -0
  57. package/contracts/privacyBridge/PrivacyBridgeUSDCe.sol +16 -0
  58. package/contracts/privacyBridge/PrivacyBridgeUSDT.sol +17 -0
  59. package/contracts/privacyBridge/PrivacyBridgeWADA.sol +17 -0
  60. package/contracts/privacyBridge/PrivacyBridgeWBTC.sol +17 -0
  61. package/contracts/privacyBridge/PrivacyBridgeWETH.sol +17 -0
  62. package/contracts/privacyBridge/PrivacyBridgegCoti.sol +17 -0
  63. package/contracts/token/PrivateERC20/IPrivateERC20.sol +273 -40
  64. package/contracts/token/PrivateERC20/ITokenReceiver.sol +17 -0
  65. package/contracts/token/PrivateERC20/ITokenReceiverEncrypted.sol +20 -0
  66. package/contracts/token/PrivateERC20/PrivateERC20.sol +861 -152
  67. package/contracts/token/PrivateERC20/tokens/PrivateBridgedUSDC.sol +17 -0
  68. package/contracts/token/PrivateERC20/tokens/PrivateCOTI.sol +13 -0
  69. package/contracts/token/PrivateERC20/tokens/PrivateCOTITreasuryGovernanceToken.sol +17 -0
  70. package/contracts/token/PrivateERC20/tokens/PrivateTetherUSD.sol +17 -0
  71. package/contracts/token/PrivateERC20/tokens/PrivateWrappedADA.sol +17 -0
  72. package/contracts/token/PrivateERC20/tokens/PrivateWrappedBTC.sol +17 -0
  73. package/contracts/token/PrivateERC20/tokens/PrivateWrappedEther.sol +13 -0
  74. package/contracts/utils/mpc/MpcCore.sol +10780 -4962
  75. package/contracts/utils/mpc/MpcInterface.sol +7 -2
  76. package/hardhat/gasPriceBump.ts +60 -0
  77. package/hardhat.config.ts +60 -12
  78. package/package.json +21 -3
  79. package/scripts/deploySoulboundNodeNFT.ts +57 -0
  80. package/test/token/PrivateERC20/PrivacyBridge.fees.test.ts +327 -0
  81. package/test/token/PrivateERC20/PrivateERC20.test.ts +1109 -268
  82. package/test/utils/mpc/Bitwise128.test.ts +92 -0
  83. package/test/utils/mpc/Bitwise256.test.ts +101 -0
  84. package/test/utils/mpc/BuildInputText128.test.ts +398 -0
  85. package/test/utils/mpc/CheckedArithmetic128.test.ts +45 -0
  86. package/test/utils/mpc/CheckedArithmetic256.test.ts +45 -0
  87. package/test/utils/mpc/Comparison128.test.ts +48 -0
  88. package/test/utils/mpc/Comparison256.test.ts +48 -0
  89. package/test/utils/mpc/InvalidCiphertext.test.ts +179 -0
  90. package/test/utils/mpc/InvalidCiphertext128.test.ts +179 -0
  91. package/test/utils/mpc/InvalidCiphertext256.test.ts +201 -0
  92. package/test/utils/mpc/InvalidTransfer128.test.ts +166 -0
  93. package/test/utils/mpc/InvalidTransfer256.test.ts +158 -0
  94. package/test/utils/mpc/MinMax128.test.ts +44 -0
  95. package/test/utils/mpc/MinMax256.test.ts +44 -0
  96. package/test/utils/mpc/MinimalProxyDemo.test.ts +280 -0
  97. package/test/utils/mpc/MpcOperations.test.ts +589 -0
  98. package/test/utils/mpc/MpcOperations128.test.ts +632 -0
  99. package/test/utils/mpc/Mux128.test.ts +41 -0
  100. package/test/utils/mpc/Mux256.test.ts +41 -0
  101. package/test/utils/mpc/NegativeCases.test.ts +478 -0
  102. package/test/utils/mpc/NegativeCases128.test.ts +237 -0
  103. package/test/utils/mpc/NegativeCases256.test.ts +236 -0
  104. package/test/utils/mpc/OnBoard128.test.ts +90 -0
  105. package/test/utils/mpc/OnBoard256.test.ts +114 -0
  106. package/test/utils/mpc/Precompile128.test.ts +51 -0
  107. package/test/utils/mpc/Precompile256.test.ts +115 -0
  108. package/test/utils/mpc/ProxyValidation.test.ts +199 -0
  109. package/test/utils/mpc/Random128.test.ts +47 -0
  110. package/test/utils/mpc/Random256.test.ts +47 -0
  111. package/test/utils/mpc/Shift128.test.ts +41 -0
  112. package/test/utils/mpc/Shift256.test.ts +41 -0
  113. package/test/utils/mpc/Transfer128.test.ts +46 -0
  114. package/test/utils/mpc/Transfer256.test.ts +46 -0
  115. package/test/utils/mpc/TransferWithAllowance128.test.ts +48 -0
  116. package/test/utils/mpc/TransferWithAllowance256.test.ts +48 -0
  117. package/test/utils/mpc/UpgradeablePrivacyProxy.test.ts +307 -0
  118. package/test/utils/mpc/ValidateCiphertext.test.ts +221 -0
  119. package/test/utils/mpc/ValidateCiphertext256.test.ts +161 -0
  120. package/test/utils/mpc/ValidateCiphertext256Decryption.test.ts +383 -0
  121. package/test/utils/mpc/WalletEncryptionFailures.test.ts +494 -0
  122. package/test/utils/privateErc20Helpers.ts +97 -0
  123. package/typechain-types/@openzeppelin/contracts/access/Ownable.ts +153 -0
  124. package/typechain-types/@openzeppelin/contracts/access/index.ts +4 -0
  125. package/typechain-types/@openzeppelin/contracts/index.ts +2 -0
  126. package/typechain-types/@openzeppelin/contracts/token/ERC20/IERC20.ts +262 -0
  127. package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.ts +143 -0
  128. package/typechain-types/@openzeppelin/contracts/token/ERC20/extensions/index.ts +4 -0
  129. package/typechain-types/@openzeppelin/contracts/token/ERC20/index.ts +6 -0
  130. package/typechain-types/@openzeppelin/contracts/token/index.ts +2 -0
  131. package/typechain-types/contracts/disperse/coinByRatio/FixedRatioCoinDisperser.ts +679 -0
  132. package/typechain-types/contracts/disperse/coinByRatio/index.ts +4 -0
  133. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20.ts +97 -0
  134. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser.ts +206 -0
  135. package/typechain-types/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +5 -0
  136. package/typechain-types/contracts/disperse/disperseToken/TokenDisperser.ts +191 -0
  137. package/typechain-types/contracts/disperse/disperseToken/index.ts +6 -0
  138. package/typechain-types/contracts/disperse/index.ts +7 -0
  139. package/typechain-types/contracts/index.ts +2 -0
  140. package/typechain-types/contracts/mocks/token/PrivateERC20/PrivateERC20Mock.ts +77 -60
  141. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic128TestsContract.ts +311 -0
  142. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic256TestsContract.ts +311 -0
  143. package/typechain-types/contracts/mocks/utils/mpc/Bitwise128TestsContract.ts +179 -0
  144. package/typechain-types/contracts/mocks/utils/mpc/Bitwise256TestsContract.ts +179 -0
  145. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.ts +206 -0
  146. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.ts +206 -0
  147. package/typechain-types/contracts/mocks/utils/mpc/Comparison128TestsContract.ts +278 -0
  148. package/typechain-types/contracts/mocks/utils/mpc/Comparison256TestsContract.ts +278 -0
  149. package/typechain-types/contracts/mocks/utils/mpc/MinMax128TestsContract.ts +142 -0
  150. package/typechain-types/contracts/mocks/utils/mpc/MinMax256TestsContract.ts +142 -0
  151. package/typechain-types/contracts/mocks/utils/mpc/MinimalImplementation.ts +259 -0
  152. package/typechain-types/contracts/mocks/utils/mpc/MinimalProxy.ts +90 -0
  153. package/typechain-types/contracts/mocks/utils/mpc/MpcOperations128TestContract.ts +388 -0
  154. package/typechain-types/contracts/mocks/utils/mpc/MpcOperationsTestContract.ts +455 -0
  155. package/typechain-types/contracts/mocks/utils/mpc/Mux128TestsContract.ts +109 -0
  156. package/typechain-types/contracts/mocks/utils/mpc/Mux256TestsContract.ts +109 -0
  157. package/typechain-types/contracts/mocks/utils/mpc/OnBoard128TestsContract.ts +166 -0
  158. package/typechain-types/contracts/mocks/utils/mpc/OnBoard256TestsContract.ts +194 -0
  159. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV1.ts +385 -0
  160. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV2.ts +441 -0
  161. package/typechain-types/contracts/mocks/utils/mpc/PrivacyProxy.ts +201 -0
  162. package/typechain-types/contracts/mocks/utils/mpc/Random128TestsContract.ts +131 -0
  163. package/typechain-types/contracts/mocks/utils/mpc/Random256TestsContract.ts +131 -0
  164. package/typechain-types/contracts/mocks/utils/mpc/Shift128TestsContract.ts +142 -0
  165. package/typechain-types/contracts/mocks/utils/mpc/Shift256TestsContract.ts +142 -0
  166. package/typechain-types/contracts/mocks/utils/mpc/Transfer128TestsContract.ts +109 -0
  167. package/typechain-types/contracts/mocks/utils/mpc/Transfer256TestsContract.ts +109 -0
  168. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.ts +128 -0
  169. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.ts +128 -0
  170. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.ts +198 -0
  171. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.ts +182 -0
  172. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.ts +257 -0
  173. package/typechain-types/contracts/mocks/utils/mpc/index.ts +32 -8
  174. package/typechain-types/contracts/token/PrivateERC20/IPrivateERC20.ts +77 -60
  175. package/typechain-types/contracts/token/PrivateERC20/PrivateERC20.ts +77 -60
  176. package/typechain-types/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations.ts +111 -10
  177. package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +74 -0
  178. package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +4 -0
  179. package/typechain-types/factories/@openzeppelin/contracts/index.ts +1 -0
  180. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/IERC20__factory.ts +205 -0
  181. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit__factory.ts +100 -0
  182. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/index.ts +4 -0
  183. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/index.ts +5 -0
  184. package/typechain-types/factories/@openzeppelin/contracts/token/index.ts +1 -0
  185. package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperser__factory.ts +759 -0
  186. package/typechain-types/factories/contracts/disperse/coinByRatio/index.ts +4 -0
  187. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20__factory.ts +43 -0
  188. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser__factory.ts +185 -0
  189. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +5 -0
  190. package/typechain-types/factories/contracts/disperse/disperseToken/TokenDisperser__factory.ts +198 -0
  191. package/typechain-types/factories/contracts/disperse/disperseToken/index.ts +5 -0
  192. package/typechain-types/factories/contracts/disperse/index.ts +5 -0
  193. package/typechain-types/factories/contracts/index.ts +1 -0
  194. package/typechain-types/factories/contracts/mocks/access/DataPrivacyFramework/DataPrivacyFrameworkMock__factory.ts +1 -1
  195. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20Mock__factory.ts +179 -47
  196. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128TestsContract__factory.ts +330 -0
  197. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256TestsContract__factory.ts +330 -0
  198. package/typechain-types/factories/contracts/mocks/utils/mpc/ArithmeticTestsContract__factory.ts +1 -1
  199. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128TestsContract__factory.ts +184 -0
  200. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256TestsContract__factory.ts +184 -0
  201. package/typechain-types/factories/contracts/mocks/utils/mpc/BitwiseTestsContract__factory.ts +1 -1
  202. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract__factory.ts +203 -0
  203. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract__factory.ts +203 -0
  204. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmeticWIthOverflowBitTestsContract.sol/CheckedArithmeticWithOverflowBitTestsContract__factory.ts +1 -1
  205. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128TestsContract__factory.ts +295 -0
  206. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison1TestsContract__factory.ts +1 -1
  207. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256TestsContract__factory.ts +295 -0
  208. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison2TestsContract__factory.ts +1 -1
  209. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax128TestsContract__factory.ts +147 -0
  210. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax256TestsContract__factory.ts +147 -0
  211. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMaxTestsContract__factory.ts +1 -1
  212. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalImplementation__factory.ts +263 -0
  213. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalProxy__factory.ts +104 -0
  214. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous1TestsContract__factory.ts +1 -1
  215. package/typechain-types/factories/contracts/mocks/utils/mpc/MiscellaneousTestsContract__factory.ts +1 -1
  216. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperations128TestContract__factory.ts +723 -0
  217. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperationsTestContract__factory.ts +1410 -0
  218. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux128TestsContract__factory.ts +115 -0
  219. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux256TestsContract__factory.ts +115 -0
  220. package/typechain-types/factories/contracts/mocks/utils/mpc/OffboardToUserKeyTestContract__factory.ts +1 -1
  221. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard128TestsContract__factory.ts +175 -0
  222. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard256TestsContract__factory.ts +205 -0
  223. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV1__factory.ts +521 -0
  224. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV2__factory.ts +648 -0
  225. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyProxy__factory.ts +179 -0
  226. package/typechain-types/factories/contracts/mocks/utils/mpc/Random128TestsContract__factory.ts +131 -0
  227. package/typechain-types/factories/contracts/mocks/utils/mpc/Random256TestsContract__factory.ts +131 -0
  228. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift128TestsContract__factory.ts +147 -0
  229. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift256TestsContract__factory.ts +147 -0
  230. package/typechain-types/factories/contracts/mocks/utils/mpc/ShiftTestsContract__factory.ts +1 -1
  231. package/typechain-types/factories/contracts/mocks/utils/mpc/StringTestsContract__factory.ts +1 -1
  232. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer128TestsContract__factory.ts +135 -0
  233. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer256TestsContract__factory.ts +135 -0
  234. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferScalarTestsContract__factory.ts +1 -1
  235. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferTestsContract__factory.ts +1 -1
  236. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract__factory.ts +154 -0
  237. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract__factory.ts +154 -0
  238. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_16TestsContract__factory.ts +1 -1
  239. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_32TestsContract__factory.ts +1 -1
  240. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_64TestsContract__factory.ts +1 -1
  241. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_8TestsContract__factory.ts +1 -1
  242. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceScalarTestsContract__factory.ts +1 -1
  243. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceTestsContract__factory.ts +1 -1
  244. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract__factory.ts +190 -0
  245. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract__factory.ts +210 -0
  246. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract__factory.ts +260 -0
  247. package/typechain-types/factories/contracts/mocks/utils/mpc/index.ts +32 -8
  248. package/typechain-types/factories/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock__factory.ts +7 -7
  249. package/typechain-types/factories/contracts/token/PrivateERC20/IPrivateERC20__factory.ts +174 -42
  250. package/typechain-types/factories/contracts/token/PrivateERC20/PrivateERC20__factory.ts +174 -42
  251. package/typechain-types/factories/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations__factory.ts +126 -0
  252. package/typechain-types/hardhat.d.ts +638 -80
  253. package/typechain-types/index.ts +78 -16
  254. package/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract.sol +0 -260
  255. package/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.sol +0 -264
  256. package/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.sol +0 -91
  257. package/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.sol +0 -91
  258. package/contracts/mocks/utils/mpc/Comparison128BitTestsContract.sol +0 -158
  259. package/contracts/mocks/utils/mpc/Comparison256BitTestsContract.sol +0 -158
  260. package/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.sol +0 -185
  261. package/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.sol +0 -190
  262. package/contracts/mocks/utils/mpc/README.md +0 -486
  263. package/test/utils/mpc/Unsigned128BitIntegers.test.ts +0 -945
  264. package/test/utils/mpc/Unsigned256BitIntegers.test.ts +0 -1124
  265. package/test/utils/mpc/helpers.ts +0 -77
@@ -1,15 +1,18 @@
1
1
  // SPDX-License-Identifier: MIT
2
-
3
- pragma solidity ^0.8.19;
2
+ pragma solidity 0.8.19;
4
3
 
5
4
  interface ExtendedOperations {
6
5
 
7
6
  function OnBoard(bytes1 metaData, uint256 ct) external returns (uint256 result);
7
+ function OnBoard(bytes1 metaData, uint256 ctHigh, uint256 ctLow) external returns (uint256 result);
8
8
  function OffBoard(bytes1 metaData, uint256 ct) external returns (uint256 result);
9
+ function OffBoard256(bytes1 metaData, uint256 gt) external returns (uint256 ctHigh, uint256 ctLow);
9
10
  function OffBoardToUser(bytes1 metaData, uint256 ct, bytes calldata addr) external returns (uint256 result);
11
+ function OffBoardToUser256(bytes1 metaData, uint256 ct, bytes calldata addr) external returns (uint256 ctHigh, uint256 ctLow);
10
12
  function SetPublic(bytes1 metaData, uint256 ct) external returns (uint256 result);
11
13
  function Rand(bytes1 metaData) external returns (uint256 result);
12
14
  function RandBoundedBits(bytes1 metaData, uint8 numBits) external returns (uint256 result);
15
+
13
16
  function Add(bytes3 metaData, uint256 lhs, uint256 rhs) external returns (uint256 result);
14
17
  function CheckedAdd(bytes3 metaData, uint256 lhs, uint256 rhs) external returns (uint256 overflowBit, uint256 result);
15
18
  function Sub(bytes3 metaData, uint256 lhs, uint256 rhs) external returns (uint256 result);
@@ -37,8 +40,10 @@ interface ExtendedOperations {
37
40
  function Transfer(bytes4 metaData, uint256 a, uint256 b, uint256 amount) external returns (uint256 new_a, uint256 new_b, uint256 res);
38
41
  function TransferWithAllowance(bytes5 metaData, uint256 a, uint256 b, uint256 amount, uint256 allowance) external returns (uint256 new_a, uint256 new_b, uint256 res, uint256 new_allowance);
39
42
  function ValidateCiphertext(bytes1 metaData, uint256 ciphertext, bytes calldata signature) external returns (uint256 result);
43
+ function ValidateCiphertext(bytes1 metaData, uint256 ciphertextHigh, uint256 ciphertextLow, bytes calldata signature) external returns (uint256 result);
40
44
  function GetUserKey(bytes calldata signedEK) external returns (bytes memory encryptedKey);
41
45
  function SHA256Fixed432BitInput(uint256 amount, uint256 seed1, uint256 seed2, uint256 padding1, uint256 padding2, bytes calldata addr) external returns (bytes memory result);
42
46
  }
43
47
 
44
48
  address constant MPC_PRECOMPILE = address(0x0000000000000000000000000000000000000064);
49
+
@@ -0,0 +1,60 @@
1
+ import { extendProvider } from "hardhat/config";
2
+ import type { EIP1193Provider } from "hardhat/types";
3
+
4
+ /** Networks where RPC fee hints are multiplied by this factor (130% = +30%). */
5
+ const GAS_PRICE_MULTIPLIER_NUM = 130n;
6
+ const GAS_PRICE_MULTIPLIER_DEN = 100n;
7
+
8
+ const NETWORKS_WITH_BUMP = new Set(["coti-testnet", "coti-mainnet"]);
9
+
10
+ function bumpHexFee(value: unknown): unknown {
11
+ if (typeof value !== "string" || !value.startsWith("0x")) {
12
+ return value;
13
+ }
14
+ const v = BigInt(value);
15
+ const bumped = (v * GAS_PRICE_MULTIPLIER_NUM) / GAS_PRICE_MULTIPLIER_DEN;
16
+ return "0x" + bumped.toString(16);
17
+ }
18
+
19
+ function bumpBlockFeeFields(block: unknown): unknown {
20
+ if (!block || typeof block !== "object") {
21
+ return block;
22
+ }
23
+ const b = block as Record<string, unknown>;
24
+ const out = { ...b };
25
+ if (typeof b.baseFeePerGas === "string") {
26
+ out.baseFeePerGas = bumpHexFee(b.baseFeePerGas);
27
+ }
28
+ return out;
29
+ }
30
+
31
+ extendProvider((provider: EIP1193Provider, _config, network: string) => {
32
+ if (!NETWORKS_WITH_BUMP.has(network)) {
33
+ return provider;
34
+ }
35
+
36
+ const originalRequest = provider.request.bind(provider);
37
+
38
+ const wrapped: EIP1193Provider = Object.assign(
39
+ Object.create(Object.getPrototypeOf(provider)),
40
+ provider,
41
+ {
42
+ request: async (args: { method: string; params?: readonly unknown[] | object }) => {
43
+ const result = await originalRequest(args);
44
+ const method = args.method;
45
+
46
+ if (method === "eth_gasPrice" || method === "eth_maxPriorityFeePerGas") {
47
+ return bumpHexFee(result);
48
+ }
49
+
50
+ if (method === "eth_getBlockByNumber" || method === "eth_getBlockByHash") {
51
+ return bumpBlockFeeFields(result);
52
+ }
53
+
54
+ return result;
55
+ },
56
+ }
57
+ );
58
+
59
+ return wrapped;
60
+ });
package/hardhat.config.ts CHANGED
@@ -1,36 +1,84 @@
1
1
  import { HardhatUserConfig } from "hardhat/config";
2
2
  import "@nomicfoundation/hardhat-toolbox";
3
+ import "@nomicfoundation/hardhat-verify";
3
4
 
4
5
  import dotenv from "dotenv"
5
6
  dotenv.config()
6
7
 
8
+ /** Bump estimated gas price / EIP-1559 fees by 30% on COTI networks (see hardhat/gasPriceBump.ts). */
9
+ import "./hardhat/gasPriceBump"
10
+
11
+ const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [];
12
+
7
13
  const config: HardhatUserConfig = {
8
14
  defaultNetwork: "coti-testnet",
9
15
  solidity: {
10
- version: "0.8.19",
11
- settings: {
12
- optimizer: {
13
- enabled: true,
14
- runs: 10000
16
+ compilers: [
17
+ {
18
+ version: "0.8.20",
19
+ settings: {
20
+ optimizer: {
21
+ enabled: true,
22
+ runs: 10000
23
+ },
24
+ metadata: {
25
+ // do not include the metadata hash, since this is machine dependent
26
+ // and we want all generated code to be deterministic
27
+ // https://docs.soliditylang.org/en/v0.7.6/metadata.html
28
+ bytecodeHash: 'none',
29
+ },
30
+ }
15
31
  },
16
- metadata: {
17
- // do not include the metadata hash, since this is machine dependent
18
- // and we want all generated code to be deterministic
19
- // https://docs.soliditylang.org/en/v0.7.6/metadata.html
20
- bytecodeHash: 'none',
21
- },
22
- }
32
+ {
33
+ version: "0.8.19",
34
+ settings: {
35
+ optimizer: {
36
+ enabled: true,
37
+ runs: 10000
38
+ },
39
+ metadata: {
40
+ bytecodeHash: 'none',
41
+ },
42
+ }
43
+ }
44
+ ]
23
45
  },
24
46
  networks: {
25
47
  "coti-testnet": {
26
48
  url: "https://testnet.coti.io/rpc",
27
49
  chainId: 7082400,
50
+ accounts,
28
51
  },
29
52
  "coti-mainnet": {
30
53
  url: "https://mainnet.coti.io/rpc",
31
54
  chainId: 2632500,
55
+ accounts,
32
56
  },
33
57
  },
58
+ etherscan: {
59
+ apiKey: {
60
+ "coti-testnet": "placeholder",
61
+ "coti-mainnet": "placeholder",
62
+ },
63
+ customChains: [
64
+ {
65
+ network: "coti-testnet",
66
+ chainId: 7082400,
67
+ urls: {
68
+ apiURL: "https://testnet.cotiscan.io/api",
69
+ browserURL: "https://testnet.cotiscan.io/",
70
+ },
71
+ },
72
+ {
73
+ network: "coti-mainnet",
74
+ chainId: 2632500,
75
+ urls: {
76
+ apiURL: "https://cotiscan.io/api",
77
+ browserURL: "https://cotiscan.io/",
78
+ },
79
+ },
80
+ ],
81
+ },
34
82
  mocha: {
35
83
  timeout: 100000000
36
84
  },
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@coti-io/coti-contracts",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A library for smart contract development on the COTI network.",
5
5
  "author": "COTI Development",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "git://github.com/coti-io/coti-contracts.git"
8
+ "url": "https://github.com/coti-io/coti-contracts.git"
9
9
  },
10
10
  "homepage": "https://docs.coti.io/coti-v2-documentation/build-on-coti/tools/contracts-library",
11
11
  "keywords": [
@@ -19,11 +19,29 @@
19
19
  "on-chain-compute"
20
20
  ],
21
21
  "devDependencies": {
22
+ "@nomicfoundation/hardhat-chai-matchers": "^2.1.0",
23
+ "@nomicfoundation/hardhat-ethers": "^3.1.0",
24
+ "@nomicfoundation/hardhat-ignition": "^0.15.0",
25
+ "@nomicfoundation/hardhat-ignition-ethers": "^0.15.0",
26
+ "@nomicfoundation/hardhat-network-helpers": "^1.0.0",
22
27
  "@nomicfoundation/hardhat-toolbox": "^5.0.0",
23
- "hardhat": "^2.22.8"
28
+ "@nomicfoundation/hardhat-verify": "^2.0.1",
29
+ "@nomicfoundation/ignition-core": "^0.15.0",
30
+ "@typechain/ethers-v6": "^0.5.1",
31
+ "@typechain/hardhat": "^9.1.0",
32
+ "@types/chai": "^4.2.0",
33
+ "@types/mocha": "^10.0.10",
34
+ "chai": "^4.2.0",
35
+ "hardhat": "^2.22.8",
36
+ "hardhat-gas-reporter": "^1.0.8",
37
+ "solidity-coverage": "^0.8.16",
38
+ "ts-node": "^10.9.2",
39
+ "typechain": "^8.3.2",
40
+ "typescript": "^5.9.3"
24
41
  },
25
42
  "dependencies": {
26
43
  "@coti-io/coti-ethers": "^1.0.5",
44
+ "@coti-io/coti-sdk-typescript": "^1.0.6",
27
45
  "@openzeppelin/contracts": "^4.9.6",
28
46
  "dotenv": "^16.4.5"
29
47
  }
@@ -0,0 +1,57 @@
1
+ import { ethers } from "hardhat";
2
+
3
+ async function main() {
4
+ const [deployer] = await ethers.getSigners();
5
+ if (!deployer) {
6
+ throw new Error(
7
+ "No deployer signer found. Set PRIVATE_KEY in your environment."
8
+ );
9
+ }
10
+
11
+ const owner = process.env.NFT_OWNER || deployer.address;
12
+ const name = process.env.NFT_NAME || "Soulbound Node NFT";
13
+ const symbol = process.env.NFT_SYMBOL || "SNODE";
14
+
15
+ console.log("Deploying SoulboundNodeNFT...");
16
+ console.log("Deployer:", deployer.address);
17
+ console.log("Owner:", owner);
18
+ console.log("Name:", name);
19
+ console.log("Symbol:", symbol);
20
+
21
+ const provider = deployer.provider;
22
+ if (!provider) {
23
+ throw new Error("No provider found for deployer signer.");
24
+ }
25
+
26
+ // COTI testnet RPC may not support "pending" block queries.
27
+ // Use legacy gas price + latest nonce + explicit gas limit to avoid
28
+ // pending-based fee/nonce/gas estimation lookups.
29
+ const gasPrice = BigInt(await provider.send("eth_gasPrice", []));
30
+ const nonce = await provider.getTransactionCount(deployer.address, "latest");
31
+ const gasLimit = BigInt(process.env.NFT_DEPLOY_GAS_LIMIT || "6000000");
32
+
33
+ const factory = await ethers.getContractFactory("SoulboundNodeNFT");
34
+ const deployTxReq = await factory.getDeployTransaction(name, symbol, owner);
35
+ if (!deployTxReq.data) {
36
+ throw new Error("Failed to build deploy transaction data.");
37
+ }
38
+
39
+ const sentTx = await deployer.sendTransaction({
40
+ data: deployTxReq.data,
41
+ gasPrice,
42
+ nonce,
43
+ gasLimit,
44
+ });
45
+ console.log("Deployment tx hash:", sentTx.hash);
46
+ const receipt = await sentTx.wait();
47
+ if (!receipt || !receipt.contractAddress) {
48
+ throw new Error("Deployment transaction mined but no contract address found.");
49
+ }
50
+
51
+ console.log("SoulboundNodeNFT deployed at:", receipt.contractAddress);
52
+ }
53
+
54
+ main().catch((error) => {
55
+ console.error(error);
56
+ process.exitCode = 1;
57
+ });
@@ -0,0 +1,327 @@
1
+ import hre from "hardhat"
2
+ import { expect } from "chai"
3
+ import { setupAccounts } from "../../utils/accounts"
4
+ import { PrivacyBridgeERC20Mock, PrivateERC20Mock } from "../../../typechain-types"
5
+ import { Wallet } from "@coti-io/coti-ethers"
6
+ import { Contract } from "ethers"
7
+ import { txOpts } from "../../utils/privateErc20Helpers"
8
+
9
+ const GAS_LIMIT = 12000000
10
+
11
+ describe("PrivacyBridge Fees", function () {
12
+ let bridge: PrivacyBridgeERC20Mock
13
+ let bridgeAddress: string
14
+ let privateToken: PrivateERC20Mock
15
+ let privateTokenAddress: string
16
+ let publicToken: Contract
17
+ let publicTokenAddress: string
18
+ let owner: Wallet
19
+ let user: Wallet
20
+ /** Fee recipient when only two funded accounts exist in `.env` (same as `owner`). */
21
+ let feeRecipient: Wallet
22
+
23
+ const INITIAL_SUPPLY = 1000000n
24
+ const DEPOSIT_AMOUNT = 10000n
25
+ const ONE_PERCENT_FEE = 100n // 100 basis points = 1%
26
+ const TEN_PERCENT_FEE = 1000n // 1000 basis points = 10%
27
+ const MAX_FEE = 1000n
28
+
29
+ before(async function () {
30
+ ;[owner, user] = await setupAccounts()
31
+ feeRecipient = owner
32
+
33
+ // Deploy public ERC20 token (mock)
34
+ const ERC20Factory = await hre.ethers.getContractFactory("ERC20Mock")
35
+ publicToken = await ERC20Factory.connect(owner).deploy("Public Token", "PUB", txOpts)
36
+ await publicToken.waitForDeployment()
37
+ publicTokenAddress = await publicToken.getAddress()
38
+
39
+ // Mint tokens to owner and user
40
+ await publicToken.mint(owner.address, INITIAL_SUPPLY, { gasLimit: GAS_LIMIT })
41
+ await publicToken.mint(user.address, INITIAL_SUPPLY, { gasLimit: GAS_LIMIT })
42
+
43
+ // Deploy private ERC20 token
44
+ const PrivateERC20Factory = await hre.ethers.getContractFactory("PrivateERC20Mock")
45
+ privateToken = await PrivateERC20Factory.connect(owner).deploy(txOpts)
46
+ await privateToken.waitForDeployment()
47
+ privateTokenAddress = await privateToken.getAddress()
48
+
49
+ // Deploy bridge
50
+ const BridgeFactory = await hre.ethers.getContractFactory("PrivacyBridgeERC20Mock")
51
+ bridge = await BridgeFactory.connect(owner).deploy(publicTokenAddress, privateTokenAddress, txOpts)
52
+ await bridge.waitForDeployment()
53
+ bridgeAddress = await bridge.getAddress()
54
+
55
+ // Fund bridge with public tokens for withdrawals
56
+ await publicToken.connect(owner).transfer(bridgeAddress, INITIAL_SUPPLY / 2n, { gasLimit: GAS_LIMIT })
57
+ })
58
+
59
+ describe("Fee Configuration", function () {
60
+ it("should have zero fees by default", async function () {
61
+ expect(await bridge.depositFeeBasisPoints()).to.equal(0n)
62
+ expect(await bridge.withdrawFeeBasisPoints()).to.equal(0n)
63
+ })
64
+
65
+ it("should allow owner to set deposit fee", async function () {
66
+ const tx = await bridge.connect(owner).setDepositFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
67
+ await tx.wait()
68
+
69
+ expect(await bridge.depositFeeBasisPoints()).to.equal(ONE_PERCENT_FEE)
70
+ await expect(tx).to.emit(bridge, "FeeUpdated").withArgs("deposit", ONE_PERCENT_FEE)
71
+ })
72
+
73
+ it("should allow owner to set withdrawal fee", async function () {
74
+ const tx = await bridge.connect(owner).setWithdrawFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
75
+ await tx.wait()
76
+
77
+ expect(await bridge.withdrawFeeBasisPoints()).to.equal(ONE_PERCENT_FEE)
78
+ await expect(tx).to.emit(bridge, "FeeUpdated").withArgs("withdraw", ONE_PERCENT_FEE)
79
+ })
80
+
81
+ it("should allow setting fee to zero", async function () {
82
+ const tx = await bridge.connect(owner).setDepositFee(0n, { gasLimit: GAS_LIMIT })
83
+ await tx.wait()
84
+
85
+ expect(await bridge.depositFeeBasisPoints()).to.equal(0n)
86
+ })
87
+
88
+ it("should allow setting fee to maximum (10%)", async function () {
89
+ const tx = await bridge.connect(owner).setDepositFee(MAX_FEE, { gasLimit: GAS_LIMIT })
90
+ await tx.wait()
91
+
92
+ expect(await bridge.depositFeeBasisPoints()).to.equal(MAX_FEE)
93
+ })
94
+
95
+ it("should revert when fee exceeds maximum", async function () {
96
+ await expect(
97
+ bridge.connect(owner).setDepositFee(MAX_FEE + 1n, { gasLimit: GAS_LIMIT })
98
+ ).to.be.revertedWithCustomError(bridge, "InvalidFee")
99
+ })
100
+
101
+ it("should revert when non-owner tries to set deposit fee", async function () {
102
+ await expect(
103
+ bridge.connect(user).setDepositFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
104
+ ).to.be.revertedWithCustomError(bridge, "OwnableUnauthorizedAccount")
105
+ })
106
+
107
+ it("should revert when non-owner tries to set withdraw fee", async function () {
108
+ await expect(
109
+ bridge.connect(user).setWithdrawFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
110
+ ).to.be.revertedWithCustomError(bridge, "OwnableUnauthorizedAccount")
111
+ })
112
+
113
+ // Reset fees to zero for next tests
114
+ after(async function () {
115
+ await bridge.connect(owner).setDepositFee(0n, { gasLimit: GAS_LIMIT })
116
+ await bridge.connect(owner).setWithdrawFee(0n, { gasLimit: GAS_LIMIT })
117
+ })
118
+ })
119
+
120
+ describe("Deposit with Fees", function () {
121
+ it("should deposit without fee when fee is zero", async function () {
122
+ const balanceBefore = await publicToken.balanceOf(user.address)
123
+
124
+ await publicToken.connect(user).approve(bridgeAddress, DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
125
+ const tx = await bridge.connect(user).deposit(DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
126
+ await tx.wait()
127
+
128
+ const balanceAfter = await publicToken.balanceOf(user.address)
129
+ expect(balanceBefore - balanceAfter).to.equal(DEPOSIT_AMOUNT)
130
+
131
+ // User should receive full amount in private tokens
132
+ const ctBalance = await privateToken["balanceOf(address)"](user.address)
133
+ const privateBalance = await user.decryptValue256(ctBalance)
134
+ expect(privateBalance).to.equal(DEPOSIT_AMOUNT)
135
+
136
+ expect(await bridge.accumulatedFees()).to.equal(0n)
137
+ })
138
+
139
+ it("should deduct 1% fee on deposit", async function () {
140
+ // Set 1% fee
141
+ await bridge.connect(owner).setDepositFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
142
+
143
+ const expectedFee = (DEPOSIT_AMOUNT * ONE_PERCENT_FEE) / 10000n
144
+ const expectedAmount = DEPOSIT_AMOUNT - expectedFee
145
+
146
+ await publicToken.connect(user).approve(bridgeAddress, DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
147
+ const tx = await bridge.connect(user).deposit(DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
148
+ await tx.wait()
149
+
150
+ // User should receive amount minus fee
151
+ const ctBalance = await privateToken["balanceOf(address)"](user.address)
152
+ const privateBalance = await user.decryptValue256(ctBalance)
153
+ expect(privateBalance).to.equal(DEPOSIT_AMOUNT + expectedAmount) // Previous deposit + this one
154
+
155
+ expect(await bridge.accumulatedFees()).to.equal(expectedFee)
156
+ })
157
+
158
+ it("should deduct 10% fee on deposit", async function () {
159
+ // Set 10% fee
160
+ await bridge.connect(owner).setDepositFee(TEN_PERCENT_FEE, { gasLimit: GAS_LIMIT })
161
+
162
+ const previousFees = await bridge.accumulatedFees()
163
+ const expectedFee = (DEPOSIT_AMOUNT * TEN_PERCENT_FEE) / 10000n
164
+ const expectedAmount = DEPOSIT_AMOUNT - expectedFee
165
+
166
+ await publicToken.connect(user).approve(bridgeAddress, DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
167
+ const tx = await bridge.connect(user).deposit(DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
168
+ await tx.wait()
169
+
170
+ expect(await bridge.accumulatedFees()).to.equal(previousFees + expectedFee)
171
+ })
172
+
173
+ // Reset fee to zero
174
+ after(async function () {
175
+ await bridge.connect(owner).setDepositFee(0n, { gasLimit: GAS_LIMIT })
176
+ })
177
+ })
178
+
179
+ describe("Withdraw with Fees (via onTokenReceived)", function () {
180
+ const WITHDRAW_AMOUNT = 5000n
181
+
182
+ before(async function () {
183
+ // Ensure user has private tokens to withdraw
184
+ const ctBalance = await privateToken["balanceOf(address)"](user.address)
185
+ const balance = await user.decryptValue256(ctBalance)
186
+ if (balance < WITHDRAW_AMOUNT) {
187
+ await publicToken.connect(user).approve(bridgeAddress, DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
188
+ await bridge.connect(user).deposit(DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
189
+ }
190
+ })
191
+
192
+ it("should withdraw without fee when fee is zero", async function () {
193
+ const publicBalanceBefore = await publicToken.balanceOf(user.address)
194
+
195
+ const tx = await privateToken.connect(user).transferAndCall(
196
+ bridgeAddress,
197
+ WITHDRAW_AMOUNT,
198
+ "0x",
199
+ { gasLimit: GAS_LIMIT }
200
+ )
201
+ await tx.wait()
202
+
203
+ const publicBalanceAfter = await publicToken.balanceOf(user.address)
204
+ expect(publicBalanceAfter - publicBalanceBefore).to.equal(WITHDRAW_AMOUNT)
205
+
206
+ // No fees accumulated
207
+ expect(await bridge.accumulatedFees()).to.be.greaterThan(0n) // From previous deposit tests
208
+ })
209
+
210
+ it("should deduct 1% fee on withdrawal", async function () {
211
+ // Set 1% fee
212
+ await bridge.connect(owner).setWithdrawFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
213
+
214
+ const previousFees = await bridge.accumulatedFees()
215
+ const expectedFee = (WITHDRAW_AMOUNT * ONE_PERCENT_FEE) / 10000n
216
+ const expectedAmount = WITHDRAW_AMOUNT - expectedFee
217
+
218
+ const publicBalanceBefore = await publicToken.balanceOf(user.address)
219
+
220
+ const tx = await privateToken.connect(user).transferAndCall(
221
+ bridgeAddress,
222
+ WITHDRAW_AMOUNT,
223
+ "0x",
224
+ { gasLimit: GAS_LIMIT }
225
+ )
226
+ await tx.wait()
227
+
228
+ const publicBalanceAfter = await publicToken.balanceOf(user.address)
229
+ expect(publicBalanceAfter - publicBalanceBefore).to.equal(expectedAmount)
230
+
231
+ expect(await bridge.accumulatedFees()).to.equal(previousFees + expectedFee)
232
+ })
233
+
234
+ it("should deduct 10% fee on withdrawal", async function () {
235
+ // Set 10% fee
236
+ await bridge.connect(owner).setWithdrawFee(TEN_PERCENT_FEE, { gasLimit: GAS_LIMIT })
237
+
238
+ const previousFees = await bridge.accumulatedFees()
239
+ const expectedFee = (WITHDRAW_AMOUNT * TEN_PERCENT_FEE) / 10000n
240
+ const expectedAmount = WITHDRAW_AMOUNT - expectedFee
241
+
242
+ const publicBalanceBefore = await publicToken.balanceOf(user.address)
243
+
244
+ const tx = await privateToken.connect(user).transferAndCall(
245
+ bridgeAddress,
246
+ WITHDRAW_AMOUNT,
247
+ "0x",
248
+ { gasLimit: GAS_LIMIT }
249
+ )
250
+ await tx.wait()
251
+
252
+ const publicBalanceAfter = await publicToken.balanceOf(user.address)
253
+ expect(publicBalanceAfter - publicBalanceBefore).to.equal(expectedAmount)
254
+
255
+ expect(await bridge.accumulatedFees()).to.equal(previousFees + expectedFee)
256
+ })
257
+
258
+ // Reset fee to zero
259
+ after(async function () {
260
+ await bridge.connect(owner).setWithdrawFee(0n, { gasLimit: GAS_LIMIT })
261
+ })
262
+ })
263
+
264
+ describe("Fee Withdrawal", function () {
265
+ it("should allow owner to withdraw accumulated fees", async function () {
266
+ const accumulatedFees = await bridge.accumulatedFees()
267
+ expect(accumulatedFees).to.be.greaterThan(0n)
268
+
269
+ const recipientBalanceBefore = await publicToken.balanceOf(feeRecipient.address)
270
+
271
+ const tx = await bridge.connect(owner).withdrawFees(
272
+ feeRecipient.address,
273
+ accumulatedFees,
274
+ { gasLimit: GAS_LIMIT }
275
+ )
276
+ await tx.wait()
277
+
278
+ const recipientBalanceAfter = await publicToken.balanceOf(feeRecipient.address)
279
+ expect(recipientBalanceAfter - recipientBalanceBefore).to.equal(accumulatedFees)
280
+
281
+ expect(await bridge.accumulatedFees()).to.equal(0n)
282
+ await expect(tx).to.emit(bridge, "FeesWithdrawn").withArgs(feeRecipient.address, accumulatedFees)
283
+ })
284
+
285
+ it("should allow partial withdrawal of fees", async function () {
286
+ // Accumulate some fees first
287
+ await bridge.connect(owner).setDepositFee(ONE_PERCENT_FEE, { gasLimit: GAS_LIMIT })
288
+ await publicToken.connect(user).approve(bridgeAddress, DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
289
+ await bridge.connect(user).deposit(DEPOSIT_AMOUNT, { gasLimit: GAS_LIMIT })
290
+
291
+ const accumulatedFees = await bridge.accumulatedFees()
292
+ const withdrawAmount = accumulatedFees / 2n
293
+
294
+ await bridge.connect(owner).withdrawFees(feeRecipient.address, withdrawAmount, { gasLimit: GAS_LIMIT })
295
+
296
+ expect(await bridge.accumulatedFees()).to.equal(accumulatedFees - withdrawAmount)
297
+
298
+ // Reset
299
+ await bridge.connect(owner).setDepositFee(0n, { gasLimit: GAS_LIMIT })
300
+ })
301
+
302
+ it("should revert when non-owner tries to withdraw fees", async function () {
303
+ await expect(
304
+ bridge.connect(user).withdrawFees(feeRecipient.address, 100n, { gasLimit: GAS_LIMIT })
305
+ ).to.be.revertedWithCustomError(bridge, "OwnableUnauthorizedAccount")
306
+ })
307
+
308
+ it("should revert when withdrawing to zero address", async function () {
309
+ await expect(
310
+ bridge.connect(owner).withdrawFees(hre.ethers.ZeroAddress, 100n, { gasLimit: GAS_LIMIT })
311
+ ).to.be.revertedWithCustomError(bridge, "InvalidAddress")
312
+ })
313
+
314
+ it("should revert when withdrawing zero amount", async function () {
315
+ await expect(
316
+ bridge.connect(owner).withdrawFees(feeRecipient.address, 0n, { gasLimit: GAS_LIMIT })
317
+ ).to.be.revertedWithCustomError(bridge, "AmountZero")
318
+ })
319
+
320
+ it("should revert when withdrawing more than accumulated fees", async function () {
321
+ const accumulatedFees = await bridge.accumulatedFees()
322
+ await expect(
323
+ bridge.connect(owner).withdrawFees(feeRecipient.address, accumulatedFees + 1n, { gasLimit: GAS_LIMIT })
324
+ ).to.be.revertedWithCustomError(bridge, "InsufficientAccumulatedFees")
325
+ })
326
+ })
327
+ })