@coti-io/coti-contracts 1.0.9 → 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 (271) 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 +10891 -2449
  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/Arithmetic128BitTestsContract.ts +341 -0
  142. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic128TestsContract.ts +311 -0
  143. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract.ts +347 -0
  144. package/typechain-types/contracts/mocks/utils/mpc/Arithmetic256TestsContract.ts +311 -0
  145. package/typechain-types/contracts/mocks/utils/mpc/Bitwise128BitTestsContract.ts +186 -0
  146. package/typechain-types/contracts/mocks/utils/mpc/Bitwise128TestsContract.ts +179 -0
  147. package/typechain-types/contracts/mocks/utils/mpc/Bitwise256BitTestsContract.ts +186 -0
  148. package/typechain-types/contracts/mocks/utils/mpc/Bitwise256TestsContract.ts +179 -0
  149. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract.ts +206 -0
  150. package/typechain-types/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract.ts +206 -0
  151. package/typechain-types/contracts/mocks/utils/mpc/Comparison128BitTestsContract.ts +260 -0
  152. package/typechain-types/contracts/mocks/utils/mpc/Comparison128TestsContract.ts +278 -0
  153. package/typechain-types/contracts/mocks/utils/mpc/Comparison256BitTestsContract.ts +260 -0
  154. package/typechain-types/contracts/mocks/utils/mpc/Comparison256TestsContract.ts +278 -0
  155. package/typechain-types/contracts/mocks/utils/mpc/MinMax128TestsContract.ts +142 -0
  156. package/typechain-types/contracts/mocks/utils/mpc/MinMax256TestsContract.ts +142 -0
  157. package/typechain-types/contracts/mocks/utils/mpc/MinimalImplementation.ts +259 -0
  158. package/typechain-types/contracts/mocks/utils/mpc/MinimalProxy.ts +90 -0
  159. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract.ts +302 -0
  160. package/typechain-types/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract.ts +322 -0
  161. package/typechain-types/contracts/mocks/utils/mpc/MpcOperations128TestContract.ts +388 -0
  162. package/typechain-types/contracts/mocks/utils/mpc/MpcOperationsTestContract.ts +455 -0
  163. package/typechain-types/contracts/mocks/utils/mpc/Mux128TestsContract.ts +109 -0
  164. package/typechain-types/contracts/mocks/utils/mpc/Mux256TestsContract.ts +109 -0
  165. package/typechain-types/contracts/mocks/utils/mpc/OnBoard128TestsContract.ts +166 -0
  166. package/typechain-types/contracts/mocks/utils/mpc/OnBoard256TestsContract.ts +194 -0
  167. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV1.ts +385 -0
  168. package/typechain-types/contracts/mocks/utils/mpc/PrivacyImplementationV2.ts +441 -0
  169. package/typechain-types/contracts/mocks/utils/mpc/PrivacyProxy.ts +201 -0
  170. package/typechain-types/contracts/mocks/utils/mpc/Random128TestsContract.ts +131 -0
  171. package/typechain-types/contracts/mocks/utils/mpc/Random256TestsContract.ts +131 -0
  172. package/typechain-types/contracts/mocks/utils/mpc/Shift128TestsContract.ts +142 -0
  173. package/typechain-types/contracts/mocks/utils/mpc/Shift256TestsContract.ts +142 -0
  174. package/typechain-types/contracts/mocks/utils/mpc/Transfer128TestsContract.ts +109 -0
  175. package/typechain-types/contracts/mocks/utils/mpc/Transfer256TestsContract.ts +109 -0
  176. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract.ts +128 -0
  177. package/typechain-types/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract.ts +128 -0
  178. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract.ts +198 -0
  179. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract.ts +182 -0
  180. package/typechain-types/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract.ts +257 -0
  181. package/typechain-types/contracts/mocks/utils/mpc/index.ts +32 -0
  182. package/typechain-types/contracts/token/PrivateERC20/IPrivateERC20.ts +77 -60
  183. package/typechain-types/contracts/token/PrivateERC20/PrivateERC20.ts +77 -60
  184. package/typechain-types/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations.ts +111 -10
  185. package/typechain-types/factories/@openzeppelin/contracts/access/Ownable__factory.ts +74 -0
  186. package/typechain-types/factories/@openzeppelin/contracts/access/index.ts +4 -0
  187. package/typechain-types/factories/@openzeppelin/contracts/index.ts +1 -0
  188. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/IERC20__factory.ts +205 -0
  189. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit__factory.ts +100 -0
  190. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/extensions/index.ts +4 -0
  191. package/typechain-types/factories/@openzeppelin/contracts/token/ERC20/index.ts +5 -0
  192. package/typechain-types/factories/@openzeppelin/contracts/token/index.ts +1 -0
  193. package/typechain-types/factories/contracts/disperse/coinByRatio/FixedRatioCoinDisperser__factory.ts +759 -0
  194. package/typechain-types/factories/contracts/disperse/coinByRatio/index.ts +4 -0
  195. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/IMintableERC20__factory.ts +43 -0
  196. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/MintDisperser__factory.ts +185 -0
  197. package/typechain-types/factories/contracts/disperse/disperseToken/MintDisperser.sol/index.ts +5 -0
  198. package/typechain-types/factories/contracts/disperse/disperseToken/TokenDisperser__factory.ts +198 -0
  199. package/typechain-types/factories/contracts/disperse/disperseToken/index.ts +5 -0
  200. package/typechain-types/factories/contracts/disperse/index.ts +5 -0
  201. package/typechain-types/factories/contracts/index.ts +1 -0
  202. package/typechain-types/factories/contracts/mocks/access/DataPrivacyFramework/DataPrivacyFrameworkMock__factory.ts +1 -1
  203. package/typechain-types/factories/contracts/mocks/token/PrivateERC20/PrivateERC20Mock__factory.ts +179 -47
  204. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128BitTestsContract__factory.ts +349 -0
  205. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic128TestsContract__factory.ts +330 -0
  206. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256BitTestsContract__factory.ts +364 -0
  207. package/typechain-types/factories/contracts/mocks/utils/mpc/Arithmetic256TestsContract__factory.ts +330 -0
  208. package/typechain-types/factories/contracts/mocks/utils/mpc/ArithmeticTestsContract__factory.ts +1 -1
  209. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128BitTestsContract__factory.ts +182 -0
  210. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise128TestsContract__factory.ts +184 -0
  211. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256BitTestsContract__factory.ts +182 -0
  212. package/typechain-types/factories/contracts/mocks/utils/mpc/Bitwise256TestsContract__factory.ts +184 -0
  213. package/typechain-types/factories/contracts/mocks/utils/mpc/BitwiseTestsContract__factory.ts +1 -1
  214. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic128WithOverflowBitTestsContract__factory.ts +203 -0
  215. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmetic256WithOverflowBitTestsContract__factory.ts +203 -0
  216. package/typechain-types/factories/contracts/mocks/utils/mpc/CheckedArithmeticWIthOverflowBitTestsContract.sol/CheckedArithmeticWithOverflowBitTestsContract__factory.ts +1 -1
  217. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128BitTestsContract__factory.ts +255 -0
  218. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison128TestsContract__factory.ts +295 -0
  219. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison1TestsContract__factory.ts +1 -1
  220. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256BitTestsContract__factory.ts +255 -0
  221. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison256TestsContract__factory.ts +295 -0
  222. package/typechain-types/factories/contracts/mocks/utils/mpc/Comparison2TestsContract__factory.ts +1 -1
  223. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax128TestsContract__factory.ts +147 -0
  224. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMax256TestsContract__factory.ts +147 -0
  225. package/typechain-types/factories/contracts/mocks/utils/mpc/MinMaxTestsContract__factory.ts +1 -1
  226. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalImplementation__factory.ts +263 -0
  227. package/typechain-types/factories/contracts/mocks/utils/mpc/MinimalProxy__factory.ts +104 -0
  228. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous128BitTestsContract__factory.ts +332 -0
  229. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous1TestsContract__factory.ts +1 -1
  230. package/typechain-types/factories/contracts/mocks/utils/mpc/Miscellaneous256BitTestsContract__factory.ts +380 -0
  231. package/typechain-types/factories/contracts/mocks/utils/mpc/MiscellaneousTestsContract__factory.ts +1 -1
  232. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperations128TestContract__factory.ts +723 -0
  233. package/typechain-types/factories/contracts/mocks/utils/mpc/MpcOperationsTestContract__factory.ts +1410 -0
  234. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux128TestsContract__factory.ts +115 -0
  235. package/typechain-types/factories/contracts/mocks/utils/mpc/Mux256TestsContract__factory.ts +115 -0
  236. package/typechain-types/factories/contracts/mocks/utils/mpc/OffboardToUserKeyTestContract__factory.ts +1 -1
  237. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard128TestsContract__factory.ts +175 -0
  238. package/typechain-types/factories/contracts/mocks/utils/mpc/OnBoard256TestsContract__factory.ts +205 -0
  239. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV1__factory.ts +521 -0
  240. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyImplementationV2__factory.ts +648 -0
  241. package/typechain-types/factories/contracts/mocks/utils/mpc/PrivacyProxy__factory.ts +179 -0
  242. package/typechain-types/factories/contracts/mocks/utils/mpc/Random128TestsContract__factory.ts +131 -0
  243. package/typechain-types/factories/contracts/mocks/utils/mpc/Random256TestsContract__factory.ts +131 -0
  244. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift128TestsContract__factory.ts +147 -0
  245. package/typechain-types/factories/contracts/mocks/utils/mpc/Shift256TestsContract__factory.ts +147 -0
  246. package/typechain-types/factories/contracts/mocks/utils/mpc/ShiftTestsContract__factory.ts +1 -1
  247. package/typechain-types/factories/contracts/mocks/utils/mpc/StringTestsContract__factory.ts +1 -1
  248. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer128TestsContract__factory.ts +135 -0
  249. package/typechain-types/factories/contracts/mocks/utils/mpc/Transfer256TestsContract__factory.ts +135 -0
  250. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferScalarTestsContract__factory.ts +1 -1
  251. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferTestsContract__factory.ts +1 -1
  252. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance128TestsContract__factory.ts +154 -0
  253. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance256TestsContract__factory.ts +154 -0
  254. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_16TestsContract__factory.ts +1 -1
  255. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_32TestsContract__factory.ts +1 -1
  256. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_64TestsContract__factory.ts +1 -1
  257. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowance64_8TestsContract__factory.ts +1 -1
  258. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceScalarTestsContract__factory.ts +1 -1
  259. package/typechain-types/factories/contracts/mocks/utils/mpc/TransferWithAllowanceTestsContract__factory.ts +1 -1
  260. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext128TestsContract__factory.ts +190 -0
  261. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertext256TestsContract__factory.ts +210 -0
  262. package/typechain-types/factories/contracts/mocks/utils/mpc/ValidateCiphertextTestsContract__factory.ts +260 -0
  263. package/typechain-types/factories/contracts/mocks/utils/mpc/index.ts +32 -0
  264. package/typechain-types/factories/contracts/mocks/wallet/PrivateERC20Wallet/PrivateERC20WalletMock__factory.ts +7 -7
  265. package/typechain-types/factories/contracts/token/PrivateERC20/IPrivateERC20__factory.ts +174 -42
  266. package/typechain-types/factories/contracts/token/PrivateERC20/PrivateERC20__factory.ts +174 -42
  267. package/typechain-types/factories/contracts/utils/mpc/MpcInterface.sol/ExtendedOperations__factory.ts +126 -0
  268. package/typechain-types/hardhat.d.ts +702 -0
  269. package/typechain-types/index.ts +78 -0
  270. package/contracts/mocks/utils/mpc/README.md +0 -486
  271. package/contracts/package.json +0 -11
