@keep-network/tbtc-v2 0.1.0 → 0.1.1-dev

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 (296) hide show
  1. package/README.adoc +12 -0
  2. package/artifacts/.chainId +1 -1
  3. package/artifacts/Bank.json +807 -0
  4. package/artifacts/Bridge.json +2300 -0
  5. package/artifacts/Deposit.json +117 -0
  6. package/artifacts/DepositSweep.json +77 -0
  7. package/artifacts/EcdsaDkgValidator.json +532 -0
  8. package/artifacts/EcdsaInactivity.json +156 -0
  9. package/artifacts/EcdsaSortitionPool.json +1004 -0
  10. package/artifacts/Fraud.json +164 -0
  11. package/artifacts/KeepRegistry.json +99 -0
  12. package/artifacts/KeepStake.json +286 -0
  13. package/artifacts/KeepToken.json +711 -0
  14. package/artifacts/KeepTokenStaking.json +483 -0
  15. package/artifacts/MovingFunds.json +249 -0
  16. package/artifacts/NuCypherStakingEscrow.json +256 -0
  17. package/artifacts/NuCypherToken.json +711 -0
  18. package/artifacts/RandomBeaconStub.json +141 -0
  19. package/artifacts/Redemption.json +174 -0
  20. package/artifacts/ReimbursementPool.json +509 -0
  21. package/artifacts/Relay.json +123 -0
  22. package/artifacts/T.json +1148 -0
  23. package/artifacts/TBTC.json +36 -35
  24. package/artifacts/TBTCToken.json +738 -0
  25. package/artifacts/TBTCVault.json +691 -0
  26. package/artifacts/TokenStaking.json +2288 -0
  27. package/artifacts/TokenholderGovernor.json +1795 -0
  28. package/artifacts/TokenholderTimelock.json +1058 -0
  29. package/artifacts/VendingMachine.json +34 -33
  30. package/artifacts/VendingMachineKeep.json +400 -0
  31. package/artifacts/VendingMachineNuCypher.json +400 -0
  32. package/artifacts/WalletRegistry.json +1843 -0
  33. package/artifacts/WalletRegistryGovernance.json +2754 -0
  34. package/artifacts/Wallets.json +186 -0
  35. package/artifacts/solcInputs/5e62cff1ead0900b07facca4b559e818.json +314 -0
  36. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  37. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
  38. package/build/contracts/bank/Bank.sol/Bank.dbg.json +4 -0
  39. package/build/contracts/bank/Bank.sol/Bank.json +542 -0
  40. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +4 -0
  41. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.json +34 -0
  42. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
  43. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
  44. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +4 -0
  45. package/build/contracts/bridge/Bridge.sol/Bridge.json +2686 -0
  46. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
  47. package/build/contracts/bridge/BridgeState.sol/BridgeState.json +226 -0
  48. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
  49. package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
  50. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +4 -0
  51. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +30 -0
  52. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
  53. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
  54. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
  55. package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
  56. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +4 -0
  57. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.json +10 -0
  58. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
  59. package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
  60. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
  61. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +138 -0
  62. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
  63. package/build/contracts/bridge/Redemption.sol/OutboundTx.json +10 -0
  64. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
  65. package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
  66. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  67. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
  68. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
  69. package/build/contracts/bridge/Wallets.sol/Wallets.json +112 -0
  70. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  71. package/build/contracts/token/TBTC.sol/TBTC.json +4 -4
  72. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +4 -0
  73. package/build/contracts/vault/DonationVault.sol/DonationVault.json +108 -0
  74. package/build/contracts/vault/IVault.sol/IVault.dbg.json +4 -0
  75. package/build/contracts/vault/IVault.sol/IVault.json +52 -0
  76. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +4 -0
  77. package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +449 -0
  78. package/contracts/GovernanceUtils.sol +4 -4
  79. package/contracts/bank/Bank.sol +436 -0
  80. package/contracts/bank/IReceiveBalanceApproval.sol +45 -0
  81. package/contracts/bridge/BitcoinTx.sol +326 -0
  82. package/contracts/bridge/Bridge.sol +1793 -0
  83. package/contracts/bridge/BridgeState.sol +739 -0
  84. package/contracts/bridge/Deposit.sol +269 -0
  85. package/contracts/bridge/DepositSweep.sol +574 -0
  86. package/contracts/bridge/EcdsaLib.sol +45 -0
  87. package/contracts/bridge/Fraud.sol +579 -0
  88. package/contracts/bridge/Heartbeat.sol +112 -0
  89. package/contracts/bridge/IRelay.sol +28 -0
  90. package/contracts/bridge/MovingFunds.sol +1077 -0
  91. package/contracts/bridge/Redemption.sol +1020 -0
  92. package/contracts/bridge/VendingMachine.sol +2 -2
  93. package/contracts/bridge/Wallets.sol +719 -0
  94. package/contracts/hardhat-dependency-compiler/.hardhat-dependency-compiler +1 -0
  95. package/contracts/hardhat-dependency-compiler/@keep-network/ecdsa/contracts/WalletRegistry.sol +3 -0
  96. package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol +3 -0
  97. package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +3 -0
  98. package/contracts/token/TBTC.sol +1 -1
  99. package/contracts/vault/DonationVault.sol +125 -0
  100. package/contracts/vault/IVault.sol +44 -0
  101. package/contracts/vault/TBTCVault.sol +305 -0
  102. package/deploy/00_resolve_relay.ts +28 -0
  103. package/deploy/00_resolve_tbtc_v1_token.ts +1 -1
  104. package/deploy/01_deploy_tbtc_v2_token.ts +8 -1
  105. package/deploy/02_deploy_vending_machine.ts +7 -0
  106. package/deploy/{03_transfer_roles.ts → 03_transfer_vending_machine_roles.ts} +1 -1
  107. package/deploy/04_deploy_bank.ts +27 -0
  108. package/deploy/05_deploy_bridge.ts +80 -0
  109. package/deploy/06_deploy_tbtc_vault.ts +30 -0
  110. package/deploy/07_bank_update_bridge.ts +19 -0
  111. package/deploy/08_transfer_bank_ownership.ts +15 -0
  112. package/deploy/09_transfer_tbtc_vault_ownership.ts +15 -0
  113. package/deploy/10_transfer_bridge_governance.ts +20 -0
  114. package/deploy/11_initialize_wallet_owner.ts +18 -0
  115. package/deploy/11_transfer_proxy_admin_ownership.ts +30 -0
  116. package/deploy/12_deploy_proxy_admin_with_deputy.ts +33 -0
  117. package/export/deploy/00_resolve_relay.js +24 -0
  118. package/export/deploy/00_resolve_tbtc_v1_token.js +24 -0
  119. package/export/deploy/01_deploy_tbtc_v2_token.js +19 -0
  120. package/export/deploy/02_deploy_vending_machine.js +25 -0
  121. package/export/deploy/03_transfer_vending_machine_roles.js +19 -0
  122. package/export/deploy/04_deploy_bank.js +21 -0
  123. package/export/deploy/05_deploy_bridge.js +69 -0
  124. package/export/deploy/06_deploy_tbtc_vault.js +24 -0
  125. package/export/deploy/07_bank_update_bridge.js +13 -0
  126. package/export/deploy/08_transfer_bank_ownership.js +11 -0
  127. package/export/deploy/09_transfer_tbtc_vault_ownership.js +11 -0
  128. package/export/deploy/10_transfer_bridge_governance.js +11 -0
  129. package/export/deploy/11_initialize_wallet_owner.js +14 -0
  130. package/export/deploy/11_transfer_proxy_admin_ownership.js +23 -0
  131. package/export/deploy/12_deploy_proxy_admin_with_deputy.js +22 -0
  132. package/export/hardhat.config.js +169 -0
  133. package/export/test/bank/Bank.test.js +1012 -0
  134. package/export/test/bridge/Bridge.Deployment.test.js +76 -0
  135. package/export/test/bridge/Bridge.Deposit.test.js +1834 -0
  136. package/export/test/bridge/Bridge.Frauds.test.js +1349 -0
  137. package/export/test/bridge/Bridge.MovingFunds.test.js +2437 -0
  138. package/export/test/bridge/Bridge.Parameters.test.js +400 -0
  139. package/export/test/bridge/Bridge.Redemption.test.js +2523 -0
  140. package/export/test/bridge/Bridge.Vaults.test.js +74 -0
  141. package/export/test/bridge/Bridge.Wallets.test.js +1017 -0
  142. package/export/test/bridge/EcdsaLib.test.js +46 -0
  143. package/export/test/bridge/Heartbeat.test.js +77 -0
  144. package/export/test/bridge/VendingMachine.Upgrade.test.js +160 -0
  145. package/export/test/bridge/VendingMachine.test.js +762 -0
  146. package/export/test/data/deposit-sweep.js +655 -0
  147. package/export/test/data/ecdsa.js +18 -0
  148. package/export/test/data/fraud.js +158 -0
  149. package/export/test/data/moving-funds.js +815 -0
  150. package/export/test/data/redemption.js +1011 -0
  151. package/export/test/fixtures/bridge.js +54 -0
  152. package/export/test/fixtures/index.js +57 -0
  153. package/export/test/helpers/contract-test-helpers.js +18 -0
  154. package/export/test/integration/Slashing.test.js +279 -0
  155. package/export/test/integration/WalleCreation.test.js +66 -0
  156. package/export/test/integration/utils/ecdsa-wallet-registry.js +137 -0
  157. package/export/test/integration/utils/fixture.js +77 -0
  158. package/export/test/integration/utils/gas.js +36 -0
  159. package/export/test/integration/utils/random-beacon.js +26 -0
  160. package/export/test/integration/utils/staking.js +19 -0
  161. package/export/test/vault/DonationVault.test.js +202 -0
  162. package/export/test/vault/TBTCVault.Redemption.test.js +357 -0
  163. package/export/test/vault/TBTCVault.test.js +768 -0
  164. package/export/typechain/BTCUtils.js +2 -0
  165. package/export/typechain/Bank.js +2 -0
  166. package/export/typechain/BankStub.js +2 -0
  167. package/export/typechain/Bridge.js +2 -0
  168. package/export/typechain/BridgeState.js +2 -0
  169. package/export/typechain/BridgeStub.js +2 -0
  170. package/export/typechain/Deposit.js +2 -0
  171. package/export/typechain/DepositSweep.js +2 -0
  172. package/export/typechain/DonationVault.js +2 -0
  173. package/export/typechain/ERC165.js +2 -0
  174. package/export/typechain/ERC1967Proxy.js +2 -0
  175. package/export/typechain/ERC1967Upgrade.js +2 -0
  176. package/export/typechain/ERC20WithPermit.js +2 -0
  177. package/export/typechain/ERC721.js +2 -0
  178. package/export/typechain/EcdsaAuthorization.js +2 -0
  179. package/export/typechain/EcdsaDkg.js +2 -0
  180. package/export/typechain/EcdsaDkgValidator.js +2 -0
  181. package/export/typechain/EcdsaInactivity.js +2 -0
  182. package/export/typechain/Fraud.js +2 -0
  183. package/export/typechain/Governable.js +2 -0
  184. package/export/typechain/HeartbeatStub.js +2 -0
  185. package/export/typechain/IApplication.js +2 -0
  186. package/export/typechain/IApproveAndCall.js +2 -0
  187. package/export/typechain/IBeacon.js +2 -0
  188. package/export/typechain/IERC165.js +2 -0
  189. package/export/typechain/IERC1822Proxiable.js +2 -0
  190. package/export/typechain/IERC20.js +2 -0
  191. package/export/typechain/IERC20Metadata.js +2 -0
  192. package/export/typechain/IERC20WithPermit.js +2 -0
  193. package/export/typechain/IERC721.js +2 -0
  194. package/export/typechain/IERC721Metadata.js +2 -0
  195. package/export/typechain/IERC721Receiver.js +2 -0
  196. package/export/typechain/IRandomBeacon.js +2 -0
  197. package/export/typechain/IRandomBeaconConsumer.js +2 -0
  198. package/export/typechain/IReceiveApproval.js +2 -0
  199. package/export/typechain/IReceiveBalanceApproval.js +2 -0
  200. package/export/typechain/IRelay.js +2 -0
  201. package/export/typechain/IStaking.js +2 -0
  202. package/export/typechain/IVault.js +2 -0
  203. package/export/typechain/IWalletOwner.js +2 -0
  204. package/export/typechain/IWalletRegistry.js +2 -0
  205. package/export/typechain/Initializable.js +2 -0
  206. package/export/typechain/MisfundRecovery.js +2 -0
  207. package/export/typechain/MovingFunds.js +2 -0
  208. package/export/typechain/Ownable.js +2 -0
  209. package/export/typechain/Proxy.js +2 -0
  210. package/export/typechain/ProxyAdmin.js +2 -0
  211. package/export/typechain/ReceiveApprovalStub.js +2 -0
  212. package/export/typechain/Redemption.js +2 -0
  213. package/export/typechain/Reimbursable.js +2 -0
  214. package/export/typechain/ReimbursementPool.js +2 -0
  215. package/export/typechain/Rewards.js +2 -0
  216. package/export/typechain/SortitionPool.js +2 -0
  217. package/export/typechain/SortitionTree.js +2 -0
  218. package/export/typechain/TBTC.js +2 -0
  219. package/export/typechain/TBTCVault.js +2 -0
  220. package/export/typechain/TestERC20.js +2 -0
  221. package/export/typechain/TestERC721.js +2 -0
  222. package/export/typechain/TestEcdsaLib.js +2 -0
  223. package/export/typechain/TestRelay.js +2 -0
  224. package/export/typechain/TransparentUpgradeableProxy.js +2 -0
  225. package/export/typechain/VendingMachine.js +2 -0
  226. package/export/typechain/WalletRegistry.js +2 -0
  227. package/export/typechain/Wallets.js +2 -0
  228. package/export/typechain/common.js +2 -0
  229. package/export/typechain/factories/BTCUtils__factory.js +94 -0
  230. package/export/typechain/factories/BankStub__factory.js +586 -0
  231. package/export/typechain/factories/Bank__factory.js +573 -0
  232. package/export/typechain/factories/BridgeState__factory.js +257 -0
  233. package/export/typechain/factories/BridgeStub__factory.js +2912 -0
  234. package/export/typechain/factories/Bridge__factory.js +2526 -0
  235. package/export/typechain/factories/DepositSweep__factory.js +61 -0
  236. package/export/typechain/factories/Deposit__factory.js +103 -0
  237. package/export/typechain/factories/DonationVault__factory.js +139 -0
  238. package/export/typechain/factories/ERC165__factory.js +38 -0
  239. package/export/typechain/factories/ERC1967Proxy__factory.js +111 -0
  240. package/export/typechain/factories/ERC1967Upgrade__factory.js +64 -0
  241. package/export/typechain/factories/ERC20WithPermit__factory.js +524 -0
  242. package/export/typechain/factories/ERC721__factory.js +388 -0
  243. package/export/typechain/factories/EcdsaAuthorization__factory.js +211 -0
  244. package/export/typechain/factories/EcdsaDkgValidator__factory.js +441 -0
  245. package/export/typechain/factories/EcdsaDkg__factory.js +192 -0
  246. package/export/typechain/factories/EcdsaInactivity__factory.js +134 -0
  247. package/export/typechain/factories/Fraud__factory.js +117 -0
  248. package/export/typechain/factories/Governable__factory.js +64 -0
  249. package/export/typechain/factories/HeartbeatStub__factory.js +61 -0
  250. package/export/typechain/factories/IApplication__factory.js +152 -0
  251. package/export/typechain/factories/IApproveAndCall__factory.js +48 -0
  252. package/export/typechain/factories/IBeacon__factory.js +32 -0
  253. package/export/typechain/factories/IERC165__factory.js +38 -0
  254. package/export/typechain/factories/IERC1822Proxiable__factory.js +32 -0
  255. package/export/typechain/factories/IERC20Metadata__factory.js +241 -0
  256. package/export/typechain/factories/IERC20WithPermit__factory.js +389 -0
  257. package/export/typechain/factories/IERC20__factory.js +202 -0
  258. package/export/typechain/factories/IERC721Metadata__factory.js +349 -0
  259. package/export/typechain/factories/IERC721Receiver__factory.js +53 -0
  260. package/export/typechain/factories/IERC721__factory.js +304 -0
  261. package/export/typechain/factories/IRandomBeaconConsumer__factory.js +37 -0
  262. package/export/typechain/factories/IRandomBeacon__factory.js +32 -0
  263. package/export/typechain/factories/IReceiveApproval__factory.js +47 -0
  264. package/export/typechain/factories/IReceiveBalanceApproval__factory.js +42 -0
  265. package/export/typechain/factories/IRelay__factory.js +45 -0
  266. package/export/typechain/factories/IStaking__factory.js +722 -0
  267. package/export/typechain/factories/IVault__factory.js +60 -0
  268. package/export/typechain/factories/IWalletOwner__factory.js +65 -0
  269. package/export/typechain/factories/IWalletRegistry__factory.js +138 -0
  270. package/export/typechain/factories/Initializable__factory.js +32 -0
  271. package/export/typechain/factories/MisfundRecovery__factory.js +145 -0
  272. package/export/typechain/factories/MovingFunds__factory.js +169 -0
  273. package/export/typechain/factories/Ownable__factory.js +71 -0
  274. package/export/typechain/factories/ProxyAdmin__factory.js +191 -0
  275. package/export/typechain/factories/Proxy__factory.js +27 -0
  276. package/export/typechain/factories/ReceiveApprovalStub__factory.js +127 -0
  277. package/export/typechain/factories/Redemption__factory.js +123 -0
  278. package/export/typechain/factories/Reimbursable__factory.js +58 -0
  279. package/export/typechain/factories/ReimbursementPool__factory.js +350 -0
  280. package/export/typechain/factories/Rewards__factory.js +117 -0
  281. package/export/typechain/factories/SortitionPool__factory.js +610 -0
  282. package/export/typechain/factories/SortitionTree__factory.js +149 -0
  283. package/export/typechain/factories/TBTCVault__factory.js +480 -0
  284. package/export/typechain/factories/TBTC__factory.js +564 -0
  285. package/export/typechain/factories/TestERC20__factory.js +539 -0
  286. package/export/typechain/factories/TestERC721__factory.js +421 -0
  287. package/export/typechain/factories/TestEcdsaLib__factory.js +66 -0
  288. package/export/typechain/factories/TestRelay__factory.js +94 -0
  289. package/export/typechain/factories/TransparentUpgradeableProxy__factory.js +186 -0
  290. package/export/typechain/factories/VendingMachine__factory.js +549 -0
  291. package/export/typechain/factories/WalletRegistry__factory.js +1919 -0
  292. package/export/typechain/factories/Wallets__factory.js +143 -0
  293. package/export/typechain/index.js +132 -0
  294. package/export.json +15932 -503
  295. package/package.json +47 -26
  296. package/artifacts/solcInputs/7cc3eda3cb3ff2522d18b5e7b31ea228.json +0 -104
