@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.
- package/README.adoc +12 -0
- package/artifacts/.chainId +1 -1
- package/artifacts/Bank.json +807 -0
- package/artifacts/Bridge.json +2300 -0
- package/artifacts/Deposit.json +117 -0
- package/artifacts/DepositSweep.json +77 -0
- package/artifacts/EcdsaDkgValidator.json +532 -0
- package/artifacts/EcdsaInactivity.json +156 -0
- package/artifacts/EcdsaSortitionPool.json +1004 -0
- package/artifacts/Fraud.json +164 -0
- package/artifacts/KeepRegistry.json +99 -0
- package/artifacts/KeepStake.json +286 -0
- package/artifacts/KeepToken.json +711 -0
- package/artifacts/KeepTokenStaking.json +483 -0
- package/artifacts/MovingFunds.json +249 -0
- package/artifacts/NuCypherStakingEscrow.json +256 -0
- package/artifacts/NuCypherToken.json +711 -0
- package/artifacts/RandomBeaconStub.json +141 -0
- package/artifacts/Redemption.json +174 -0
- package/artifacts/ReimbursementPool.json +509 -0
- package/artifacts/Relay.json +123 -0
- package/artifacts/T.json +1148 -0
- package/artifacts/TBTC.json +36 -35
- package/artifacts/TBTCToken.json +738 -0
- package/artifacts/TBTCVault.json +691 -0
- package/artifacts/TokenStaking.json +2288 -0
- package/artifacts/TokenholderGovernor.json +1795 -0
- package/artifacts/TokenholderTimelock.json +1058 -0
- package/artifacts/VendingMachine.json +34 -33
- package/artifacts/VendingMachineKeep.json +400 -0
- package/artifacts/VendingMachineNuCypher.json +400 -0
- package/artifacts/WalletRegistry.json +1843 -0
- package/artifacts/WalletRegistryGovernance.json +2754 -0
- package/artifacts/Wallets.json +186 -0
- package/artifacts/solcInputs/5e62cff1ead0900b07facca4b559e818.json +314 -0
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.json +2 -2
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +4 -0
- package/build/contracts/bank/Bank.sol/Bank.json +542 -0
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +4 -0
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.json +34 -0
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +4 -0
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.json +10 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +4 -0
- package/build/contracts/bridge/Bridge.sol/Bridge.json +2686 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +4 -0
- package/build/contracts/bridge/BridgeState.sol/BridgeState.json +226 -0
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +4 -0
- package/build/contracts/bridge/Deposit.sol/Deposit.json +72 -0
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +4 -0
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.json +30 -0
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +4 -0
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.json +10 -0
- package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +4 -0
- package/build/contracts/bridge/Fraud.sol/Fraud.json +86 -0
- package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +4 -0
- package/build/contracts/bridge/Heartbeat.sol/Heartbeat.json +10 -0
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +4 -0
- package/build/contracts/bridge/IRelay.sol/IRelay.json +37 -0
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +4 -0
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.json +138 -0
- package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +4 -0
- package/build/contracts/bridge/Redemption.sol/OutboundTx.json +10 -0
- package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +4 -0
- package/build/contracts/bridge/Redemption.sol/Redemption.json +92 -0
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.json +2 -2
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +4 -0
- package/build/contracts/bridge/Wallets.sol/Wallets.json +112 -0
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/token/TBTC.sol/TBTC.json +4 -4
- package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +4 -0
- package/build/contracts/vault/DonationVault.sol/DonationVault.json +108 -0
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +4 -0
- package/build/contracts/vault/IVault.sol/IVault.json +52 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +4 -0
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +449 -0
- package/contracts/GovernanceUtils.sol +4 -4
- package/contracts/bank/Bank.sol +436 -0
- package/contracts/bank/IReceiveBalanceApproval.sol +45 -0
- package/contracts/bridge/BitcoinTx.sol +326 -0
- package/contracts/bridge/Bridge.sol +1793 -0
- package/contracts/bridge/BridgeState.sol +739 -0
- package/contracts/bridge/Deposit.sol +269 -0
- package/contracts/bridge/DepositSweep.sol +574 -0
- package/contracts/bridge/EcdsaLib.sol +45 -0
- package/contracts/bridge/Fraud.sol +579 -0
- package/contracts/bridge/Heartbeat.sol +112 -0
- package/contracts/bridge/IRelay.sol +28 -0
- package/contracts/bridge/MovingFunds.sol +1077 -0
- package/contracts/bridge/Redemption.sol +1020 -0
- package/contracts/bridge/VendingMachine.sol +2 -2
- package/contracts/bridge/Wallets.sol +719 -0
- package/contracts/hardhat-dependency-compiler/.hardhat-dependency-compiler +1 -0
- package/contracts/hardhat-dependency-compiler/@keep-network/ecdsa/contracts/WalletRegistry.sol +3 -0
- package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol +3 -0
- package/contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol +3 -0
- package/contracts/token/TBTC.sol +1 -1
- package/contracts/vault/DonationVault.sol +125 -0
- package/contracts/vault/IVault.sol +44 -0
- package/contracts/vault/TBTCVault.sol +305 -0
- package/deploy/00_resolve_relay.ts +28 -0
- package/deploy/00_resolve_tbtc_v1_token.ts +1 -1
- package/deploy/01_deploy_tbtc_v2_token.ts +8 -1
- package/deploy/02_deploy_vending_machine.ts +7 -0
- package/deploy/{03_transfer_roles.ts → 03_transfer_vending_machine_roles.ts} +1 -1
- package/deploy/04_deploy_bank.ts +27 -0
- package/deploy/05_deploy_bridge.ts +80 -0
- package/deploy/06_deploy_tbtc_vault.ts +30 -0
- package/deploy/07_bank_update_bridge.ts +19 -0
- package/deploy/08_transfer_bank_ownership.ts +15 -0
- package/deploy/09_transfer_tbtc_vault_ownership.ts +15 -0
- package/deploy/10_transfer_bridge_governance.ts +20 -0
- package/deploy/11_initialize_wallet_owner.ts +18 -0
- package/deploy/11_transfer_proxy_admin_ownership.ts +30 -0
- package/deploy/12_deploy_proxy_admin_with_deputy.ts +33 -0
- package/export/deploy/00_resolve_relay.js +24 -0
- package/export/deploy/00_resolve_tbtc_v1_token.js +24 -0
- package/export/deploy/01_deploy_tbtc_v2_token.js +19 -0
- package/export/deploy/02_deploy_vending_machine.js +25 -0
- package/export/deploy/03_transfer_vending_machine_roles.js +19 -0
- package/export/deploy/04_deploy_bank.js +21 -0
- package/export/deploy/05_deploy_bridge.js +69 -0
- package/export/deploy/06_deploy_tbtc_vault.js +24 -0
- package/export/deploy/07_bank_update_bridge.js +13 -0
- package/export/deploy/08_transfer_bank_ownership.js +11 -0
- package/export/deploy/09_transfer_tbtc_vault_ownership.js +11 -0
- package/export/deploy/10_transfer_bridge_governance.js +11 -0
- package/export/deploy/11_initialize_wallet_owner.js +14 -0
- package/export/deploy/11_transfer_proxy_admin_ownership.js +23 -0
- package/export/deploy/12_deploy_proxy_admin_with_deputy.js +22 -0
- package/export/hardhat.config.js +169 -0
- package/export/test/bank/Bank.test.js +1012 -0
- package/export/test/bridge/Bridge.Deployment.test.js +76 -0
- package/export/test/bridge/Bridge.Deposit.test.js +1834 -0
- package/export/test/bridge/Bridge.Frauds.test.js +1349 -0
- package/export/test/bridge/Bridge.MovingFunds.test.js +2437 -0
- package/export/test/bridge/Bridge.Parameters.test.js +400 -0
- package/export/test/bridge/Bridge.Redemption.test.js +2523 -0
- package/export/test/bridge/Bridge.Vaults.test.js +74 -0
- package/export/test/bridge/Bridge.Wallets.test.js +1017 -0
- package/export/test/bridge/EcdsaLib.test.js +46 -0
- package/export/test/bridge/Heartbeat.test.js +77 -0
- package/export/test/bridge/VendingMachine.Upgrade.test.js +160 -0
- package/export/test/bridge/VendingMachine.test.js +762 -0
- package/export/test/data/deposit-sweep.js +655 -0
- package/export/test/data/ecdsa.js +18 -0
- package/export/test/data/fraud.js +158 -0
- package/export/test/data/moving-funds.js +815 -0
- package/export/test/data/redemption.js +1011 -0
- package/export/test/fixtures/bridge.js +54 -0
- package/export/test/fixtures/index.js +57 -0
- package/export/test/helpers/contract-test-helpers.js +18 -0
- package/export/test/integration/Slashing.test.js +279 -0
- package/export/test/integration/WalleCreation.test.js +66 -0
- package/export/test/integration/utils/ecdsa-wallet-registry.js +137 -0
- package/export/test/integration/utils/fixture.js +77 -0
- package/export/test/integration/utils/gas.js +36 -0
- package/export/test/integration/utils/random-beacon.js +26 -0
- package/export/test/integration/utils/staking.js +19 -0
- package/export/test/vault/DonationVault.test.js +202 -0
- package/export/test/vault/TBTCVault.Redemption.test.js +357 -0
- package/export/test/vault/TBTCVault.test.js +768 -0
- package/export/typechain/BTCUtils.js +2 -0
- package/export/typechain/Bank.js +2 -0
- package/export/typechain/BankStub.js +2 -0
- package/export/typechain/Bridge.js +2 -0
- package/export/typechain/BridgeState.js +2 -0
- package/export/typechain/BridgeStub.js +2 -0
- package/export/typechain/Deposit.js +2 -0
- package/export/typechain/DepositSweep.js +2 -0
- package/export/typechain/DonationVault.js +2 -0
- package/export/typechain/ERC165.js +2 -0
- package/export/typechain/ERC1967Proxy.js +2 -0
- package/export/typechain/ERC1967Upgrade.js +2 -0
- package/export/typechain/ERC20WithPermit.js +2 -0
- package/export/typechain/ERC721.js +2 -0
- package/export/typechain/EcdsaAuthorization.js +2 -0
- package/export/typechain/EcdsaDkg.js +2 -0
- package/export/typechain/EcdsaDkgValidator.js +2 -0
- package/export/typechain/EcdsaInactivity.js +2 -0
- package/export/typechain/Fraud.js +2 -0
- package/export/typechain/Governable.js +2 -0
- package/export/typechain/HeartbeatStub.js +2 -0
- package/export/typechain/IApplication.js +2 -0
- package/export/typechain/IApproveAndCall.js +2 -0
- package/export/typechain/IBeacon.js +2 -0
- package/export/typechain/IERC165.js +2 -0
- package/export/typechain/IERC1822Proxiable.js +2 -0
- package/export/typechain/IERC20.js +2 -0
- package/export/typechain/IERC20Metadata.js +2 -0
- package/export/typechain/IERC20WithPermit.js +2 -0
- package/export/typechain/IERC721.js +2 -0
- package/export/typechain/IERC721Metadata.js +2 -0
- package/export/typechain/IERC721Receiver.js +2 -0
- package/export/typechain/IRandomBeacon.js +2 -0
- package/export/typechain/IRandomBeaconConsumer.js +2 -0
- package/export/typechain/IReceiveApproval.js +2 -0
- package/export/typechain/IReceiveBalanceApproval.js +2 -0
- package/export/typechain/IRelay.js +2 -0
- package/export/typechain/IStaking.js +2 -0
- package/export/typechain/IVault.js +2 -0
- package/export/typechain/IWalletOwner.js +2 -0
- package/export/typechain/IWalletRegistry.js +2 -0
- package/export/typechain/Initializable.js +2 -0
- package/export/typechain/MisfundRecovery.js +2 -0
- package/export/typechain/MovingFunds.js +2 -0
- package/export/typechain/Ownable.js +2 -0
- package/export/typechain/Proxy.js +2 -0
- package/export/typechain/ProxyAdmin.js +2 -0
- package/export/typechain/ReceiveApprovalStub.js +2 -0
- package/export/typechain/Redemption.js +2 -0
- package/export/typechain/Reimbursable.js +2 -0
- package/export/typechain/ReimbursementPool.js +2 -0
- package/export/typechain/Rewards.js +2 -0
- package/export/typechain/SortitionPool.js +2 -0
- package/export/typechain/SortitionTree.js +2 -0
- package/export/typechain/TBTC.js +2 -0
- package/export/typechain/TBTCVault.js +2 -0
- package/export/typechain/TestERC20.js +2 -0
- package/export/typechain/TestERC721.js +2 -0
- package/export/typechain/TestEcdsaLib.js +2 -0
- package/export/typechain/TestRelay.js +2 -0
- package/export/typechain/TransparentUpgradeableProxy.js +2 -0
- package/export/typechain/VendingMachine.js +2 -0
- package/export/typechain/WalletRegistry.js +2 -0
- package/export/typechain/Wallets.js +2 -0
- package/export/typechain/common.js +2 -0
- package/export/typechain/factories/BTCUtils__factory.js +94 -0
- package/export/typechain/factories/BankStub__factory.js +586 -0
- package/export/typechain/factories/Bank__factory.js +573 -0
- package/export/typechain/factories/BridgeState__factory.js +257 -0
- package/export/typechain/factories/BridgeStub__factory.js +2912 -0
- package/export/typechain/factories/Bridge__factory.js +2526 -0
- package/export/typechain/factories/DepositSweep__factory.js +61 -0
- package/export/typechain/factories/Deposit__factory.js +103 -0
- package/export/typechain/factories/DonationVault__factory.js +139 -0
- package/export/typechain/factories/ERC165__factory.js +38 -0
- package/export/typechain/factories/ERC1967Proxy__factory.js +111 -0
- package/export/typechain/factories/ERC1967Upgrade__factory.js +64 -0
- package/export/typechain/factories/ERC20WithPermit__factory.js +524 -0
- package/export/typechain/factories/ERC721__factory.js +388 -0
- package/export/typechain/factories/EcdsaAuthorization__factory.js +211 -0
- package/export/typechain/factories/EcdsaDkgValidator__factory.js +441 -0
- package/export/typechain/factories/EcdsaDkg__factory.js +192 -0
- package/export/typechain/factories/EcdsaInactivity__factory.js +134 -0
- package/export/typechain/factories/Fraud__factory.js +117 -0
- package/export/typechain/factories/Governable__factory.js +64 -0
- package/export/typechain/factories/HeartbeatStub__factory.js +61 -0
- package/export/typechain/factories/IApplication__factory.js +152 -0
- package/export/typechain/factories/IApproveAndCall__factory.js +48 -0
- package/export/typechain/factories/IBeacon__factory.js +32 -0
- package/export/typechain/factories/IERC165__factory.js +38 -0
- package/export/typechain/factories/IERC1822Proxiable__factory.js +32 -0
- package/export/typechain/factories/IERC20Metadata__factory.js +241 -0
- package/export/typechain/factories/IERC20WithPermit__factory.js +389 -0
- package/export/typechain/factories/IERC20__factory.js +202 -0
- package/export/typechain/factories/IERC721Metadata__factory.js +349 -0
- package/export/typechain/factories/IERC721Receiver__factory.js +53 -0
- package/export/typechain/factories/IERC721__factory.js +304 -0
- package/export/typechain/factories/IRandomBeaconConsumer__factory.js +37 -0
- package/export/typechain/factories/IRandomBeacon__factory.js +32 -0
- package/export/typechain/factories/IReceiveApproval__factory.js +47 -0
- package/export/typechain/factories/IReceiveBalanceApproval__factory.js +42 -0
- package/export/typechain/factories/IRelay__factory.js +45 -0
- package/export/typechain/factories/IStaking__factory.js +722 -0
- package/export/typechain/factories/IVault__factory.js +60 -0
- package/export/typechain/factories/IWalletOwner__factory.js +65 -0
- package/export/typechain/factories/IWalletRegistry__factory.js +138 -0
- package/export/typechain/factories/Initializable__factory.js +32 -0
- package/export/typechain/factories/MisfundRecovery__factory.js +145 -0
- package/export/typechain/factories/MovingFunds__factory.js +169 -0
- package/export/typechain/factories/Ownable__factory.js +71 -0
- package/export/typechain/factories/ProxyAdmin__factory.js +191 -0
- package/export/typechain/factories/Proxy__factory.js +27 -0
- package/export/typechain/factories/ReceiveApprovalStub__factory.js +127 -0
- package/export/typechain/factories/Redemption__factory.js +123 -0
- package/export/typechain/factories/Reimbursable__factory.js +58 -0
- package/export/typechain/factories/ReimbursementPool__factory.js +350 -0
- package/export/typechain/factories/Rewards__factory.js +117 -0
- package/export/typechain/factories/SortitionPool__factory.js +610 -0
- package/export/typechain/factories/SortitionTree__factory.js +149 -0
- package/export/typechain/factories/TBTCVault__factory.js +480 -0
- package/export/typechain/factories/TBTC__factory.js +564 -0
- package/export/typechain/factories/TestERC20__factory.js +539 -0
- package/export/typechain/factories/TestERC721__factory.js +421 -0
- package/export/typechain/factories/TestEcdsaLib__factory.js +66 -0
- package/export/typechain/factories/TestRelay__factory.js +94 -0
- package/export/typechain/factories/TransparentUpgradeableProxy__factory.js +186 -0
- package/export/typechain/factories/VendingMachine__factory.js +549 -0
- package/export/typechain/factories/WalletRegistry__factory.js +1919 -0
- package/export/typechain/factories/Wallets__factory.js +143 -0
- package/export/typechain/index.js +132 -0
- package/export.json +15932 -503
- package/package.json +47 -26
- 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
|
+
});
|