@@ -2,19 +2,26 @@ import hre from "hardhat"
2
2
  import { expect } from "chai"
3
3
 
4
4
  import { setupAccounts } from "../../utils/accounts"
5
+ import {
6
+ IPRIVATE_ERC20_INTERFACE_ID,
7
+ mintPublic,
8
+ burnPublic,
9
+ txOpts,
10
+ encryptItUint256,
11
+ decryptCtUint256,
12
+ expectTxFails
13
+ } from "../../utils/privateErc20Helpers"
5
14
  import { PrivateERC20Mock, PrivateERC20WalletMock } from "../../../typechain-types"
6
- import { itUint, Wallet, ZeroAddress } from "@coti-io/coti-ethers"
7
-
8
- const GAS_LIMIT = 12000000
15
+ import { Wallet } from "@coti-io/coti-ethers"
16
+ import type { itUint256 } from "@coti-io/coti-sdk-typescript"
17
+ import { ZeroAddress } from "ethers"
9
18
 
10
19
  async function deploy() {
11
20
  const [owner, otherAccount] = await setupAccounts()
12
21
 
13
22
  const tokenContract = await hre.ethers.getContractFactory("PrivateERC20Mock")
14
23
 
15
- const token = await tokenContract
16
- .connect(owner)
17
- .deploy({ gasLimit: GAS_LIMIT })
24
+ const token = await tokenContract.connect(owner).deploy(txOpts)
18
25
 
19
26
  const contract = await token.waitForDeployment()
20
27
 
@@ -42,118 +49,102 @@ describe("Private ERC20", function () {
42
49
  })
43
50
 
44
51
  describe("deploy", function () {
45
- it('has a name', async function () {
46
- await expect(await contract.name()).to.equal("PrivateERC20Mock");
52
+ it("has a name", async function () {
53
+ await expect(await contract.name()).to.equal("PrivateERC20Mock")
54
+ })
55
+
56
+ it("has a symbol", async function () {
57
+ await expect(await contract.symbol()).to.equal("PE20M")
58
+ })
59
+
60
+ it("has 6 decimals", async function () {
61
+ await expect(await contract.decimals()).to.equal(6n)
62
+ })
63
+
64
+ it("totalSupply view is zero (privacy stance)", async function () {
65
+ await expect(await contract.totalSupply()).to.equal(0n)
47
66
  })
48
67
 
49
- it('has a symbol', async function () {
50
- await expect(await contract.symbol()).to.equal("PE20M");
68
+ it("publicAmountsEnabled is true by default", async function () {
69
+ await expect(await contract.publicAmountsEnabled()).to.equal(true)
51
70
  })
52
71
 
53
- it('has 6 decimals', async function () {
54
- await expect(await contract.decimals()).to.equal(6n);
72
+ it("supports ERC-165 and IPrivateERC20", async function () {
73
+ await expect(await contract.supportsInterface("0x01ffc9a7")).to.equal(true)
74
+ await expect(await contract.supportsInterface(IPRIVATE_ERC20_INTERFACE_ID)).to.equal(true)
75
+ await expect(await contract.supportsInterface("0xffffffff")).to.equal(false)
55
76
  })
56
77
  })
57
78
 
58
79
  describe("mint", function () {
59
80
  const value = 5000n
60
81
 
61
- describe("successful mint", async function () {
82
+ describe("successful mint", function () {
62
83
  before("minting", async function () {
63
- this.tx = await contract
64
- .connect(owner)
65
- .mint(owner.address, value, { gasLimit: GAS_LIMIT })
66
-
84
+ this.tx = await mintPublic(contract, owner, owner.address, value)
67
85
  await this.tx.wait()
68
86
  })
69
-
70
- it('does not increment totalSupply', async function () {
87
+
88
+ it("does not increment totalSupply", async function () {
71
89
  await expect(await contract.totalSupply()).to.equal(0n)
72
90
  })
73
-
74
- it('increments recipient balance', async function () {
91
+
92
+ it("increments recipient balance", async function () {
75
93
  const ctBalance = await contract["balanceOf(address)"](owner.address)
76
-
77
- const balance = await owner.decryptValue(ctBalance)
78
-
94
+
95
+ const balance = decryptCtUint256(owner, ctBalance)
96
+
79
97
  await expect(balance).to.equal(value)
80
98
  })
81
-
82
- it('emits Transfer event', async function () {
99
+
100
+ it("emits Transfer event", async function () {
83
101
  await expect(this.tx).to.emit(contract, "Transfer")
84
102
  })
85
103
  })
86
-
87
- describe('failed mint', async function () {
88
- it('rejects minting to the zero address', async function () {
89
- await expect(
90
- contract
91
- .connect(owner)
92
- .mint(ZeroAddress, value)
93
- )
94
- .to.be.revertedWithCustomError(contract, "ERC20InvalidReceiver")
95
- .withArgs(ZeroAddress)
104
+
105
+ describe("failed mint", function () {
106
+ it("rejects minting to the zero address", async function () {
107
+ await expectTxFails(
108
+ contract.connect(owner).getFunction("mint(address,uint256)")(ZeroAddress, value, txOpts)
109
+ )
96
110
  })
97
-
98
- // it('rejects overflow', async function () {
99
- // const tx = await contract
100
- // .connect(owner.wallet)
101
- // .mint(owner.wallet.address, (BigInt(2) ** BigInt(64)) - BigInt(1), { gasLimit: GAS_LIMIT })
102
-
103
- // await tx.wait()
104
-
105
- // await expect(tx).to.be.reverted
106
- // })
107
111
  })
108
112
  })
109
113
 
110
114
  describe("burn", function () {
111
115
  const value = 5000n
112
-
113
- describe('failed burn', async function () {
114
- it('rejects burning from the zero address', async function () {
115
- await expect(
116
- contract
117
- .connect(owner)
118
- .burn(ZeroAddress, value)
119
- )
120
- .to.be.revertedWithCustomError(contract, "ERC20InvalidSender")
121
- .withArgs(ZeroAddress)
122
- })
123
116
 
124
- it('does not update balance when burning more than balance', async function () {
125
- const tx = await contract
126
- .connect(owner)
127
- .burn(owner.address, value + 1n, { gasLimit: GAS_LIMIT })
128
-
129
- await tx.wait()
130
-
131
- const ctBalance = await contract["balanceOf(address)"](owner.address)
117
+ describe("failed burn", function () {
118
+ it("rejects burning from the zero address", async function () {
119
+ await expectTxFails(
120
+ contract.connect(owner).getFunction("burn(address,uint256)")(ZeroAddress, value, txOpts)
121
+ )
122
+ })
132
123
 
133
- const balance = await owner.decryptValue(ctBalance)
134
-
135
- await expect(balance).to.equal(value)
124
+ it("reverts when burning more than balance (no Transfer)", async function () {
125
+ await expectTxFails(
126
+ contract
127
+ .connect(owner)
128
+ .getFunction("burn(address,uint256)")(owner.address, value + 1n, txOpts)
129
+ )
136
130
  })
137
131
  })
138
132
 
139
- describe('successful burn', async function () {
133
+ describe("successful burn", function () {
140
134
  before("burning", async function () {
141
- this.tx = await contract
142
- .connect(owner)
143
- .burn(owner.address, value, { gasLimit: GAS_LIMIT })
144
-
135
+ this.tx = await burnPublic(contract, owner, owner.address, value)
145
136
  await this.tx.wait()
146
137
  })
147
138
 
148
- it('decrements the senders balance', async function () {
139
+ it("decrements the senders balance", async function () {
149
140
  const ctBalance = await contract["balanceOf(address)"](owner.address)
150
141
 
151
- const balance = await owner.decryptValue(ctBalance)
152
-
142
+ const balance = decryptCtUint256(owner, ctBalance)
143
+
153
144
  await expect(balance).to.equal(0n)
154
145
  })
155
146
 
156
- it('emits Transfer event', async function () {
147
+ it("emits Transfer event", async function () {
157
148
  await expect(this.tx).to.emit(contract, "Transfer")
158
149
  })
159
150
  })
@@ -163,76 +154,78 @@ describe("Private ERC20", function () {
163
154
  const value = 10000n
164
155
 
165
156
  before("minting", async function () {
166
- this.tx = await contract
167
- .connect(owner)
168
- .mint(owner.address, value, { gasLimit: GAS_LIMIT })
169
-
157
+ this.tx = await mintPublic(contract, owner, owner.address, value)
170
158
  await this.tx.wait()
171
159
  })
172
160
 
173
- describe("failed transfer", async function () {
174
- it('rejects transferring to the zero address', async function () {
175
- const itValue = await owner.encryptValue(value, contractAddress, contract["transfer(address,(uint256,bytes))"].fragment.selector) as itUint
176
-
161
+ describe("failed transfer", function () {
162
+ it("rejects transferring to the zero address", async function () {
163
+ const itValue = (await encryptItUint256(owner,
164
+ value,
165
+ contractAddress,
166
+ contract.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
167
+ )) as itUint256
168
+
177
169
  await expect(
178
170
  contract
179
171
  .connect(owner)
180
- ["transfer(address,(uint256,bytes))"]
181
- (ZeroAddress, itValue)
182
- )
172
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(ZeroAddress, itValue)
173
+ )
183
174
  .to.be.revertedWithCustomError(contract, "ERC20InvalidReceiver")
184
175
  .withArgs(ZeroAddress)
185
176
  })
186
177
 
187
- it('does not transfer tokens when amount exceeds balance', async function () {
188
- const itValue = await owner.encryptValue(value + 1n, contractAddress, contract["transfer(address,(uint256,bytes))"].fragment.selector) as itUint
189
-
190
- const tx = await contract
191
- .connect(owner)
192
- ["transfer(address,(uint256,bytes))"]
193
- (otherAccount.address, itValue, { gasLimit: GAS_LIMIT })
194
-
195
- await tx.wait()
196
-
197
- let ctBalance = await contract["balanceOf(address)"](owner.address)
198
-
199
- let balance = await owner.decryptValue(ctBalance)
200
-
201
- await expect(balance).to.equal(value)
202
-
203
- ctBalance = await contract["balanceOf(address)"](otherAccount.address)
178
+ it("reverts when amount exceeds balance (no Transfer)", async function () {
179
+ const itValue = (await encryptItUint256(owner,
180
+ value + 1n,
181
+ contractAddress,
182
+ contract.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
183
+ )) as itUint256
204
184
 
205
- balance = await otherAccount.decryptValue(ctBalance)
206
-
207
- await expect(balance).to.equal(0n)
185
+ await expectTxFails(
186
+ contract
187
+ .connect(owner)
188
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(
189
+ otherAccount.address,
190
+ itValue,
191
+ txOpts
192
+ )
193
+ )
208
194
  })
209
195
  })
210
196
 
211
- describe("successful transfer", async function () {
197
+ describe("successful transfer", function () {
212
198
  before("transferring", async function () {
213
- const itValue = await owner.encryptValue(value / 2n, contractAddress, contract["transfer(address,(uint256,bytes))"].fragment.selector) as itUint
199
+ const itValue = (await encryptItUint256(owner,
200
+ value / 2n,
201
+ contractAddress,
202
+ contract.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
203
+ )) as itUint256
214
204
 
215
205
  const tx = await contract
216
206
  .connect(owner)
217
- ["transfer(address,(uint256,bytes))"]
218
- (otherAccount.address, itValue, { gasLimit: GAS_LIMIT })
219
-
207
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(
208
+ otherAccount.address,
209
+ itValue,
210
+ txOpts
211
+ )
212
+
220
213
  await tx.wait()
221
214
  })
222
215
 
223
- it('decrements the senders balance', async function () {
216
+ it("decrements the senders balance", async function () {
224
217
  const ctBalance = await contract["balanceOf(address)"](owner.address)
225
218
 
226
- const balance = await owner.decryptValue(ctBalance)
227
-
219
+ const balance = decryptCtUint256(owner, ctBalance)
220
+
228
221
  await expect(balance).to.equal(value / 2n)
229
222
  })
230
223
 
231
- it('increments the receivers balance', async function () {
224
+ it("increments the receivers balance", async function () {
232
225
  const ctBalance = await contract["balanceOf(address)"](otherAccount.address)
233
226
 
234
- const balance = await otherAccount.decryptValue(ctBalance)
235
-
227
+ const balance = decryptCtUint256(otherAccount, ctBalance)
228
+
236
229
  await expect(balance).to.equal(value / 2n)
237
230
  })
238
231
  })
@@ -241,135 +234,187 @@ describe("Private ERC20", function () {
241
234
  describe("approve", function () {
242
235
  const value = 5000n
243
236
 
244
- describe("failed approval", async function () {
245
- it('rejects when approving the zero address', async function () {
246
- const itValue = await owner.encryptValue(value, contractAddress, contract["transfer(address,(uint256,bytes))"].fragment.selector) as itUint
247
-
237
+ describe("failed approval", function () {
238
+ it("rejects when approving the zero address", async function () {
239
+ const itValue = (await encryptItUint256(owner,
240
+ value,
241
+ contractAddress,
242
+ contract.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
243
+ )) as itUint256
244
+
248
245
  await expect(
249
246
  contract
250
247
  .connect(owner)
251
- ["approve(address,(uint256,bytes))"]
252
- (ZeroAddress, itValue)
253
- )
248
+ .getFunction("approve(address,((uint256,uint256),bytes))")(ZeroAddress, itValue)
249
+ )
254
250
  .to.be.revertedWithCustomError(contract, "ERC20InvalidSpender")
255
251
  .withArgs(ZeroAddress)
256
252
  })
257
253
  })
258
254
 
259
- describe("successful approval", async function () {
260
- before('approving', async function () {
261
- const itValue = await owner.encryptValue(value, contractAddress, contract["approve(address,(uint256,bytes))"].fragment.selector) as itUint
255
+ describe("successful approval", function () {
256
+ before("approving", async function () {
257
+ const itValue = (await encryptItUint256(owner,
258
+ value,
259
+ contractAddress,
260
+ contract.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
261
+ )) as itUint256
262
262
 
263
263
  const tx = await contract
264
264
  .connect(owner)
265
- ["approve(address,(uint256,bytes))"]
266
- (otherAccount.address, itValue, { gasLimit: GAS_LIMIT })
267
-
265
+ .getFunction("approve(address,((uint256,uint256),bytes))")(otherAccount.address, itValue, txOpts)
266
+
268
267
  await tx.wait()
269
268
  })
270
269
 
271
- it('increment the allowance encrypted with the owners key', async function () {
272
- const ctAllowance = await contract
273
- ["allowance(address,address)"]
274
- (owner.address, otherAccount.address)
275
-
276
- const allowance = await owner.decryptValue(ctAllowance[1])
270
+ it("stores allowance ciphertext for owner view", async function () {
271
+ const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
272
+
273
+ const allowance = decryptCtUint256(owner, ctAllowance.ownerCiphertext)
277
274
 
278
275
  await expect(allowance).to.equal(value)
279
276
  })
280
277
 
281
- it('increment the allowance encrypted with the spenders key', async function () {
282
- const ctAllowance = await contract
283
- ["allowance(address,address)"]
284
- (owner.address, otherAccount.address)
285
-
286
- const allowance = await otherAccount.decryptValue(ctAllowance[2])
278
+ it("stores allowance ciphertext for spender view", async function () {
279
+ const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
280
+
281
+ const allowance = decryptCtUint256(otherAccount, ctAllowance.spenderCiphertext)
287
282
 
288
283
  await expect(allowance).to.equal(value)
289
284
  })
290
285
  })
291
286
  })
292
287
 
293
- describe('transferFrom', function () {
288
+ describe("transferFrom", function () {
294
289
  const value = 3000n
295
290
 
296
- describe('failed transferFrom', async function () {
297
- it('rejects transferring to the zero address', async function () {
298
- const itValue = await otherAccount.encryptValue(value, contractAddress, contract["transferFrom(address,address,(uint256,bytes))"].fragment.selector)as itUint
299
-
291
+ describe("failed transferFrom", function () {
292
+ it("rejects transferring to the zero address", async function () {
293
+ const itValue = (await encryptItUint256(otherAccount,
294
+ value,
295
+ contractAddress,
296
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
297
+ )) as itUint256
298
+
300
299
  await expect(
301
300
  contract
302
301
  .connect(otherAccount)
303
- ["transferFrom(address,address,(uint256,bytes))"]
304
- (owner.address, ZeroAddress, itValue)
305
- )
302
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")
303
+ .staticCall(owner.address, ZeroAddress, itValue)
304
+ )
306
305
  .to.be.revertedWithCustomError(contract, "ERC20InvalidReceiver")
307
306
  .withArgs(ZeroAddress)
308
307
  })
309
308
 
310
- describe("transferring more than the owners balance", async function () {
311
- before("transferring", async function () {
312
- const itValue = await otherAccount.encryptValue(2n * value, contractAddress, contract["transferFrom(address,address,(uint256,bytes))"].fragment.selector)as itUint
309
+ it("rejects from zero address", async function () {
310
+ const itValue = (await encryptItUint256(otherAccount,
311
+ value,
312
+ contractAddress,
313
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
314
+ )) as itUint256
313
315
 
314
- const tx = await contract
316
+ await expect(
317
+ contract
315
318
  .connect(otherAccount)
316
- ["transferFrom(address,address,(uint256,bytes))"]
317
- (owner.address, "0x0000000000000000000000000000000000000001", itValue)
318
-
319
- await tx.wait()
320
- })
319
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")
320
+ .staticCall(ZeroAddress, otherAccount.address, itValue)
321
+ ).to.be.revertedWithCustomError(contract, "ERC20InvalidSender")
322
+ })
321
323
 
322
- it("does not decrement the owners balance", async function () {
323
- const ctBalance = await contract["balanceOf(address)"](owner.address)
324
+ it("reverts when allowance is insufficient", async function () {
325
+ const itValue = (await encryptItUint256(otherAccount,
326
+ 999999n,
327
+ contractAddress,
328
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
329
+ )) as itUint256
324
330
 
325
- const balance = await owner.decryptValue(ctBalance)
326
-
327
- await expect(balance).to.equal(5000n)
328
- })
331
+ await expectTxFails(
332
+ contract
333
+ .connect(otherAccount)
334
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
335
+ owner.address,
336
+ otherAccount.address,
337
+ itValue,
338
+ txOpts
339
+ )
340
+ )
341
+ })
342
+
343
+ it("reverts when amount exceeds balance (allowance OK)", async function () {
344
+ await contract
345
+ .connect(owner)
346
+ .getFunction("increaseAllowance(address,uint256)")(otherAccount.address, 5000n, txOpts)
347
+ .then((t) => t.wait())
329
348
 
330
- it("does not decrement the spenders allowance", async function () {
331
- const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
349
+ const itValue = (await encryptItUint256(otherAccount,
350
+ 6000n,
351
+ contractAddress,
352
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
353
+ )) as itUint256
332
354
 
333
- const allowance = await owner.decryptValue(ctAllowance[1])
334
-
335
- await expect(allowance).to.equal(5000n)
336
- })
355
+ await expectTxFails(
356
+ contract
357
+ .connect(otherAccount)
358
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
359
+ owner.address,
360
+ "0x0000000000000000000000000000000000000001",
361
+ itValue,
362
+ txOpts
363
+ )
364
+ )
337
365
  })
338
366
  })
339
367
 
340
- describe('successful transferFrom', async function () {
341
- before('transferring', async function () {
342
- const itValue = await otherAccount.encryptValue(value, contractAddress, contract["transferFrom(address,address,(uint256,bytes))"].fragment.selector)as itUint
368
+ describe("successful transferFrom", function () {
369
+ before("transferring", async function () {
370
+ await contract
371
+ .connect(owner)
372
+ .getFunction("approve(address,uint256)")(otherAccount.address, 0n, txOpts)
373
+ .then((t) => t.wait())
374
+ await contract
375
+ .connect(owner)
376
+ .getFunction("approve(address,uint256)")(otherAccount.address, 5000n, txOpts)
377
+ .then((t) => t.wait())
378
+
379
+ const itValue = (await encryptItUint256(otherAccount,
380
+ value,
381
+ contractAddress,
382
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
383
+ )) as itUint256
343
384
 
344
385
  const tx = await contract
345
386
  .connect(otherAccount)
346
- ["transferFrom(address,address,(uint256,bytes))"]
347
- (owner.address, otherAccount.address, itValue)
348
-
387
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
388
+ owner.address,
389
+ otherAccount.address,
390
+ itValue,
391
+ txOpts
392
+ )
393
+
349
394
  await tx.wait()
350
395
  })
351
396
 
352
- it('decrement the owners balance', async function () {
397
+ it("decrements the owners balance", async function () {
353
398
  const ctBalance = await contract["balanceOf(address)"](owner.address)
354
399
 
355
- const balance = await owner.decryptValue(ctBalance)
356
-
400
+ const balance = decryptCtUint256(owner, ctBalance)
401
+
357
402
  await expect(balance).to.equal(2000n)
358
403
  })
359
404
 
360
- it('increment the recipients balance', async function () {
405
+ it("increments the recipients balance", async function () {
361
406
  const ctBalance = await contract["balanceOf(address)"](otherAccount.address)
362
407
 
363
- const balance = await otherAccount.decryptValue(ctBalance)
364
-
408
+ const balance = decryptCtUint256(otherAccount, ctBalance)
409
+
365
410
  await expect(balance).to.equal(8000n)
366
411
  })
367
412
 
368
- it('decrement the spenders allowance', async function () {
413
+ it("decrements the spenders allowance", async function () {
369
414
  const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
370
415
 
371
- const allowance = await otherAccount.decryptValue(ctAllowance[2])
372
-
416
+ const allowance = decryptCtUint256(otherAccount, ctAllowance.spenderCiphertext)
417
+
373
418
  await expect(allowance).to.equal(2000n)
374
419
  })
375
420
  })
@@ -383,164 +428,960 @@ describe("Private ERC20", function () {
383
428
  before("deploying and funding wallet contract", async function () {
384
429
  const walletContractFactory = await hre.ethers.getContractFactory("PrivateERC20WalletMock")
385
430
 
386
- walletContract = await walletContractFactory
387
- .connect(owner)
388
- .deploy({ gasLimit: GAS_LIMIT })
389
-
390
- walletContract = await walletContract.waitForDeployment()
431
+ const w = await walletContractFactory.connect(owner).deploy(txOpts)
432
+ await w.waitForDeployment()
433
+ walletContract = w as unknown as PrivateERC20WalletMock
391
434
 
392
435
  walletContractAddress = await walletContract.getAddress()
393
436
 
394
- this.tx = await contract
395
- .connect(owner)
396
- .mint(walletContractAddress, value, { gasLimit: GAS_LIMIT })
397
-
398
- await this.tx.wait()
437
+ await mintPublic(contract, owner, walletContractAddress, value).then((t) => t.wait())
399
438
  })
400
439
 
401
- describe("setAccountEncryptionAddress", async function () {
440
+ describe("setAccountEncryptionAddress", function () {
402
441
  before("setting account encryption address", async function () {
403
442
  const tx = await walletContract
404
443
  .connect(otherAccount)
405
- .setAccountEncryptionAddress(contractAddress, otherAccount.address, { gasLimit: GAS_LIMIT })
406
-
444
+ .setAccountEncryptionAddress(contractAddress, otherAccount.address, txOpts)
445
+
407
446
  await tx.wait()
408
447
  })
409
448
 
410
- it("update accountEncryptionAddress mapping", async function () {
449
+ it("updates accountEncryptionAddress mapping", async function () {
411
450
  const accountEncryptionAddress = await contract.accountEncryptionAddress(walletContractAddress)
412
-
451
+
413
452
  await expect(accountEncryptionAddress).to.equal(otherAccount.address)
414
453
  })
415
454
 
416
- it("reencrypt balance using the new account encryption address", async function () {
455
+ it("reencrypts balance for the new encryption address", async function () {
417
456
  const ctBalance = await contract["balanceOf(address)"](walletContractAddress)
418
457
 
419
- const balance = await otherAccount.decryptValue(ctBalance)
458
+ const balance = decryptCtUint256(otherAccount, ctBalance)
420
459
 
421
460
  await expect(balance).to.equal(value)
422
461
  })
423
462
  })
424
463
 
425
- describe("transfer", async function () {
464
+ describe("transfer via wallet", function () {
465
+ let otherBalanceBeforeWalletTransfer = 0n
466
+
426
467
  before("transferring", async function () {
468
+ const ctBefore = await contract["balanceOf(address)"](otherAccount.address)
469
+ otherBalanceBeforeWalletTransfer = decryptCtUint256(otherAccount, ctBefore)
470
+
427
471
  const tx = await walletContract
428
472
  .connect(otherAccount)
429
- .transfer(contractAddress, otherAccount.address, value / 2n, { gasLimit: GAS_LIMIT })
430
-
473
+ .transfer(contractAddress, otherAccount.address, value / 2n, txOpts)
474
+
431
475
  await tx.wait()
432
476
  })
433
477
 
434
- it('decrement the senders balance', async function () {
478
+ it("decrements the wallet balance", async function () {
435
479
  const ctBalance = await contract["balanceOf(address)"](walletContractAddress)
436
480
 
437
- const balance = await otherAccount.decryptValue(ctBalance)
481
+ const balance = decryptCtUint256(otherAccount, ctBalance)
438
482
 
439
483
  await expect(balance).to.equal(value / 2n)
440
484
  })
441
485
 
442
- it('increment the receivers balance', async function () {
486
+ it("increments the receiver balance", async function () {
443
487
  const ctBalance = await contract["balanceOf(address)"](otherAccount.address)
444
488
 
445
- const balance = await otherAccount.decryptValue(ctBalance)
489
+ const balance = decryptCtUint256(otherAccount, ctBalance)
446
490
 
447
- await expect(balance).to.equal(13000n)
491
+ await expect(balance).to.equal(otherBalanceBeforeWalletTransfer + value / 2n)
448
492
  })
449
493
  })
450
494
 
451
- describe("approve", async function () {
495
+ describe("approve via wallet", function () {
452
496
  before("approving", async function () {
453
497
  const tx = await walletContract
454
498
  .connect(otherAccount)
455
- .approve(contractAddress, owner.address, value / 2n, { gasLimit: GAS_LIMIT })
456
-
499
+ .approve(contractAddress, owner.address, value / 2n, txOpts)
500
+
457
501
  await tx.wait()
458
502
  })
459
503
 
460
- it("increment the spenders allowance", async function () {
504
+ it("sets allowance for wallet owner/spender pair", async function () {
461
505
  const ctAllowance = await contract["allowance(address,address)"](walletContractAddress, owner.address)
462
506
 
463
- const allowance = await otherAccount.decryptValue(ctAllowance[1])
507
+ const allowance = decryptCtUint256(otherAccount, ctAllowance.ownerCiphertext)
464
508
 
465
509
  await expect(allowance).to.equal(value / 2n)
466
510
  })
467
511
  })
512
+ })
468
513
 
469
- describe("transferFrom", async function () {
470
- const value = 5000n
514
+ describe("maximum allowance", function () {
515
+ const value = 2n ** 256n - 1n
516
+ let ownerBalanceBeforeMaxTransfer = 0n
517
+ let maxAllowanceTransferFromReceipt: any
518
+
519
+ before("approve max and transferFrom", async function () {
520
+ let ct = await contract["balanceOf(address)"](owner.address)
521
+ ownerBalanceBeforeMaxTransfer = decryptCtUint256(owner, ct)
522
+ const need = 7000n
523
+ if (ownerBalanceBeforeMaxTransfer < need) {
524
+ await mintPublic(contract, owner, owner.address, need - ownerBalanceBeforeMaxTransfer).then((t) =>
525
+ t.wait()
526
+ )
527
+ ct = await contract["balanceOf(address)"](owner.address)
528
+ ownerBalanceBeforeMaxTransfer = decryptCtUint256(owner, ct)
529
+ }
530
+
531
+ await contract
532
+ .connect(owner)
533
+ .getFunction("approve(address,uint256)")(otherAccount.address, 0n, txOpts)
534
+ .then((t) => t.wait())
471
535
 
472
- before("transferring", async function () {
473
- const itValue = await otherAccount.encryptValue(value, contractAddress, contract["approve(address,(uint256,bytes))"].fragment.selector) as itUint
536
+ const itMax = (await encryptItUint256(owner,
537
+ value,
538
+ contractAddress,
539
+ contract.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
540
+ )) as itUint256
474
541
 
475
- let tx = await contract
476
- .connect(otherAccount)
477
- ["approve(address,(uint256,bytes))"]
478
- (walletContractAddress, itValue)
479
-
480
- await tx.wait()
542
+ let tx = await contract
543
+ .connect(owner)
544
+ .getFunction("approve(address,((uint256,uint256),bytes))")(otherAccount.address, itMax, txOpts)
481
545
 
482
- tx = await walletContract
483
- .connect(otherAccount)
484
- .transferFrom(contractAddress, otherAccount.address, owner.address, value)
485
-
486
- await tx.wait()
487
- })
546
+ await tx.wait()
488
547
 
489
- it('decrement the owners balance', async function () {
490
- const itBalance = await contract["balanceOf(address)"](otherAccount.address)
548
+ const itValue = (await encryptItUint256(otherAccount,
549
+ 7000n,
550
+ contractAddress,
551
+ contract.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
552
+ )) as itUint256
491
553
 
492
- const balance = await otherAccount.decryptValue(itBalance)
554
+ tx = await contract
555
+ .connect(otherAccount)
556
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
557
+ owner.address,
558
+ "0x0000000000000000000000000000000000000001",
559
+ itValue,
560
+ txOpts
561
+ )
562
+
563
+ maxAllowanceTransferFromReceipt = await tx.wait()
564
+ })
493
565
 
494
- await expect(balance).to.equal(8000n)
495
- })
566
+ it("reduces owner balance but not unlimited allowance", async function () {
567
+ const ctBalance = await contract["balanceOf(address)"](owner.address)
496
568
 
497
- it('increment the recipients balance', async function () {
498
- const itBalance = await contract["balanceOf(address)"](owner.address)
569
+ const balance = decryptCtUint256(owner, ctBalance)
499
570
 
500
- const balance = await owner.decryptValue(itBalance)
571
+ await expect(balance).to.equal(ownerBalanceBeforeMaxTransfer - 7000n)
572
+ })
501
573
 
502
- await expect(balance).to.equal(7000n)
503
- })
574
+ it("keeps max allowance for spender", async function () {
575
+ const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
576
+
577
+ const allowance = decryptCtUint256(owner, ctAllowance.ownerCiphertext)
578
+
579
+ await expect(allowance).to.equal(value)
580
+ })
581
+
582
+ it("does not emit Approval on spend when allowance is unlimited", async function () {
583
+ const approvalEv = contract.interface.getEvent("Approval")
584
+ const approvalTopic = approvalEv.topicHash
585
+ const hasApproval = maxAllowanceTransferFromReceipt.logs.some((l: any) => l.topics?.[0] === approvalTopic)
586
+ await expect(hasApproval).to.equal(false)
504
587
  })
505
588
  })
506
589
 
507
- describe("maximum allowance", function () {
508
- const value = (2n ** 64n) - 1n
590
+ describe("supplyCap boundaries (capped token)", function () {
591
+ it("enforces cap for public and encrypted mint", async function () {
592
+ const [capOwner] = await setupAccounts()
593
+ const cappedFactory = await hre.ethers.getContractFactory("PrivateERC20CappedMock")
594
+ const capped = await cappedFactory.connect(capOwner).deploy(1000n, txOpts)
595
+ await capped.waitForDeployment()
596
+ const cappedAddr = await capped.getAddress()
597
+
598
+ // grant MINTER_ROLE for both mint overloads
599
+ const minterRole = await (capped as any).MINTER_ROLE()
600
+ await (await (capped as any).grantRole(minterRole, capOwner.address, txOpts)).wait()
601
+
602
+ // public mint within cap
603
+ await (await capped.connect(capOwner).getFunction("mint(address,uint256)")(capOwner.address, 1000n, txOpts)).wait()
604
+ // public mint above cap
605
+ await expectTxFails(
606
+ capped.connect(capOwner).getFunction("mint(address,uint256)")(capOwner.address, 1n, txOpts)
607
+ )
608
+
609
+ // encrypted mint within remaining (cap already full, so minting any more should fail)
610
+ const itOne = (await encryptItUint256(
611
+ capOwner,
612
+ 1n,
613
+ cappedAddr,
614
+ (capped.interface.getFunction("mint(address,((uint256,uint256),bytes))") as any).selector
615
+ )) as itUint256
616
+ await expectTxFails(
617
+ capped.connect(capOwner).getFunction("mint(address,((uint256,uint256),bytes))")(capOwner.address, itOne, txOpts)
618
+ )
619
+ })
620
+ })
509
621
 
510
- before("transferring", async function () {
511
- let itValue = await owner.encryptValue(value, contractAddress, contract["approve(address,(uint256,bytes))"].fragment.selector) as itUint
622
+ describe("increaseAllowance and decreaseAllowance", function () {
623
+ let localContract: PrivateERC20Mock
624
+ let localAddress: string
625
+ let localOwner: Wallet
626
+ let localOther: Wallet
627
+
628
+ before(async function () {
629
+ const deployment = await deploy()
630
+ localContract = deployment.contract
631
+ localAddress = deployment.contractAddress
632
+ localOwner = deployment.owner
633
+ localOther = deployment.otherAccount
634
+ await mintPublic(localContract, localOwner, localOwner.address, 100000n).then((t) => t.wait())
635
+ })
512
636
 
513
- let tx = await contract
637
+ it("increases allowance from zero", async function () {
638
+ const add = 1234n
639
+ await localContract
640
+ .connect(localOwner)
641
+ .getFunction("increaseAllowance(address,uint256)")(localOther.address, add, txOpts)
642
+ .then((t) => t.wait())
643
+ const ctAllowance = await localContract["allowance(address,address)"](localOwner.address, localOther.address)
644
+ const allowance = decryptCtUint256(localOwner, ctAllowance.ownerCiphertext)
645
+ await expect(allowance).to.equal(add)
646
+ })
647
+
648
+ it("decreases allowance", async function () {
649
+ const sub = 234n
650
+ await localContract
651
+ .connect(localOwner)
652
+ .getFunction("decreaseAllowance(address,uint256)")(localOther.address, sub, txOpts)
653
+ .then((t) => t.wait())
654
+ const ctAllowance = await localContract["allowance(address,address)"](localOwner.address, localOther.address)
655
+ const allowance = decryptCtUint256(localOwner, ctAllowance.ownerCiphertext)
656
+ await expect(allowance).to.equal(1234n - 234n)
657
+ })
658
+
659
+ it("reverts decrease when allowance is insufficient", async function () {
660
+ await expectTxFails(
661
+ localContract
662
+ .connect(localOwner)
663
+ .getFunction("decreaseAllowance(address,uint256)")(localOther.address, 100000n, txOpts)
664
+ )
665
+ })
666
+
667
+ it("reverts increaseAllowance when addition overflows uint256", async function () {
668
+ const max = 2n ** 256n - 1n
669
+ await localContract
670
+ .connect(localOwner)
671
+ .getFunction("approve(address,uint256)")(localOther.address, 0n, txOpts)
672
+ .then((t) => t.wait())
673
+
674
+ const itMax = (await encryptItUint256(localOwner,
675
+ max,
676
+ localAddress,
677
+ localContract.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
678
+ )) as itUint256
679
+
680
+ await localContract
681
+ .connect(localOwner)
682
+ .getFunction("approve(address,((uint256,uint256),bytes))")(localOther.address, itMax, txOpts)
683
+ .then((t) => t.wait())
684
+
685
+ await expectTxFails(
686
+ localContract
687
+ .connect(localOwner)
688
+ .getFunction("increaseAllowance(address,uint256)")(localOther.address, 1n, txOpts)
689
+ )
690
+ })
691
+ })
692
+
693
+ describe("publicAmountsDisabled", function () {
694
+ let c: PrivateERC20Mock
695
+ let o: Wallet
696
+
697
+ before(async function () {
698
+ const d = await deploy()
699
+ c = d.contract
700
+ o = d.owner
701
+ await mintPublic(c, o, o.address, 1000n).then((t) => t.wait())
702
+ await c.connect(o).setPublicAmountsEnabled(false).then((t) => t.wait())
703
+ })
704
+
705
+ it("reverts public transfer", async function () {
706
+ await expect(
707
+ c.connect(o).getFunction("transfer(address,uint256)").staticCall(otherAccount.address, 1n)
708
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
709
+ })
710
+
711
+ it("reverts public approve/increase/decrease/transferFrom/burn/transferAndCall", async function () {
712
+ await expect(
713
+ c.connect(o).getFunction("approve(address,uint256)").staticCall(otherAccount.address, 1n)
714
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
715
+
716
+ await expect(
717
+ c.connect(o).getFunction("increaseAllowance(address,uint256)").staticCall(otherAccount.address, 1n)
718
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
719
+
720
+ await expect(
721
+ c.connect(o).getFunction("decreaseAllowance(address,uint256)").staticCall(otherAccount.address, 1n)
722
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
723
+
724
+ await expect(
725
+ c.connect(o)
726
+ .getFunction("transferFrom(address,address,uint256)")
727
+ .staticCall(o.address, otherAccount.address, 1n)
728
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
729
+
730
+ await expect(
731
+ c.connect(o).getFunction("burn(uint256)").staticCall(1n)
732
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
733
+
734
+ // transferAndCall(public) also blocked
735
+ const recvFactory = await hre.ethers.getContractFactory("PublicTokenReceiverMock")
736
+ const recv = await recvFactory.connect(o).deploy(txOpts)
737
+ await recv.waitForDeployment()
738
+ await expect(
739
+ c.connect(o)
740
+ .getFunction("transferAndCall(address,uint256,bytes)")
741
+ .staticCall(await recv.getAddress(), 1n, "0x")
742
+ ).to.be.revertedWithCustomError(c, "PublicAmountsDisabled")
743
+ })
744
+
745
+ after(async function () {
746
+ await c.connect(o).setPublicAmountsEnabled(true).then((t) => t.wait())
747
+ })
748
+ })
749
+
750
+ describe("transferAndCall reentrancy (encrypted)", function () {
751
+ it("prevents receiver callback re-entering token", async function () {
752
+ const d = await deploy()
753
+ const t = d.contract
754
+ const tAddr = d.contractAddress
755
+ const o = d.owner
756
+ const other = d.otherAccount
757
+
758
+ // fund owner
759
+ await mintPublic(t, o, o.address, 1000n).then((x) => x.wait())
760
+
761
+ const receiverFactory = await hre.ethers.getContractFactory("PrivateERC20ReentrantReceiverMock")
762
+ const receiver = await receiverFactory.connect(o).deploy(txOpts)
763
+ await receiver.waitForDeployment()
764
+ const receiverAddr = await receiver.getAddress()
765
+
766
+ // configure callback to attempt re-entry into approve(uint256)
767
+ const reenterData = t.interface.encodeFunctionData("approve(address,uint256)", [other.address, 1n])
768
+ await (await (receiver as any).configure(tAddr, true, reenterData, txOpts)).wait()
769
+
770
+ const itAmount = (await encryptItUint256(
771
+ o,
772
+ 1n,
773
+ tAddr,
774
+ t.interface.getFunction("transferAndCall(address,((uint256,uint256),bytes),bytes)").selector
775
+ )) as itUint256
776
+
777
+ await expectTxFails(
778
+ t.connect(o)
779
+ .getFunction("transferAndCall(address,((uint256,uint256),bytes),bytes)")(receiverAddr, itAmount, "0x", txOpts)
780
+ )
781
+ })
782
+ })
783
+
784
+ describe("more edge cases (fresh deploy)", function () {
785
+ let c: PrivateERC20Mock
786
+ let addr: string
787
+ let o: Wallet
788
+ let other: Wallet
789
+
790
+ before(async function () {
791
+ const d = await deploy()
792
+ c = d.contract
793
+ addr = d.contractAddress
794
+ o = d.owner
795
+ other = d.otherAccount
796
+ await mintPublic(c, o, o.address, 1000n).then((t) => t.wait())
797
+ })
798
+
799
+ it("public transfer exact balance moves all tokens", async function () {
800
+ const amount = 1000n
801
+ await (await c.connect(o).getFunction("transfer(address,uint256)")(other.address, amount, txOpts)).wait()
802
+ await expect(decryptCtUint256(o, await c["balanceOf(address)"](o.address))).to.equal(0n)
803
+ await expect(decryptCtUint256(other, await c["balanceOf(address)"](other.address))).to.equal(amount)
804
+ })
805
+
806
+ it("public transferFrom exact allowance reduces allowance to zero", async function () {
807
+ // reset: mint back to owner
808
+ await mintPublic(c, o, o.address, 500n).then((t) => t.wait())
809
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 123n, txOpts)).wait()
810
+
811
+ await (await c.connect(other).getFunction("transferFrom(address,address,uint256)")(o.address, other.address, 123n, txOpts)).wait()
812
+
813
+ const a = await c["allowance(address,address)"](o.address, other.address)
814
+ await expect(decryptCtUint256(other, a.spenderCiphertext)).to.equal(0n)
815
+ })
816
+
817
+ it("public approve is unsafe when current allowance non-zero", async function () {
818
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 10n, txOpts)).wait()
819
+ await expectTxFails(c.connect(o).getFunction("approve(address,uint256)")(other.address, 11n, txOpts))
820
+ })
821
+
822
+ it("public transferFrom with from==to reverts (self-transfer)", async function () {
823
+ // clear any prior allowance set by earlier tests in this block
824
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 0n, txOpts)).wait()
825
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 1n, txOpts)).wait()
826
+ await expectTxFails(
827
+ c.connect(other).getFunction("transferFrom(address,address,uint256)")(o.address, o.address, 1n, txOpts)
828
+ )
829
+ })
830
+
831
+ it("encrypted approve(0) is NOT a safe reset when current allowance non-zero", async function () {
832
+ // set non-zero allowance first
833
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 0n, txOpts)).wait()
834
+ await (await c.connect(o).getFunction("approve(address,uint256)")(other.address, 55n, txOpts)).wait()
835
+
836
+ const itZero = (await encryptItUint256(
837
+ o,
838
+ 0n,
839
+ addr,
840
+ c.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
841
+ )) as itUint256
842
+
843
+ // Contract treats "new allowance is zero" via MPC equality; depending on MPC encoding,
844
+ // an encrypted-zero may not compare equal to public zero, so this may still hit ERC20UnsafeApprove.
845
+ await expectTxFails(
846
+ c.connect(o)
847
+ .getFunction("approve(address,((uint256,uint256),bytes))")(other.address, itZero, txOpts)
848
+ )
849
+ })
850
+
851
+ it("transferAndCall(public) checks receiver must be contract (before PublicAmountsDisabled)", async function () {
852
+ await (await c.connect(o).setPublicAmountsEnabled(false).then((t) => t.wait()))
853
+ // EOA receiver => should hit TransferAndCallRequiresContract first
854
+ await expect(
855
+ c.connect(o).getFunction("transferAndCall(address,uint256,bytes)").staticCall(other.address, 1n, "0x")
856
+ ).to.be.revertedWithCustomError(c, "TransferAndCallRequiresContract")
857
+ })
858
+
859
+ it("accountEncryptionAddress(0) and allowance(address,bool) with zero address revert", async function () {
860
+ await expect(c.accountEncryptionAddress(ZeroAddress)).to.be.revertedWithCustomError(c, "ERC20InvalidReceiver")
861
+ await expect(
862
+ c.connect(o).getFunction("allowance(address,bool)").staticCall(ZeroAddress, true)
863
+ ).to.be.revertedWithCustomError(c, "ERC20InvalidReceiver")
864
+ })
865
+ })
866
+
867
+ describe("transferAndCall reentrancy (public)", function () {
868
+ it("prevents receiver callback re-entering token", async function () {
869
+ const d = await deploy()
870
+ const t = d.contract
871
+ const tAddr = d.contractAddress
872
+ const o = d.owner
873
+ const other = d.otherAccount
874
+
875
+ await mintPublic(t, o, o.address, 1000n).then((x) => x.wait())
876
+
877
+ const recvFactory = await hre.ethers.getContractFactory("PublicReentrantTokenReceiverMock")
878
+ const recv = await recvFactory.connect(o).deploy(txOpts)
879
+ await recv.waitForDeployment()
880
+ const recvAddr = await recv.getAddress()
881
+
882
+ // ensure public ops enabled for this test
883
+ await (await t.connect(o).setPublicAmountsEnabled(true).then((x) => x.wait()))
884
+
885
+ const reenterData = t.interface.encodeFunctionData("approve(address,uint256)", [other.address, 1n])
886
+ await (await (recv as any).configure(tAddr, true, reenterData, txOpts)).wait()
887
+
888
+ await expectTxFails(
889
+ t.connect(o).getFunction("transferAndCall(address,uint256,bytes)")(recvAddr, 1n, "0x", txOpts)
890
+ )
891
+ })
892
+ })
893
+
894
+ describe("missing branches: GT overloads + transferAndCall success/failure", function () {
895
+ it("covers transferAndCall(public) success and callback-false failure", async function () {
896
+ const d = await deploy()
897
+ const t = d.contract
898
+ const o = d.owner
899
+
900
+ await mintPublic(t, o, o.address, 10n).then((x) => x.wait())
901
+
902
+ const okRecvF = await hre.ethers.getContractFactory("PublicTokenReceiverBoolMock")
903
+ const okRecv = await okRecvF.connect(o).deploy(true, txOpts)
904
+ await okRecv.waitForDeployment()
905
+
906
+ const badRecv = await okRecvF.connect(o).deploy(false, txOpts)
907
+ await badRecv.waitForDeployment()
908
+
909
+ await (await t.connect(o).getFunction("transferAndCall(address,uint256,bytes)")(await okRecv.getAddress(), 1n, "0x", txOpts)).wait()
910
+ await expectTxFails(
911
+ t.connect(o).getFunction("transferAndCall(address,uint256,bytes)")(await badRecv.getAddress(), 1n, "0x", txOpts)
912
+ )
913
+ })
914
+
915
+ it("covers transferAndCall(encrypted) success and callback-false failure", async function () {
916
+ const d = await deploy()
917
+ const t = d.contract
918
+ const tAddr = d.contractAddress
919
+ const o = d.owner
920
+
921
+ await mintPublic(t, o, o.address, 10n).then((x) => x.wait())
922
+
923
+ const recvF = await hre.ethers.getContractFactory("EncryptedTokenReceiverMock")
924
+ const okRecv = await recvF.connect(o).deploy(true, txOpts)
925
+ await okRecv.waitForDeployment()
926
+ const badRecv = await recvF.connect(o).deploy(false, txOpts)
927
+ await badRecv.waitForDeployment()
928
+
929
+ const itAmt = (await encryptItUint256(
930
+ o,
931
+ 1n,
932
+ tAddr,
933
+ t.interface.getFunction("transferAndCall(address,((uint256,uint256),bytes),bytes)").selector
934
+ )) as itUint256
935
+
936
+ await (await t.connect(o).getFunction("transferAndCall(address,((uint256,uint256),bytes),bytes)")(await okRecv.getAddress(), itAmt, "0x", txOpts)).wait()
937
+ await expectTxFails(
938
+ t.connect(o).getFunction("transferAndCall(address,((uint256,uint256),bytes),bytes)")(await badRecv.getAddress(), itAmt, "0x", txOpts)
939
+ )
940
+ })
941
+
942
+ it("covers GT entrypoints via helper contract", async function () {
943
+ const d = await deploy()
944
+ const t = d.contract
945
+ const tAddr = d.contractAddress
946
+ const o = d.owner
947
+ const other = d.otherAccount
948
+
949
+ await mintPublic(t, o, o.address, 1000n).then((x) => x.wait())
950
+
951
+ const callerF = await hre.ethers.getContractFactory("PrivateERC20GtCallerMock")
952
+ const caller = await callerF.connect(o).deploy(txOpts)
953
+ await caller.waitForDeployment()
954
+ const callerAddr = await caller.getAddress()
955
+
956
+ // fund caller so it can burnGt and transferGT as msg.sender
957
+ await (await t.connect(o).getFunction("transfer(address,uint256)")(callerAddr, 10n, txOpts)).wait()
958
+
959
+ // transferGT from caller to other
960
+ await (await (caller as any).transferGT(tAddr, other.address, 1n, txOpts)).wait()
961
+ await expect(decryptCtUint256(other, await t["balanceOf(address)"](other.address))).to.be.greaterThan(0n)
962
+
963
+ // approveGT from caller (owner=caller) to itself (spender=caller), then transferFromGT (spender=caller)
964
+ await (await (caller as any).approveGT(tAddr, callerAddr, 5n, txOpts)).wait()
965
+ await (await (caller as any).transferFromGT(tAddr, callerAddr, other.address, 2n, txOpts)).wait()
966
+
967
+ // burnGt from caller
968
+ await (await (caller as any).burnGt(tAddr, 1n, txOpts)).wait()
969
+ })
970
+ })
971
+
972
+ describe("reencryptAllowance event", function () {
973
+ it("emits AllowanceReencrypted", async function () {
974
+ await contract
514
975
  .connect(owner)
515
- ["approve(address,(uint256,bytes))"]
516
- (otherAccount.address, itValue)
517
-
518
- await tx.wait()
976
+ .getFunction("approve(address,uint256)")(otherAccount.address, 0n, txOpts)
977
+ .then((t) => t.wait())
519
978
 
520
- itValue = await otherAccount.encryptValue(7000n, contractAddress, contract["transferFrom(address,address,(uint256,bytes))"].fragment.selector) as itUint
979
+ const it42 = (await encryptItUint256(owner,
980
+ 42n,
981
+ contractAddress,
982
+ contract.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
983
+ )) as itUint256
521
984
 
522
- tx = await contract
523
- .connect(otherAccount)
524
- ["transferFrom(address,address,(uint256,bytes))"]
525
- (owner.address, "0x0000000000000000000000000000000000000001", itValue)
526
-
527
- await tx.wait()
985
+ await contract
986
+ .connect(owner)
987
+ .getFunction("approve(address,((uint256,uint256),bytes))")(otherAccount.address, it42, txOpts)
988
+ .then((t) => t.wait())
989
+
990
+ const txResp = await contract.connect(owner).reencryptAllowance(otherAccount.address, false, txOpts)
991
+ const receipt = await txResp.wait()
992
+ expect(receipt).to.not.be.null
993
+ expect(receipt!.status).to.equal(1)
994
+ const ev = contract.interface.getEvent("AllowanceReencrypted")
995
+ expect(receipt!.logs.some((l) => l.topics[0] === ev.topicHash)).to.equal(true)
528
996
  })
997
+ })
529
998
 
530
- it("decrement the owners balance", async function () {
531
- const ctBalance = await contract["balanceOf(address)"](owner.address)
999
+ describe("encrypted edge cases (fresh deploy)", function () {
1000
+ let c: PrivateERC20Mock
1001
+ let addr: string
1002
+ let o: Wallet
1003
+ let other: Wallet
532
1004
 
533
- const balance = await owner.decryptValue(ctBalance)
1005
+ before(async function () {
1006
+ const d = await deploy()
1007
+ c = d.contract
1008
+ addr = d.contractAddress
1009
+ o = d.owner
1010
+ other = d.otherAccount
534
1011
 
535
- await expect(balance).to.equal(0n)
1012
+ await mintPublic(c, o, o.address, 1000n).then((t) => t.wait())
536
1013
  })
537
1014
 
538
- it("does not decrement the spenders allowance", async function () {
539
- const ctAllowance = await contract["allowance(address,address)"](owner.address, otherAccount.address)
1015
+ it("transfer encrypted 0n keeps balances unchanged", async function () {
1016
+ const ctFromBefore = await c["balanceOf(address)"](o.address)
1017
+ const ctToBefore = await c["balanceOf(address)"](other.address)
540
1018
 
541
- const allowance = await owner.decryptValue(ctAllowance[1])
1019
+ const fromBefore = decryptCtUint256(o, ctFromBefore)
1020
+ const toBefore = decryptCtUint256(other, ctToBefore)
542
1021
 
543
- await expect(allowance).to.equal(value)
1022
+ const itZero = (await encryptItUint256(
1023
+ o,
1024
+ 0n,
1025
+ addr,
1026
+ c.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
1027
+ )) as itUint256
1028
+
1029
+ await (
1030
+ await c
1031
+ .connect(o)
1032
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(other.address, itZero, txOpts)
1033
+ ).wait()
1034
+
1035
+ const ctFromAfter = await c["balanceOf(address)"](o.address)
1036
+ const ctToAfter = await c["balanceOf(address)"](other.address)
1037
+
1038
+ const fromAfter = decryptCtUint256(o, ctFromAfter)
1039
+ const toAfter = decryptCtUint256(other, ctToAfter)
1040
+
1041
+ await expect(fromAfter).to.equal(fromBefore)
1042
+ await expect(toAfter).to.equal(toBefore)
1043
+ })
1044
+
1045
+ it("transfer encrypted exact balance moves all tokens", async function () {
1046
+ const value = 1000n
1047
+
1048
+ const itValue = (await encryptItUint256(
1049
+ o,
1050
+ value,
1051
+ addr,
1052
+ c.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
1053
+ )) as itUint256
1054
+
1055
+ await (
1056
+ await c
1057
+ .connect(o)
1058
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(other.address, itValue, txOpts)
1059
+ ).wait()
1060
+
1061
+ const ctFrom = await c["balanceOf(address)"](o.address)
1062
+ const ctTo = await c["balanceOf(address)"](other.address)
1063
+
1064
+ await expect(decryptCtUint256(o, ctFrom)).to.equal(0n)
1065
+ await expect(decryptCtUint256(other, ctTo)).to.equal(value)
1066
+ })
1067
+
1068
+ it("approve encrypted 0 then transferFrom encrypted 0 does not change state", async function () {
1069
+ // reset: mint back for this test sequence
1070
+ // (this block runs after the previous test, which zeroed o's balance)
1071
+ await mintPublic(c, o, o.address, 1000n).then((t) => t.wait())
1072
+ await mintPublic(c, o, other.address, 0n).then((t) => t.wait())
1073
+
1074
+ const zero = 0n
1075
+ const itZeroApprove = (await encryptItUint256(
1076
+ o,
1077
+ zero,
1078
+ addr,
1079
+ c.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
1080
+ )) as itUint256
1081
+
1082
+ await (
1083
+ await c
1084
+ .connect(o)
1085
+ .getFunction("approve(address,((uint256,uint256),bytes))")(other.address, itZeroApprove, txOpts)
1086
+ ).wait()
1087
+
1088
+ const itZeroTransferFrom = (await encryptItUint256(
1089
+ other,
1090
+ zero,
1091
+ addr,
1092
+ c.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
1093
+ )) as itUint256
1094
+
1095
+ const fromBefore = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1096
+ const toBefore = decryptCtUint256(other, await c["balanceOf(address)"](other.address))
1097
+
1098
+ await (
1099
+ await c
1100
+ .connect(other)
1101
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
1102
+ o.address,
1103
+ other.address,
1104
+ itZeroTransferFrom,
1105
+ txOpts
1106
+ )
1107
+ ).wait()
1108
+
1109
+ const fromAfter = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1110
+ const toAfter = decryptCtUint256(other, await c["balanceOf(address)"](other.address))
1111
+
1112
+ await expect(fromAfter).to.equal(fromBefore)
1113
+ await expect(toAfter).to.equal(toBefore)
1114
+
1115
+ const ctAllowance = await c["allowance(address,address)"](o.address, other.address)
1116
+ const allowanceSpenderView = decryptCtUint256(other, ctAllowance.spenderCiphertext)
1117
+ await expect(allowanceSpenderView).to.equal(0n)
1118
+ })
1119
+
1120
+ it("transferFrom encrypted exact allowance reduces allowance to zero", async function () {
1121
+ // reset: mint back and set a known allowance
1122
+ await mintPublic(c, o, o.address, 1000n).then((t) => t.wait())
1123
+
1124
+ const allow = 250n
1125
+ await (
1126
+ await c
1127
+ .connect(o)
1128
+ .getFunction("approve(address,uint256)")(other.address, allow, txOpts)
1129
+ ).wait()
1130
+
1131
+ const itAllow = (await encryptItUint256(
1132
+ other,
1133
+ allow,
1134
+ addr,
1135
+ c.interface.getFunction("transferFrom(address,address,((uint256,uint256),bytes))").selector
1136
+ )) as itUint256
1137
+
1138
+ await (
1139
+ await c
1140
+ .connect(other)
1141
+ .getFunction("transferFrom(address,address,((uint256,uint256),bytes))")(
1142
+ o.address,
1143
+ other.address,
1144
+ itAllow,
1145
+ txOpts
1146
+ )
1147
+ ).wait()
1148
+
1149
+ const ctFrom = await c["balanceOf(address)"](o.address)
1150
+ const ctTo = await c["balanceOf(address)"](other.address)
1151
+
1152
+ // owner was >= 250 due to minting above; exact expected amounts depend on prior tests
1153
+ await expect(decryptCtUint256(other, ctTo)).to.be.greaterThan(0n)
1154
+
1155
+ const ctAllowance = await c["allowance(address,address)"](o.address, other.address)
1156
+ const allowanceSpenderView = decryptCtUint256(other, ctAllowance.spenderCiphertext)
1157
+ await expect(allowanceSpenderView).to.equal(0n)
1158
+ })
1159
+
1160
+ it("increaseAllowance encrypted 0n is a no-op", async function () {
1161
+ // Ensure allowance starts from 0 by approving 0 publicly.
1162
+ await (
1163
+ await c.connect(o).getFunction("approve(address,uint256)")(other.address, 0n, txOpts)
1164
+ ).wait()
1165
+
1166
+ const itZero = (await encryptItUint256(
1167
+ o,
1168
+ 0n,
1169
+ addr,
1170
+ c.interface.getFunction("increaseAllowance(address,((uint256,uint256),bytes))").selector
1171
+ )) as itUint256
1172
+
1173
+ await (
1174
+ await c
1175
+ .connect(o)
1176
+ .getFunction("increaseAllowance(address,((uint256,uint256),bytes))")(other.address, itZero, txOpts)
1177
+ ).wait()
1178
+
1179
+ const ctAllowance = await c["allowance(address,address)"](o.address, other.address)
1180
+ const allowanceOwnerView = decryptCtUint256(o, ctAllowance.ownerCiphertext)
1181
+ await expect(allowanceOwnerView).to.equal(0n)
1182
+ })
1183
+
1184
+ it("decreaseAllowance encrypted exact current allowance reduces to zero", async function () {
1185
+ const current = 123n
1186
+ await (
1187
+ await c
1188
+ .connect(o)
1189
+ .getFunction("approve(address,uint256)")(other.address, current, txOpts)
1190
+ ).wait()
1191
+
1192
+ const itSub = (await encryptItUint256(
1193
+ o,
1194
+ current,
1195
+ addr,
1196
+ c.interface.getFunction("decreaseAllowance(address,((uint256,uint256),bytes))").selector
1197
+ )) as itUint256
1198
+
1199
+ await (
1200
+ await c
1201
+ .connect(o)
1202
+ .getFunction("decreaseAllowance(address,((uint256,uint256),bytes))")(other.address, itSub, txOpts)
1203
+ ).wait()
1204
+
1205
+ const ctAllowance = await c["allowance(address,address)"](o.address, other.address)
1206
+ const allowanceOwnerView = decryptCtUint256(o, ctAllowance.ownerCiphertext)
1207
+ await expect(allowanceOwnerView).to.equal(0n)
1208
+ })
1209
+
1210
+ it("encrypted mint/burn accept 0n and reject malformed itUint256", async function () {
1211
+ const fromBefore = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1212
+
1213
+ // PrivateERC20Mock only bypasses MINTER_ROLE for the public mint overload.
1214
+ // The encrypted mint overload still requires MINTER_ROLE, so grant it here.
1215
+ await (
1216
+ await c.connect(o).grantRole(await c.MINTER_ROLE(), o.address)
1217
+ ).wait()
1218
+
1219
+ const itZeroMint = (await encryptItUint256(
1220
+ o,
1221
+ 0n,
1222
+ addr,
1223
+ c.interface.getFunction("mint(address,((uint256,uint256),bytes))").selector
1224
+ )) as itUint256
1225
+
1226
+ await (
1227
+ await c
1228
+ .connect(o)
1229
+ .getFunction("mint(address,((uint256,uint256),bytes))")(o.address, itZeroMint, txOpts)
1230
+ ).wait()
1231
+
1232
+ const itZeroBurn = (await encryptItUint256(
1233
+ o,
1234
+ 0n,
1235
+ addr,
1236
+ c.interface.getFunction("burn(((uint256,uint256),bytes))").selector
1237
+ )) as itUint256
1238
+
1239
+ await (
1240
+ await c
1241
+ .connect(o)
1242
+ .getFunction("burn(((uint256,uint256),bytes))")(itZeroBurn, txOpts)
1243
+ ).wait()
1244
+
1245
+ const fromAfter = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1246
+ await expect(fromAfter).to.equal(fromBefore)
1247
+
1248
+ const goodMint = (await encryptItUint256(
1249
+ o,
1250
+ 1n,
1251
+ addr,
1252
+ c.interface.getFunction("mint(address,((uint256,uint256),bytes))").selector
1253
+ )) as itUint256
1254
+
1255
+ const badSig: itUint256 = {
1256
+ ciphertext: goodMint.ciphertext,
1257
+ signature: new Uint8Array()
1258
+ }
1259
+
1260
+ await expectTxFails(
1261
+ c.connect(o)
1262
+ .getFunction("mint(address,((uint256,uint256),bytes))")(o.address, badSig, txOpts)
1263
+ )
1264
+ })
1265
+
1266
+ it("encrypted operations still work when publicAmountsEnabled=false", async function () {
1267
+ await (
1268
+ await c.connect(o).setPublicAmountsEnabled(false, txOpts)
1269
+ ).wait()
1270
+
1271
+ const value = 10n
1272
+ const itValue = (await encryptItUint256(
1273
+ o,
1274
+ value,
1275
+ addr,
1276
+ c.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
1277
+ )) as itUint256
1278
+
1279
+ const fromBefore = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1280
+ const toBefore = decryptCtUint256(other, await c["balanceOf(address)"](other.address))
1281
+
1282
+ await (
1283
+ await c
1284
+ .connect(o)
1285
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(other.address, itValue, txOpts)
1286
+ ).wait()
1287
+
1288
+ const fromAfter = decryptCtUint256(o, await c["balanceOf(address)"](o.address))
1289
+ const toAfter = decryptCtUint256(other, await c["balanceOf(address)"](other.address))
1290
+
1291
+ await expect(fromAfter).to.equal(fromBefore - value)
1292
+ await expect(toAfter).to.equal(toBefore + value)
1293
+ })
1294
+
1295
+ it("rejects malformed itUint256: bad signature and swapped ciphertext limbs", async function () {
1296
+ const good = (await encryptItUint256(
1297
+ o,
1298
+ 1n,
1299
+ addr,
1300
+ c.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
1301
+ )) as itUint256
1302
+
1303
+ const badSig: itUint256 = {
1304
+ ciphertext: good.ciphertext,
1305
+ signature: new Uint8Array()
1306
+ }
1307
+
1308
+ const swapped: itUint256 = {
1309
+ ciphertext: {
1310
+ ciphertextHigh: good.ciphertext.ciphertextLow,
1311
+ ciphertextLow: good.ciphertext.ciphertextHigh
1312
+ },
1313
+ signature: good.signature
1314
+ }
1315
+
1316
+ await expectTxFails(
1317
+ c.connect(o)
1318
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(other.address, badSig, txOpts)
1319
+ )
1320
+
1321
+ await expectTxFails(
1322
+ c.connect(o)
1323
+ .getFunction("transfer(address,((uint256,uint256),bytes))")(other.address, swapped, txOpts)
1324
+ )
1325
+ })
1326
+ })
1327
+
1328
+ describe("isolated deploy: self-transfer and unsafe approve", function () {
1329
+ let iso: PrivateERC20Mock
1330
+ let isoAddr: string
1331
+ let o: Wallet
1332
+ let other: Wallet
1333
+
1334
+ before(async function () {
1335
+ const d = await deploy()
1336
+ iso = d.contract
1337
+ isoAddr = d.contractAddress
1338
+ o = d.owner
1339
+ other = d.otherAccount
1340
+ await mintPublic(iso, o, o.address, 1000n).then((t) => t.wait())
1341
+ })
1342
+
1343
+ it("reverts self-transfer", async function () {
1344
+ const itValue = (await encryptItUint256(o,
1345
+ 1n,
1346
+ isoAddr,
1347
+ iso.interface.getFunction("transfer(address,((uint256,uint256),bytes))").selector
1348
+ )) as itUint256
1349
+
1350
+ await expectTxFails(
1351
+ iso.connect(o).getFunction("transfer(address,((uint256,uint256),bytes))")(o.address, itValue, txOpts)
1352
+ )
1353
+ })
1354
+
1355
+ it("reverts unsafe approve then clears with approve zero", async function () {
1356
+ const a1 = (await encryptItUint256(o,
1357
+ 100n,
1358
+ isoAddr,
1359
+ iso.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
1360
+ )) as itUint256
1361
+ await iso
1362
+ .connect(o)
1363
+ .getFunction("approve(address,((uint256,uint256),bytes))")(other.address, a1, txOpts)
1364
+ .then((t) => t.wait())
1365
+
1366
+ const a2 = (await encryptItUint256(o,
1367
+ 200n,
1368
+ isoAddr,
1369
+ iso.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
1370
+ )) as itUint256
1371
+
1372
+ await expectTxFails(
1373
+ iso.connect(o).getFunction("approve(address,((uint256,uint256),bytes))")(other.address, a2, txOpts)
1374
+ )
1375
+
1376
+ const z = (await encryptItUint256(o,
1377
+ 0n,
1378
+ isoAddr,
1379
+ iso.interface.getFunction("approve(address,((uint256,uint256),bytes))").selector
1380
+ )) as itUint256
1381
+ await iso
1382
+ .connect(o)
1383
+ .getFunction("approve(address,((uint256,uint256),bytes))")(other.address, z, txOpts)
1384
+ .then((t) => t.wait())
544
1385
  })
545
1386
  })
546
1387
  })