@@ -0,0 +1,762 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const hardhat_1 = require("hardhat");
7
+ const chai_1 = require("chai");
8
+ const fixtures_1 = require("../fixtures");
9
+ const contract_test_helpers_1 = require("../helpers/contract-test-helpers");
10
+ const bridge_1 = __importDefault(require("../fixtures/bridge"));
11
+ const ZERO_ADDRESS = hardhat_1.ethers.constants.AddressZero;
12
+ const { createSnapshot, restoreSnapshot } = hardhat_1.helpers.snapshot;
13
+ describe("VendingMachine", () => {
14
+ let tbtcV1;
15
+ let tbtcV2;
16
+ let vendingMachine;
17
+ let deployer;
18
+ let governance;
19
+ let unmintFeeUpdateInitiator;
20
+ let vendingMachineUpgradeInitiator;
21
+ let tokenHolder;
22
+ let thirdParty;
23
+ const initialBalance = (0, contract_test_helpers_1.to1e18)(5); // 5 TBTC v1
24
+ before(async () => {
25
+ // eslint-disable-next-line @typescript-eslint/no-extra-semi
26
+ ;
27
+ ({ deployer, governance } = await hardhat_1.helpers.signers.getNamedSigners());
28
+ [
29
+ unmintFeeUpdateInitiator,
30
+ vendingMachineUpgradeInitiator,
31
+ tokenHolder,
32
+ thirdParty,
33
+ ] = await hardhat_1.helpers.signers.getUnnamedSigners();
34
+ await hardhat_1.waffle.loadFixture(bridge_1.default);
35
+ tbtcV1 = await hardhat_1.helpers.contracts.getContract("TBTCToken");
36
+ tbtcV2 = await hardhat_1.helpers.contracts.getContract("TBTC");
37
+ vendingMachine = await hardhat_1.helpers.contracts.getContract("VendingMachine");
38
+ await tbtcV1
39
+ .connect(deployer)
40
+ .mint(await tokenHolder.getAddress(), initialBalance);
41
+ await vendingMachine
42
+ .connect(deployer)
43
+ .transferOwnership(await governance.getAddress());
44
+ await vendingMachine
45
+ .connect(deployer)
46
+ .transferUnmintFeeUpdateInitiatorRole(await unmintFeeUpdateInitiator.getAddress());
47
+ await vendingMachine
48
+ .connect(deployer)
49
+ .transferVendingMachineUpgradeInitiatorRole(await vendingMachineUpgradeInitiator.getAddress());
50
+ await tbtcV1
51
+ .connect(tokenHolder)
52
+ .approve(vendingMachine.address, initialBalance);
53
+ await vendingMachine
54
+ .connect(unmintFeeUpdateInitiator)
55
+ .initiateUnmintFeeUpdate(fixtures_1.constants.unmintFee);
56
+ await hardhat_1.helpers.time.increaseTime(604800); // +7 days contract governance delay
57
+ await vendingMachine.connect(governance).finalizeUnmintFeeUpdate();
58
+ });
59
+ describe("mint", () => {
60
+ context("when TBTC v1 owner has not enough tokens", () => {
61
+ it("should revert", async () => {
62
+ const amount = initialBalance.add(1);
63
+ await tbtcV1
64
+ .connect(tokenHolder)
65
+ .approve(vendingMachine.address, amount);
66
+ await (0, chai_1.expect)(vendingMachine.connect(tokenHolder).mint(amount)).to.be.revertedWith("Transfer amount exceeds balance");
67
+ });
68
+ });
69
+ context("when TBTC v1 owner has enough tokens", () => {
70
+ let tx;
71
+ context("when minting entire allowance", () => {
72
+ const amount = initialBalance;
73
+ before(async () => {
74
+ await createSnapshot();
75
+ tx = await vendingMachine.connect(tokenHolder).mint(amount);
76
+ });
77
+ after(async () => {
78
+ await restoreSnapshot();
79
+ });
80
+ it("should mint the same amount of TBTC v2", async () => {
81
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).is.equal(amount);
82
+ });
83
+ it("should transfer TBTC v1 tokens to the VendingMachine", async () => {
84
+ (0, chai_1.expect)(await tbtcV1.balanceOf(vendingMachine.address)).is.equal(amount);
85
+ });
86
+ it("should emit Minted event", async () => {
87
+ await (0, chai_1.expect)(tx)
88
+ .to.emit(vendingMachine, "Minted")
89
+ .withArgs(await tokenHolder.getAddress(), amount);
90
+ });
91
+ });
92
+ context("when minting part of the allowance", () => {
93
+ const amount = initialBalance.sub((0, contract_test_helpers_1.to1e18)(1));
94
+ before(async () => {
95
+ await createSnapshot();
96
+ tx = await vendingMachine.connect(tokenHolder).mint(amount);
97
+ });
98
+ after(async () => {
99
+ await restoreSnapshot();
100
+ });
101
+ it("should mint the same amount of TBTC v2", async () => {
102
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).is.equal(amount);
103
+ });
104
+ it("should transfer TBTC v1 tokens to the VendingMachine", async () => {
105
+ (0, chai_1.expect)(await tbtcV1.balanceOf(vendingMachine.address)).is.equal(amount);
106
+ });
107
+ it("should emit Minted event", async () => {
108
+ await (0, chai_1.expect)(tx)
109
+ .to.emit(vendingMachine, "Minted")
110
+ .withArgs(await tokenHolder.getAddress(), amount);
111
+ });
112
+ });
113
+ });
114
+ });
115
+ describe("receiveApproval", () => {
116
+ context("when called directly", () => {
117
+ it("should revert", async () => {
118
+ await (0, chai_1.expect)(vendingMachine
119
+ .connect(tokenHolder)
120
+ .receiveApproval(await tokenHolder.getAddress(), initialBalance, tbtcV1.address, [])).to.be.revertedWith("Only TBTC v1 caller allowed");
121
+ });
122
+ });
123
+ context("when called not for TBTC v1 token", () => {
124
+ it("should revert", async () => {
125
+ await (0, chai_1.expect)(vendingMachine
126
+ .connect(tokenHolder)
127
+ .receiveApproval(await tokenHolder.getAddress(), initialBalance, tbtcV2.address, [])).to.be.revertedWith("Token is not TBTC v1");
128
+ });
129
+ });
130
+ context("when called via approveAndCall", () => {
131
+ const amount = (0, contract_test_helpers_1.to1e18)(2);
132
+ let tx;
133
+ before(async () => {
134
+ await createSnapshot();
135
+ tx = await tbtcV1
136
+ .connect(tokenHolder)
137
+ .approveAndCall(vendingMachine.address, amount, []);
138
+ });
139
+ after(async () => {
140
+ await restoreSnapshot();
141
+ });
142
+ it("should mint TBTC v2 to the caller", async () => {
143
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).is.equal(amount);
144
+ });
145
+ it("should transfer TBTC v1 tokens to the VendingMachine", async () => {
146
+ (0, chai_1.expect)(await tbtcV1.balanceOf(vendingMachine.address)).is.equal(amount);
147
+ });
148
+ it("should emit Minted event", async () => {
149
+ await (0, chai_1.expect)(tx)
150
+ .to.emit(vendingMachine, "Minted")
151
+ .withArgs(await tokenHolder.getAddress(), amount);
152
+ });
153
+ });
154
+ });
155
+ describe("unmint", () => {
156
+ before(async () => {
157
+ await createSnapshot();
158
+ await vendingMachine.connect(tokenHolder).mint(initialBalance);
159
+ await tbtcV2
160
+ .connect(tokenHolder)
161
+ .approve(vendingMachine.address, initialBalance);
162
+ });
163
+ after(async () => {
164
+ await restoreSnapshot();
165
+ });
166
+ context("when unmint fee is zero", () => {
167
+ before(async () => {
168
+ await createSnapshot();
169
+ await vendingMachine
170
+ .connect(unmintFeeUpdateInitiator)
171
+ .initiateUnmintFeeUpdate(0);
172
+ await hardhat_1.helpers.time.increaseTime(604800); // +7 days contract governance delay
173
+ await vendingMachine.connect(governance).finalizeUnmintFeeUpdate();
174
+ });
175
+ after(async () => {
176
+ await restoreSnapshot();
177
+ });
178
+ context("when TBTC v2 owner has not enough tokens", () => {
179
+ it("should revert", async () => {
180
+ await (0, chai_1.expect)(vendingMachine.connect(tokenHolder).unmint(initialBalance.add(1))).to.be.revertedWith("Amount + fee exceeds TBTC v2 balance");
181
+ });
182
+ });
183
+ context("when TBTC v2 owner has enough tokens", () => {
184
+ context("when unminting entire TBTC v2 balance", () => {
185
+ const unmintAmount = initialBalance;
186
+ let v1StartBalance;
187
+ let v2StartBalance;
188
+ let tx;
189
+ before(async () => {
190
+ await createSnapshot();
191
+ v1StartBalance = await tbtcV1.balanceOf(await tokenHolder.getAddress());
192
+ v2StartBalance = await tbtcV2.balanceOf(await tokenHolder.getAddress());
193
+ tx = await vendingMachine.connect(tokenHolder).unmint(unmintAmount);
194
+ });
195
+ after(async () => {
196
+ await restoreSnapshot();
197
+ });
198
+ it("should transfer no TBTC v2 to the VendingMachine", async () => {
199
+ (0, chai_1.expect)(await tbtcV2.balanceOf(vendingMachine.address)).to.equal(0);
200
+ });
201
+ it("should burn unminted TBTC v2 tokens", async () => {
202
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).to.equal(v2StartBalance.sub(unmintAmount));
203
+ (0, chai_1.expect)(await tbtcV2.totalSupply()).to.equal(v2StartBalance.sub(unmintAmount));
204
+ });
205
+ it("should transfer unminted TBTC v1 tokens back to the owner", async () => {
206
+ (0, chai_1.expect)(await tbtcV1.balanceOf(await tokenHolder.getAddress())).to.equal(v1StartBalance.add(unmintAmount));
207
+ });
208
+ it("should emit the Unminted event", async () => {
209
+ await (0, chai_1.expect)(tx)
210
+ .to.emit(vendingMachine, "Unminted")
211
+ .withArgs(await tokenHolder.getAddress(), unmintAmount, 0);
212
+ });
213
+ });
214
+ context("when unminting part of TBTC v2 balance", () => {
215
+ const unmintAmount = (0, contract_test_helpers_1.to1e18)(1);
216
+ let v1StartBalance;
217
+ let v2StartBalance;
218
+ let tx;
219
+ before(async () => {
220
+ await createSnapshot();
221
+ v1StartBalance = await tbtcV1.balanceOf(await tokenHolder.getAddress());
222
+ v2StartBalance = await tbtcV2.balanceOf(await tokenHolder.getAddress());
223
+ tx = await vendingMachine.connect(tokenHolder).unmint(unmintAmount);
224
+ });
225
+ after(async () => {
226
+ await restoreSnapshot();
227
+ });
228
+ it("should transfer no TBTC v2 to the VendingMachine", async () => {
229
+ (0, chai_1.expect)(await tbtcV2.balanceOf(vendingMachine.address)).to.equal(0);
230
+ });
231
+ it("should burn unminted TBTC v2 tokens", async () => {
232
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).to.equal(v2StartBalance.sub(unmintAmount));
233
+ (0, chai_1.expect)(await tbtcV2.totalSupply()).to.equal(v2StartBalance.sub(unmintAmount));
234
+ });
235
+ it("should transfer unminted TBTC v1 tokens back to the owner", async () => {
236
+ (0, chai_1.expect)(await tbtcV1.balanceOf(await tokenHolder.getAddress())).to.equal(v1StartBalance.add(unmintAmount));
237
+ });
238
+ it("should emit the Unminted event", async () => {
239
+ await (0, chai_1.expect)(tx)
240
+ .to.emit(vendingMachine, "Unminted")
241
+ .withArgs(await tokenHolder.getAddress(), unmintAmount, 0);
242
+ });
243
+ });
244
+ });
245
+ });
246
+ context("when unmint fee is non-zero", () => {
247
+ context("when TBTC v2 owner has not enough tokens", () => {
248
+ it("should revert", async () => {
249
+ await (0, chai_1.expect)(vendingMachine.connect(tokenHolder).unmint(initialBalance.add(1))).to.be.revertedWith("Amount + fee exceeds TBTC v2 balance");
250
+ });
251
+ });
252
+ context("when TBTC v2 owner has enough tokens", () => {
253
+ context("when unminting entire TBTC v2 balance", () => {
254
+ // 1e18 * balance / (1e18 + unmintFee)
255
+ const unmintAmount = initialBalance
256
+ .mul((0, contract_test_helpers_1.to1e18)(1))
257
+ .div((0, contract_test_helpers_1.to1e18)(1).add(fixtures_1.constants.unmintFee));
258
+ let fee;
259
+ let v1StartBalance;
260
+ let v2StartBalance;
261
+ let tx;
262
+ before(async () => {
263
+ await createSnapshot();
264
+ v1StartBalance = await tbtcV1.balanceOf(await tokenHolder.getAddress());
265
+ v2StartBalance = await tbtcV2.balanceOf(await tokenHolder.getAddress());
266
+ fee = await vendingMachine.unmintFeeFor(unmintAmount);
267
+ tx = await vendingMachine.connect(tokenHolder).unmint(unmintAmount);
268
+ });
269
+ after(async () => {
270
+ await restoreSnapshot();
271
+ });
272
+ it("should transfer TBTC v2 fee to the VendingMachine", async () => {
273
+ (0, chai_1.expect)(await tbtcV2.balanceOf(vendingMachine.address)).to.equal(fee);
274
+ });
275
+ it("should burn unminted TBTC v2 tokens", async () => {
276
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).to.equal(v2StartBalance.sub(unmintAmount).sub(fee));
277
+ (0, chai_1.expect)(await tbtcV2.totalSupply()).to.equal(v2StartBalance.sub(unmintAmount));
278
+ });
279
+ it("should transfer unminted TBTC v1 tokens back to the owner", async () => {
280
+ (0, chai_1.expect)(await tbtcV1.balanceOf(await tokenHolder.getAddress())).to.equal(v1StartBalance.add(unmintAmount));
281
+ });
282
+ it("should emit the Unminted event", async () => {
283
+ await (0, chai_1.expect)(tx)
284
+ .to.emit(vendingMachine, "Unminted")
285
+ .withArgs(await tokenHolder.getAddress(), unmintAmount, fee);
286
+ });
287
+ });
288
+ context("when unminting part of TBTC v2 balance", () => {
289
+ const unmintAmount = (0, contract_test_helpers_1.to1e18)(1);
290
+ let fee;
291
+ let v1StartBalance;
292
+ let v2StartBalance;
293
+ let tx;
294
+ before(async () => {
295
+ await createSnapshot();
296
+ v1StartBalance = await tbtcV1.balanceOf(await tokenHolder.getAddress());
297
+ v2StartBalance = await tbtcV2.balanceOf(await tokenHolder.getAddress());
298
+ fee = await vendingMachine.unmintFeeFor(unmintAmount);
299
+ tx = await vendingMachine.connect(tokenHolder).unmint(unmintAmount);
300
+ });
301
+ after(async () => {
302
+ await restoreSnapshot();
303
+ });
304
+ it("should transfer TBTC v2 fee to the VendingMachine", async () => {
305
+ (0, chai_1.expect)(await tbtcV2.balanceOf(vendingMachine.address)).to.equal(fee);
306
+ });
307
+ it("should burn unminted TBTC v2 tokens", async () => {
308
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await tokenHolder.getAddress())).to.equal(v2StartBalance.sub(unmintAmount).sub(fee));
309
+ (0, chai_1.expect)(await tbtcV2.totalSupply()).to.equal(v2StartBalance.sub(unmintAmount));
310
+ });
311
+ it("should transfer unminted TBTC v1 tokens back to the owner", async () => {
312
+ (0, chai_1.expect)(await tbtcV1.balanceOf(await tokenHolder.getAddress())).to.equal(v1StartBalance.add(unmintAmount));
313
+ });
314
+ it("should emit the Unminted event", async () => {
315
+ await (0, chai_1.expect)(tx)
316
+ .to.emit(vendingMachine, "Unminted")
317
+ .withArgs(await tokenHolder.getAddress(), unmintAmount, fee);
318
+ });
319
+ });
320
+ });
321
+ });
322
+ });
323
+ describe("withdrawFees", () => {
324
+ const unmintAmount = (0, contract_test_helpers_1.to1e18)(4);
325
+ let unmintFee;
326
+ before(async () => {
327
+ await createSnapshot();
328
+ await vendingMachine.connect(tokenHolder).mint(initialBalance);
329
+ await tbtcV2
330
+ .connect(tokenHolder)
331
+ .approve(vendingMachine.address, initialBalance);
332
+ unmintFee = await vendingMachine.unmintFeeFor(unmintAmount);
333
+ await vendingMachine.connect(tokenHolder).unmint(unmintAmount);
334
+ });
335
+ after(async () => {
336
+ await restoreSnapshot();
337
+ });
338
+ context("when caller is not the owner", () => {
339
+ it("should revert", async () => {
340
+ await (0, chai_1.expect)(vendingMachine
341
+ .connect(thirdParty)
342
+ .withdrawFees(await thirdParty.getAddress(), unmintFee)).to.be.revertedWith("Ownable: caller is not the owner");
343
+ });
344
+ });
345
+ context("when caller is the owner", () => {
346
+ let withdrawnFee;
347
+ before(async () => {
348
+ await createSnapshot();
349
+ withdrawnFee = unmintFee.sub(1);
350
+ await vendingMachine
351
+ .connect(governance)
352
+ .withdrawFees(await thirdParty.getAddress(), withdrawnFee);
353
+ });
354
+ after(async () => {
355
+ await restoreSnapshot();
356
+ });
357
+ it("should withdraw the provided amount of fees", async () => {
358
+ (0, chai_1.expect)(await tbtcV2.balanceOf(await thirdParty.getAddress())).is.equal(withdrawnFee);
359
+ });
360
+ it("should leave the rest of fees in VendingMachine", async () => {
361
+ (0, chai_1.expect)(await tbtcV2.balanceOf(vendingMachine.address)).is.equal(unmintFee.sub(withdrawnFee));
362
+ });
363
+ });
364
+ });
365
+ describe("initiateUnmintFeeUpdate", () => {
366
+ before(async () => {
367
+ await createSnapshot();
368
+ });
369
+ after(async () => {
370
+ await restoreSnapshot();
371
+ });
372
+ context("when caller is a third party", () => {
373
+ it("should revert", async () => {
374
+ await (0, chai_1.expect)(vendingMachine.connect(thirdParty).initiateUnmintFeeUpdate(1)).to.be.revertedWith("Caller is not authorized");
375
+ });
376
+ });
377
+ context("when caller is the contract owner", () => {
378
+ it("should revert", async () => {
379
+ await (0, chai_1.expect)(vendingMachine.connect(governance).initiateUnmintFeeUpdate(1)).to.be.revertedWith("Caller is not authorized");
380
+ });
381
+ });
382
+ context("when caller is the update initiator", () => {
383
+ const newUnmintFee = 191111;
384
+ let tx;
385
+ before(async () => {
386
+ await createSnapshot();
387
+ tx = await vendingMachine
388
+ .connect(unmintFeeUpdateInitiator)
389
+ .initiateUnmintFeeUpdate(newUnmintFee);
390
+ });
391
+ after(async () => {
392
+ await restoreSnapshot();
393
+ });
394
+ it("should not update the unmint fee", async () => {
395
+ (0, chai_1.expect)(await vendingMachine.unmintFee()).to.equal(fixtures_1.constants.unmintFee);
396
+ });
397
+ it("should start the update initiation time", async () => {
398
+ (0, chai_1.expect)(await vendingMachine.unmintFeeUpdateInitiatedTimestamp()).to.equal(await (0, contract_test_helpers_1.getBlockTime)(tx.blockNumber));
399
+ });
400
+ it("should set the pending new unmint fee", async () => {
401
+ (0, chai_1.expect)(await vendingMachine.newUnmintFee()).to.equal(newUnmintFee);
402
+ });
403
+ it("should start the governance delay timer", async () => {
404
+ (0, chai_1.expect)(await vendingMachine.getRemainingUnmintFeeUpdateTime()).to.equal(604800 // 7 days contract governance delay
405
+ );
406
+ });
407
+ it("should emit UnmintFeeUpdateInitiated event", async () => {
408
+ await (0, chai_1.expect)(tx)
409
+ .to.emit(vendingMachine, "UnmintFeeUpdateInitiated")
410
+ .withArgs(newUnmintFee, await (0, contract_test_helpers_1.getBlockTime)(tx.blockNumber));
411
+ });
412
+ });
413
+ });
414
+ describe("finalizeUnmintFeeUpdate", () => {
415
+ before(async () => {
416
+ await createSnapshot();
417
+ });
418
+ after(async () => {
419
+ await restoreSnapshot();
420
+ });
421
+ context("when caller is a third party", () => {
422
+ it("should revert", async () => {
423
+ await (0, chai_1.expect)(vendingMachine.connect(thirdParty).finalizeUnmintFeeUpdate()).to.be.revertedWith("Ownable: caller is not the owner");
424
+ });
425
+ });
426
+ context("when caller is the update initiator", () => {
427
+ it("should revert", async () => {
428
+ await (0, chai_1.expect)(vendingMachine
429
+ .connect(unmintFeeUpdateInitiator)
430
+ .finalizeUnmintFeeUpdate()).to.be.revertedWith("Ownable: caller is not the owner");
431
+ });
432
+ });
433
+ context("when caller is the owner", () => {
434
+ context("when update process is not initialized", () => {
435
+ it("should revert", async () => {
436
+ await (0, chai_1.expect)(vendingMachine.connect(governance).finalizeUnmintFeeUpdate()).to.be.revertedWith("Change not initiated");
437
+ });
438
+ });
439
+ context("when update process is initialized", () => {
440
+ const newUnmintFee = 151511;
441
+ before(async () => {
442
+ await createSnapshot();
443
+ await vendingMachine
444
+ .connect(unmintFeeUpdateInitiator)
445
+ .initiateUnmintFeeUpdate(newUnmintFee);
446
+ });
447
+ after(async () => {
448
+ await restoreSnapshot();
449
+ });
450
+ context("when governance delay has not passed", () => {
451
+ it("should revert", async () => {
452
+ await hardhat_1.helpers.time.increaseTime(601200); // +7 days 23 hours
453
+ await (0, chai_1.expect)(vendingMachine.connect(governance).finalizeUnmintFeeUpdate()).to.be.revertedWith("Governance delay has not elapsed");
454
+ });
455
+ });
456
+ context("when governance delay passed", () => {
457
+ let tx;
458
+ before(async () => {
459
+ await createSnapshot();
460
+ await hardhat_1.helpers.time.increaseTime(604800); // +7 days contract governance delay
461
+ tx = await vendingMachine
462
+ .connect(governance)
463
+ .finalizeUnmintFeeUpdate();
464
+ });
465
+ after(async () => {
466
+ await restoreSnapshot();
467
+ });
468
+ it("should update the unmint fee", async () => {
469
+ (0, chai_1.expect)(await vendingMachine.unmintFee()).to.equal(newUnmintFee);
470
+ });
471
+ it("should emit UnmintFeeUpdated event", async () => {
472
+ await (0, chai_1.expect)(tx)
473
+ .to.emit(vendingMachine, "UnmintFeeUpdated")
474
+ .withArgs(newUnmintFee);
475
+ });
476
+ it("should reset the governance delay timer", async () => {
477
+ await (0, chai_1.expect)(vendingMachine.getRemainingUnmintFeeUpdateTime()).to.be.revertedWith("Change not initiated");
478
+ });
479
+ it("should reset the pending new unmint fee", async () => {
480
+ (0, chai_1.expect)(await vendingMachine.newUnmintFee()).to.equal(0);
481
+ });
482
+ it("should reset the unmint fee update initiated timestamp", async () => {
483
+ (0, chai_1.expect)(await vendingMachine.unmintFeeUpdateInitiatedTimestamp()).to.equal(0);
484
+ });
485
+ });
486
+ });
487
+ });
488
+ });
489
+ describe("initiateVendingMachineUpgrade", () => {
490
+ let newVendingMachine;
491
+ before(async () => {
492
+ await createSnapshot();
493
+ const VendingMachine = await hardhat_1.ethers.getContractFactory("VendingMachine");
494
+ newVendingMachine = await VendingMachine.deploy(tbtcV1.address, tbtcV2.address, fixtures_1.constants.unmintFee);
495
+ await newVendingMachine.deployed();
496
+ });
497
+ after(async () => {
498
+ await restoreSnapshot();
499
+ });
500
+ context("when caller is a third party", () => {
501
+ it("should revert", async () => {
502
+ await (0, chai_1.expect)(vendingMachine
503
+ .connect(thirdParty)
504
+ .initiateVendingMachineUpgrade(newVendingMachine.address)).to.be.revertedWith("Caller is not authorized");
505
+ });
506
+ });
507
+ context("when caller is the contract owner", () => {
508
+ it("should revert", async () => {
509
+ await (0, chai_1.expect)(vendingMachine
510
+ .connect(governance)
511
+ .initiateVendingMachineUpgrade(newVendingMachine.address)).to.be.revertedWith("Caller is not authorized");
512
+ });
513
+ });
514
+ context("when caller is the upgrade initiator", () => {
515
+ context("when new vending machine address is zero", () => {
516
+ it("should revert", async () => {
517
+ await (0, chai_1.expect)(vendingMachine
518
+ .connect(vendingMachineUpgradeInitiator)
519
+ .initiateVendingMachineUpgrade(ZERO_ADDRESS)).to.be.revertedWith("New VendingMachine cannot be zero address");
520
+ });
521
+ });
522
+ context("when new vending machine address is non-zero", () => {
523
+ let tx;
524
+ before(async () => {
525
+ await createSnapshot();
526
+ tx = await vendingMachine
527
+ .connect(vendingMachineUpgradeInitiator)
528
+ .initiateVendingMachineUpgrade(newVendingMachine.address);
529
+ });
530
+ after(async () => {
531
+ await restoreSnapshot();
532
+ });
533
+ it("should not transfer token ownership", async () => {
534
+ (0, chai_1.expect)(await tbtcV2.owner()).is.equal(vendingMachine.address);
535
+ });
536
+ it("should start the upgrade initiation time", async () => {
537
+ (0, chai_1.expect)(await vendingMachine.vendingMachineUpgradeInitiatedTimestamp()).to.equal(await (0, contract_test_helpers_1.getBlockTime)(tx.blockNumber));
538
+ });
539
+ it("should set the pending new vending machine address", async () => {
540
+ (0, chai_1.expect)(await vendingMachine.newVendingMachine()).to.equal(newVendingMachine.address);
541
+ });
542
+ it("should start the governance delay timer", async () => {
543
+ (0, chai_1.expect)(await vendingMachine.getRemainingVendingMachineUpgradeTime()).to.equal(604800 // 7 days contract governance delay
544
+ );
545
+ });
546
+ it("should emit VendingMachineUpgradeInitiated event", async () => {
547
+ await (0, chai_1.expect)(tx)
548
+ .to.emit(vendingMachine, "VendingMachineUpgradeInitiated")
549
+ .withArgs(newVendingMachine.address, await (0, contract_test_helpers_1.getBlockTime)(tx.blockNumber));
550
+ });
551
+ });
552
+ });
553
+ });
554
+ describe("finalizeVendingMachineUpgrade", () => {
555
+ before(async () => {
556
+ await createSnapshot();
557
+ });
558
+ after(async () => {
559
+ await restoreSnapshot();
560
+ });
561
+ context("when caller is a third party", () => {
562
+ it("should revert", async () => {
563
+ await (0, chai_1.expect)(vendingMachine.connect(thirdParty).finalizeVendingMachineUpgrade()).to.be.revertedWith("Ownable: caller is not the owner");
564
+ });
565
+ });
566
+ context("when caller is the upgrade initiator", () => {
567
+ it("should revert", async () => {
568
+ await (0, chai_1.expect)(vendingMachine
569
+ .connect(vendingMachineUpgradeInitiator)
570
+ .finalizeVendingMachineUpgrade()).to.be.revertedWith("Ownable: caller is not the owner");
571
+ });
572
+ });
573
+ context("when caller is the owner", () => {
574
+ context("when upgrade process is not initialized", () => {
575
+ it("should revert", async () => {
576
+ await (0, chai_1.expect)(vendingMachine.connect(governance).finalizeVendingMachineUpgrade()).to.be.revertedWith("Change not initiated");
577
+ });
578
+ });
579
+ context("when upgrade process is initialized", () => {
580
+ const tbtcV1Amount = (0, contract_test_helpers_1.to1e18)(3);
581
+ let newVendingMachine;
582
+ before(async () => {
583
+ await createSnapshot();
584
+ const VendingMachine = await hardhat_1.ethers.getContractFactory("VendingMachine");
585
+ newVendingMachine = await VendingMachine.deploy(tbtcV1.address, tbtcV2.address, fixtures_1.constants.unmintFee);
586
+ await newVendingMachine.deployed();
587
+ await tbtcV1
588
+ .connect(tokenHolder)
589
+ .approve(vendingMachine.address, tbtcV1Amount);
590
+ await vendingMachine.connect(tokenHolder).mint(tbtcV1Amount);
591
+ await vendingMachine
592
+ .connect(vendingMachineUpgradeInitiator)
593
+ .initiateVendingMachineUpgrade(newVendingMachine.address);
594
+ });
595
+ after(async () => {
596
+ await restoreSnapshot();
597
+ });
598
+ context("when governance delay has not passed", () => {
599
+ it("should revert", async () => {
600
+ await hardhat_1.helpers.time.increaseTime(601200); // +7days 23 hours
601
+ await (0, chai_1.expect)(vendingMachine.connect(governance).finalizeVendingMachineUpgrade()).to.be.revertedWith("Governance delay has not elapsed");
602
+ });
603
+ });
604
+ context("when governance delay passed", () => {
605
+ let tx;
606
+ before(async () => {
607
+ await createSnapshot();
608
+ await hardhat_1.helpers.time.increaseTime(604800); // +7 days contract governance delay
609
+ tx = await vendingMachine
610
+ .connect(governance)
611
+ .finalizeVendingMachineUpgrade();
612
+ });
613
+ after(async () => {
614
+ await restoreSnapshot();
615
+ });
616
+ it("should transfer token ownership to the new VendingMachine", async () => {
617
+ (0, chai_1.expect)(await tbtcV2.owner()).to.equal(newVendingMachine.address);
618
+ });
619
+ it("should transfer all TBTC v1 to the new VendingMachine", async () => {
620
+ (0, chai_1.expect)(await tbtcV1.balanceOf(newVendingMachine.address)).to.equal(tbtcV1Amount);
621
+ });
622
+ it("should emit VendingMachineUpgraded event", async () => {
623
+ await (0, chai_1.expect)(tx)
624
+ .to.emit(vendingMachine, "VendingMachineUpgraded")
625
+ .withArgs(newVendingMachine.address);
626
+ });
627
+ it("should reset the governance delay timer", async () => {
628
+ await (0, chai_1.expect)(vendingMachine.getRemainingVendingMachineUpgradeTime()).to.be.revertedWith("Change not initiated");
629
+ });
630
+ it("should reset the pending new vending machine address", async () => {
631
+ (0, chai_1.expect)(await vendingMachine.newVendingMachine()).to.equal(ZERO_ADDRESS);
632
+ });
633
+ it("should reset the vending machine update initiated timestamp", async () => {
634
+ (0, chai_1.expect)(await vendingMachine.vendingMachineUpgradeInitiatedTimestamp()).to.equal(0);
635
+ });
636
+ });
637
+ });
638
+ });
639
+ });
640
+ describe("transferUnmintFeeUpdateInitiatorRole", () => {
641
+ before(async () => {
642
+ await createSnapshot();
643
+ });
644
+ after(async () => {
645
+ await restoreSnapshot();
646
+ });
647
+ context("when caller is the owner", () => {
648
+ it("should revert", async () => {
649
+ await (0, chai_1.expect)(vendingMachine
650
+ .connect(governance)
651
+ .transferUnmintFeeUpdateInitiatorRole(await thirdParty.getAddress())).to.be.revertedWith("Caller is not authorized");
652
+ });
653
+ });
654
+ context("when caller is a third party", () => {
655
+ it("should revert", async () => {
656
+ await (0, chai_1.expect)(vendingMachine
657
+ .connect(thirdParty)
658
+ .transferUnmintFeeUpdateInitiatorRole(await thirdParty.getAddress())).to.be.revertedWith("Caller is not authorized");
659
+ });
660
+ });
661
+ context("when caller is the update initiator", () => {
662
+ context("when new initiator is a valid address", () => {
663
+ before(async () => {
664
+ await createSnapshot();
665
+ });
666
+ after(async () => {
667
+ await restoreSnapshot();
668
+ });
669
+ it("should transfer the role", async () => {
670
+ await vendingMachine
671
+ .connect(unmintFeeUpdateInitiator)
672
+ .transferUnmintFeeUpdateInitiatorRole(await thirdParty.getAddress());
673
+ (0, chai_1.expect)(await vendingMachine.unmintFeeUpdateInitiator()).to.equal(await thirdParty.getAddress());
674
+ });
675
+ });
676
+ context("when new initiator is zero address", () => {
677
+ it("should revert", async () => {
678
+ await (0, chai_1.expect)(vendingMachine
679
+ .connect(unmintFeeUpdateInitiator)
680
+ .transferUnmintFeeUpdateInitiatorRole(ZERO_ADDRESS)).to.be.revertedWith("New initiator must not be zero address");
681
+ });
682
+ });
683
+ });
684
+ });
685
+ describe("transferVendingMachineUpgradeInitiatorRole", () => {
686
+ before(async () => {
687
+ await createSnapshot();
688
+ });
689
+ after(async () => {
690
+ await restoreSnapshot();
691
+ });
692
+ context("when caller is the owner", () => {
693
+ it("should revert", async () => {
694
+ await (0, chai_1.expect)(vendingMachine
695
+ .connect(governance)
696
+ .transferVendingMachineUpgradeInitiatorRole(await thirdParty.getAddress())).to.be.revertedWith("Caller is not authorized");
697
+ });
698
+ });
699
+ context("when caller is a third party", () => {
700
+ it("should revert", async () => {
701
+ await (0, chai_1.expect)(vendingMachine
702
+ .connect(thirdParty)
703
+ .transferVendingMachineUpgradeInitiatorRole(await thirdParty.getAddress())).to.be.revertedWith("Caller is not authorized");
704
+ });
705
+ });
706
+ context("when caller is the update initiator", () => {
707
+ context("when new initiator is a valid address", () => {
708
+ before(async () => {
709
+ await createSnapshot();
710
+ });
711
+ after(async () => {
712
+ await restoreSnapshot();
713
+ });
714
+ it("should transfer the role", async () => {
715
+ await vendingMachine
716
+ .connect(vendingMachineUpgradeInitiator)
717
+ .transferVendingMachineUpgradeInitiatorRole(await thirdParty.getAddress());
718
+ (0, chai_1.expect)(await vendingMachine.vendingMachineUpgradeInitiator()).to.equal(await thirdParty.getAddress());
719
+ });
720
+ });
721
+ context("when new initiator is zero address", () => {
722
+ it("should revert", async () => {
723
+ await (0, chai_1.expect)(vendingMachine
724
+ .connect(vendingMachineUpgradeInitiator)
725
+ .transferVendingMachineUpgradeInitiatorRole(ZERO_ADDRESS)).to.be.revertedWith("New initiator must not be zero address");
726
+ });
727
+ });
728
+ });
729
+ });
730
+ describe("unmintFeeFor", () => {
731
+ const unmintAmount = (0, contract_test_helpers_1.to1e18)(2);
732
+ before(async () => {
733
+ await createSnapshot();
734
+ });
735
+ after(async () => {
736
+ await restoreSnapshot();
737
+ });
738
+ context("when unmint fee is non-zero", async () => {
739
+ it("should return a correct portion of the amount to unmint", async () => {
740
+ // 0.001 * 2 = 0.002
741
+ await (0, chai_1.expect)(await vendingMachine.unmintFeeFor(unmintAmount)).to.equal((0, contract_test_helpers_1.to1ePrecision)(2, 15));
742
+ });
743
+ });
744
+ context("when unmint fee is zero", async () => {
745
+ before(async () => {
746
+ await createSnapshot();
747
+ await vendingMachine
748
+ .connect(unmintFeeUpdateInitiator)
749
+ .initiateUnmintFeeUpdate(0);
750
+ await hardhat_1.helpers.time.increaseTime(604800); // +7 days contract governance delay
751
+ await vendingMachine.connect(governance).finalizeUnmintFeeUpdate();
752
+ });
753
+ after(async () => {
754
+ await restoreSnapshot();
755
+ });
756
+ it("should return zero", async () => {
757
+ // 0.001 * 0 = 0
758
+ await (0, chai_1.expect)(await vendingMachine.unmintFeeFor(unmintAmount)).to.equal(0);
759
+ });
760
+ });
761
+ });
762
+ });