@keep-network/tbtc-v2 1.0.3-dev.1 → 1.0.3-dev.3

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 (140) hide show
  1. package/artifacts/BLS.json +1 -1
  2. package/artifacts/Bank.json +3 -3
  3. package/artifacts/BeaconAuthorization.json +1 -1
  4. package/artifacts/BeaconDkg.json +1 -1
  5. package/artifacts/BeaconDkgValidator.json +1 -1
  6. package/artifacts/BeaconInactivity.json +1 -1
  7. package/artifacts/BeaconSortitionPool.json +3 -3
  8. package/artifacts/Bridge.json +5 -5
  9. package/artifacts/BridgeGovernance.json +2 -2
  10. package/artifacts/BridgeGovernanceParameters.json +2 -2
  11. package/artifacts/Deposit.json +2 -2
  12. package/artifacts/DepositSweep.json +2 -2
  13. package/artifacts/DonationVault.json +3 -3
  14. package/artifacts/EcdsaDkgValidator.json +1 -1
  15. package/artifacts/EcdsaInactivity.json +1 -1
  16. package/artifacts/EcdsaSortitionPool.json +3 -3
  17. package/artifacts/Fraud.json +2 -2
  18. package/artifacts/KeepRegistry.json +1 -1
  19. package/artifacts/KeepStake.json +2 -2
  20. package/artifacts/KeepToken.json +2 -2
  21. package/artifacts/KeepTokenStaking.json +1 -1
  22. package/artifacts/LightRelay.json +18 -18
  23. package/artifacts/MaintainerProxy.json +19 -19
  24. package/artifacts/MovingFunds.json +2 -2
  25. package/artifacts/NuCypherStakingEscrow.json +1 -1
  26. package/artifacts/NuCypherToken.json +2 -2
  27. package/artifacts/RandomBeacon.json +2 -2
  28. package/artifacts/RandomBeaconChaosnet.json +2 -2
  29. package/artifacts/RandomBeaconGovernance.json +2 -2
  30. package/artifacts/Redemption.json +2 -2
  31. package/artifacts/ReimbursementPool.json +2 -2
  32. package/artifacts/T.json +2 -2
  33. package/artifacts/TBTC.json +3 -3
  34. package/artifacts/TBTCToken.json +3 -3
  35. package/artifacts/TBTCVault.json +23 -23
  36. package/artifacts/TokenStaking.json +1 -1
  37. package/artifacts/TokenholderGovernor.json +9 -9
  38. package/artifacts/TokenholderTimelock.json +8 -8
  39. package/artifacts/VendingMachine.json +3 -3
  40. package/artifacts/VendingMachineKeep.json +1 -1
  41. package/artifacts/VendingMachineNuCypher.json +1 -1
  42. package/artifacts/VendingMachineV2.json +360 -0
  43. package/artifacts/VendingMachineV3.json +405 -0
  44. package/artifacts/WalletRegistry.json +5 -5
  45. package/artifacts/WalletRegistryGovernance.json +2 -2
  46. package/artifacts/Wallets.json +2 -2
  47. package/artifacts/solcInputs/{5835b0a74f2a5433fcfb8074d31a6e8a.json → b0025f1f7efe4824592ac0c9793776c3.json} +6 -0
  48. package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
  49. package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
  50. package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +1 -1
  51. package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
  52. package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
  53. package/build/contracts/bridge/BridgeGovernanceParameters.sol/BridgeGovernanceParameters.dbg.json +1 -1
  54. package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
  55. package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
  56. package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +1 -1
  57. package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
  58. package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
  59. package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +1 -1
  60. package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
  61. package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
  62. package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
  63. package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
  64. package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
  65. package/build/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.dbg.json +4 -0
  66. package/build/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.json +245 -0
  67. package/build/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.dbg.json +4 -0
  68. package/build/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.json +282 -0
  69. package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
  70. package/build/contracts/l2/L2TBTC.sol/L2TBTC.dbg.json +1 -1
  71. package/build/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.dbg.json +1 -1
  72. package/build/contracts/relay/LightRelay.sol/ILightRelay.dbg.json +1 -1
  73. package/build/contracts/relay/LightRelay.sol/LightRelay.dbg.json +1 -1
  74. package/build/contracts/relay/LightRelay.sol/RelayUtils.dbg.json +1 -1
  75. package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
  76. package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
  77. package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
  78. package/build/contracts/vault/TBTCOptimisticMinting.sol/TBTCOptimisticMinting.dbg.json +1 -1
  79. package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
  80. package/contracts/bridge/VendingMachineV2.sol +114 -0
  81. package/contracts/bridge/VendingMachineV3.sol +134 -0
  82. package/deploy/06_deploy_bridge.ts +7 -0
  83. package/deploy/09_deploy_bridge_governance.ts +2 -0
  84. package/deploy/24_transfer_tbtc_ownership.ts +7 -14
  85. package/deploy/30_deploy_vending_machine_v2.ts +34 -0
  86. package/deploy/31_deploy_vending_machine_v3.ts +34 -0
  87. package/deploy/32_transfer_vending_machine_v2_ownership.ts +19 -0
  88. package/deploy/33_transfer_vending_machine_v3_ownership.ts +19 -0
  89. package/export/artifacts/@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol/BTCUtils.json +20 -20
  90. package/export/artifacts/@keep-network/ecdsa/contracts/EcdsaDkgValidator.sol/EcdsaDkgValidator.json +1986 -1986
  91. package/export/artifacts/@keep-network/ecdsa/contracts/libraries/EcdsaDkg.sol/EcdsaDkg.json +20 -20
  92. package/export/artifacts/@keep-network/ecdsa/contracts/libraries/EcdsaInactivity.sol/EcdsaInactivity.json +1836 -1836
  93. package/export/artifacts/@keep-network/random-beacon/contracts/ReimbursementPool.sol/ReimbursementPool.json +545 -545
  94. package/export/artifacts/@keep-network/sortition-pools/contracts/Chaosnet.sol/Chaosnet.json +506 -506
  95. package/export/artifacts/@keep-network/sortition-pools/contracts/Rewards.sol/Rewards.json +23 -23
  96. package/export/artifacts/@keep-network/sortition-pools/contracts/SortitionPool.sol/SortitionPool.json +2456 -2456
  97. package/export/artifacts/@keep-network/sortition-pools/contracts/SortitionTree.sol/SortitionTree.json +465 -465
  98. package/export/artifacts/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol/ERC1967Proxy.json +568 -568
  99. package/export/artifacts/@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol/ProxyAdmin.json +603 -603
  100. package/export/artifacts/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol/TransparentUpgradeableProxy.json +1001 -1001
  101. package/export/artifacts/@openzeppelin/contracts/token/ERC721/ERC721.sol/ERC721.json +1877 -1877
  102. package/export/artifacts/@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol/ERC20Upgradeable.json +654 -654
  103. package/export/artifacts/@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol/ERC20WithPermit.json +2534 -2534
  104. package/export/artifacts/@thesis/solidity-contracts/contracts/token/MisfundRecovery.sol/MisfundRecovery.json +736 -736
  105. package/export/artifacts/contracts/bank/Bank.sol/Bank.json +1872 -1872
  106. package/export/artifacts/contracts/bridge/Bridge.sol/Bridge.json +7083 -7083
  107. package/export/artifacts/contracts/bridge/VendingMachine.sol/VendingMachine.json +1257 -1257
  108. package/export/artifacts/contracts/bridge/VendingMachineV2.sol/VendingMachineV2.json +6199 -0
  109. package/export/artifacts/contracts/bridge/VendingMachineV3.sol/VendingMachineV3.json +7151 -0
  110. package/export/artifacts/contracts/l2/L2TBTC.sol/L2TBTC.json +3002 -3002
  111. package/export/artifacts/contracts/maintainer/MaintainerProxy.sol/MaintainerProxy.json +2393 -2393
  112. package/export/artifacts/contracts/relay/LightRelay.sol/LightRelay.json +1922 -1922
  113. package/export/artifacts/contracts/test/BankStub.sol/BankStub.json +1874 -1874
  114. package/export/artifacts/contracts/test/BridgeStub.sol/BridgeStub.json +8122 -8122
  115. package/export/artifacts/contracts/test/GoerliLightRelay.sol/GoerliLightRelay.json +1922 -1922
  116. package/export/artifacts/contracts/test/HeartbeatStub.sol/HeartbeatStub.json +130 -130
  117. package/export/artifacts/contracts/test/LightRelayStub.sol/LightRelayStub.json +2047 -2047
  118. package/export/artifacts/contracts/test/ReceiveApprovalStub.sol/ReceiveApprovalStub.json +354 -354
  119. package/export/artifacts/contracts/test/SystemTestRelay.sol/SystemTestRelay.json +568 -568
  120. package/export/artifacts/contracts/test/TestERC20.sol/TestERC20.json +2291 -2291
  121. package/export/artifacts/contracts/test/TestERC721.sol/TestERC721.json +1698 -1698
  122. package/export/artifacts/contracts/test/TestEcdsaLib.sol/TestEcdsaLib.json +189 -189
  123. package/export/artifacts/contracts/token/TBTC.sol/TBTC.json +2847 -2847
  124. package/export/artifacts/contracts/vault/DonationVault.sol/DonationVault.json +858 -858
  125. package/export/artifacts/contracts/vault/TBTCVault.sol/TBTCVault.json +3476 -3476
  126. package/export/deploy/06_deploy_bridge.js +30 -8
  127. package/export/deploy/09_deploy_bridge_governance.js +11 -7
  128. package/export/deploy/24_transfer_tbtc_ownership.js +14 -14
  129. package/export/deploy/30_deploy_vending_machine_v2.js +86 -0
  130. package/export/deploy/31_deploy_vending_machine_v3.js +86 -0
  131. package/export/deploy/32_transfer_vending_machine_v2_ownership.js +60 -0
  132. package/export/deploy/33_transfer_vending_machine_v3_ownership.js +60 -0
  133. package/export/hardhat.config.js +6 -0
  134. package/export/typechain/VendingMachineV2.js +2 -0
  135. package/export/typechain/VendingMachineV3.js +2 -0
  136. package/export/typechain/factories/VendingMachineV2__factory.js +324 -0
  137. package/export/typechain/factories/VendingMachineV3__factory.js +361 -0
  138. package/export/typechain/index.js +5 -1
  139. package/export.json +3362 -3358
  140. package/package.json +1 -1
@@ -286,6 +286,12 @@
286
286
  "contracts/bridge/VendingMachine.sol": {
287
287
  "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\nimport \"@thesis/solidity-contracts/contracts/token/IReceiveApproval.sol\";\n\nimport \"../token/TBTC.sol\";\nimport \"../GovernanceUtils.sol\";\n\n/// @title TBTC v2 Vending Machine\n/// @notice The Vending Machine is the owner of TBTC v2 token and can mint\n/// TBTC v2 tokens in 1:1 ratio from TBTC v1 tokens with TBTC v1\n/// deposited in the contract as collateral. TBTC v2 can be\n/// unminted back to TBTC v1 with or without a fee - fee parameter is\n/// controlled by the Governance. This implementation acts as a bridge\n/// between TBTC v1 and TBTC v2 token, allowing to mint TBTC v2 before\n/// the system is ready and fully operational without sacrificing any\n/// security guarantees and decentralization of the project.\n/// Vending Machine can be upgraded in a two-step, governance-controlled\n/// process. The new version of the Vending Machine will receive the\n/// ownership of TBTC v2 token and entire TBTC v1 balance stored as\n/// collateral. It is expected that this process will be executed before\n/// the v2 system launch. There is an optional unmint fee with a value\n/// that can be updated in a two-step, governance-controlled process.\n/// All governable parameters are controlled by two roles: update\n/// initiator and finalizer. There is a separate initiator role for\n/// unmint fee update and vending machine upgrade. The initiator\n/// proposes the change by initiating the update and the finalizer\n/// (contract owner) may approve it by finalizing the change after the\n/// governance delay passes.\ncontract VendingMachine is Ownable, IReceiveApproval {\n using SafeERC20 for IERC20;\n using SafeERC20 for TBTC;\n\n /// @notice The time delay that needs to pass between initializing and\n /// finalizing update of any governable parameter in this contract.\n uint256 public constant GOVERNANCE_DELAY = 7 days;\n\n /// @notice Divisor for precision purposes. Used to represent fractions\n /// in parameter values.\n uint256 public constant FLOATING_POINT_DIVISOR = 1e18;\n\n IERC20 public immutable tbtcV1;\n TBTC public immutable tbtcV2;\n\n /// @notice The fee for unminting TBTC v2 back into TBTC v1 represented as\n /// 1e18 precision fraction. The fee is proportional to the amount\n /// being unminted and added on the top of the amount being unminted.\n /// To calculate the fee value, the amount being unminted needs\n /// to be multiplied by `unmintFee` and divided by 1e18.\n /// For example, `unmintFee` set to 1000000000000000\n /// means that 0.001 of the amount being unminted needs to be paid\n /// to the `VendingMachine` as an unminting fee on the top of the\n /// amount being unminted.\n uint256 public unmintFee;\n uint256 public newUnmintFee;\n uint256 public unmintFeeUpdateInitiatedTimestamp;\n address public unmintFeeUpdateInitiator;\n\n /// @notice The address of a new vending machine. Set only when the upgrade\n /// process is pending. Once the upgrade gets finalized, the new\n /// vending machine will become an owner of TBTC v2 token.\n address public newVendingMachine;\n uint256 public vendingMachineUpgradeInitiatedTimestamp;\n address public vendingMachineUpgradeInitiator;\n\n event UnmintFeeUpdateInitiated(uint256 newUnmintFee, uint256 timestamp);\n event UnmintFeeUpdated(uint256 newUnmintFee);\n\n event VendingMachineUpgradeInitiated(\n address newVendingMachine,\n uint256 timestamp\n );\n event VendingMachineUpgraded(address newVendingMachine);\n\n event Minted(address indexed recipient, uint256 amount);\n event Unminted(address indexed recipient, uint256 amount, uint256 fee);\n\n modifier only(address authorizedCaller) {\n require(msg.sender == authorizedCaller, \"Caller is not authorized\");\n _;\n }\n\n modifier onlyAfterGovernanceDelay(uint256 changeInitiatedTimestamp) {\n GovernanceUtils.onlyAfterGovernanceDelay(\n changeInitiatedTimestamp,\n GOVERNANCE_DELAY\n );\n _;\n }\n\n constructor(\n IERC20 _tbtcV1,\n TBTC _tbtcV2,\n uint256 _unmintFee\n ) {\n tbtcV1 = _tbtcV1;\n tbtcV2 = _tbtcV2;\n unmintFee = _unmintFee;\n\n unmintFeeUpdateInitiator = msg.sender;\n vendingMachineUpgradeInitiator = msg.sender;\n }\n\n /// @notice Mints TBTC v2 to the caller from TBTC v1 with 1:1 ratio.\n /// The caller needs to have at least `amount` of TBTC v1 balance\n /// approved for transfer to the `VendingMachine` before calling\n /// this function.\n /// @param amount The amount of TBTC v2 to mint from TBTC v1\n function mint(uint256 amount) external {\n _mint(msg.sender, amount);\n }\n\n /// @notice Mints TBTC v2 to `from` address from TBTC v1 with 1:1 ratio.\n /// `from` address needs to have at least `amount` of TBTC v1\n /// balance approved for transfer to the `VendingMachine` before\n /// calling this function.\n /// @dev This function is a shortcut for approve + mint. Only TBTC v1\n /// caller is allowed and only TBTC v1 is allowed as a token to\n /// transfer.\n /// @param from TBTC v1 token holder minting TBTC v2 tokens\n /// @param amount The amount of TBTC v2 to mint from TBTC v1\n /// @param token TBTC v1 token address\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata\n ) external override {\n require(token == address(tbtcV1), \"Token is not TBTC v1\");\n require(msg.sender == address(tbtcV1), \"Only TBTC v1 caller allowed\");\n _mint(from, amount);\n }\n\n /// @notice Unmints TBTC v2 from the caller into TBTC v1. Depending on\n /// `unmintFee` value, may require paying an additional unmint fee\n /// in TBTC v2 in addition to the amount being unminted. To see\n /// what is the value of the fee, please call `unmintFeeFor(amount)`\n /// function. The caller needs to have at least\n /// `amount + unmintFeeFor(amount)` of TBTC v2 balance approved for\n /// transfer to the `VendingMachine` before calling this function.\n /// @param amount The amount of TBTC v2 to unmint to TBTC v1\n function unmint(uint256 amount) external {\n uint256 fee = unmintFeeFor(amount);\n emit Unminted(msg.sender, amount, fee);\n\n require(\n tbtcV2.balanceOf(msg.sender) >= amount + fee,\n \"Amount + fee exceeds TBTC v2 balance\"\n );\n\n tbtcV2.safeTransferFrom(msg.sender, address(this), fee);\n tbtcV2.burnFrom(msg.sender, amount);\n tbtcV1.safeTransfer(msg.sender, amount);\n }\n\n /// @notice Allows the Governance to withdraw unmint fees accumulated by\n /// `VendingMachine`.\n /// @param recipient The address receiving the fees\n /// @param amount The amount of fees in TBTC v2 to withdraw\n function withdrawFees(address recipient, uint256 amount)\n external\n onlyOwner\n {\n tbtcV2.safeTransfer(recipient, amount);\n }\n\n /// @notice Initiates unmint fee update process. The update process needs to\n /// be finalized with a call to `finalizeUnmintFeeUpdate` function\n /// after the `GOVERNANCE_DELAY` passes. Only unmint fee update\n /// initiator role can initiate the update.\n /// @param _newUnmintFee The new unmint fee\n function initiateUnmintFeeUpdate(uint256 _newUnmintFee)\n external\n only(unmintFeeUpdateInitiator)\n {\n /* solhint-disable-next-line not-rely-on-time */\n emit UnmintFeeUpdateInitiated(_newUnmintFee, block.timestamp);\n newUnmintFee = _newUnmintFee;\n /* solhint-disable-next-line not-rely-on-time */\n unmintFeeUpdateInitiatedTimestamp = block.timestamp;\n }\n\n /// @notice Allows the contract owner to finalize unmint fee update process.\n /// The update process needs to be first initiated with a call to\n /// `initiateUnmintFeeUpdate` and the `GOVERNANCE_DELAY` needs to\n /// pass.\n function finalizeUnmintFeeUpdate()\n external\n onlyOwner\n onlyAfterGovernanceDelay(unmintFeeUpdateInitiatedTimestamp)\n {\n emit UnmintFeeUpdated(newUnmintFee);\n unmintFee = newUnmintFee;\n newUnmintFee = 0;\n unmintFeeUpdateInitiatedTimestamp = 0;\n }\n\n /// @notice Initiates vending machine upgrade process. The upgrade process\n /// needs to be finalized with a call to\n /// `finalizeVendingMachineUpgrade` function after the\n /// `GOVERNANCE_DELAY` passes. Only vending machine upgrade\n /// initiator role can initiate the upgrade.\n /// @param _newVendingMachine The new vending machine address\n function initiateVendingMachineUpgrade(address _newVendingMachine)\n external\n only(vendingMachineUpgradeInitiator)\n {\n require(\n _newVendingMachine != address(0),\n \"New VendingMachine cannot be zero address\"\n );\n\n emit VendingMachineUpgradeInitiated(\n _newVendingMachine,\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp\n );\n newVendingMachine = _newVendingMachine;\n /* solhint-disable-next-line not-rely-on-time */\n vendingMachineUpgradeInitiatedTimestamp = block.timestamp;\n }\n\n /// @notice Allows the contract owner to finalize vending machine upgrade\n /// process. The upgrade process needs to be first initiated with a\n /// call to `initiateVendingMachineUpgrade` and the `GOVERNANCE_DELAY`\n /// needs to pass. Once the upgrade is finalized, the new vending\n /// machine will become an owner of TBTC v2 token and all TBTC v1\n /// held by this contract will be transferred to the new vending\n /// machine.\n function finalizeVendingMachineUpgrade()\n external\n onlyOwner\n onlyAfterGovernanceDelay(vendingMachineUpgradeInitiatedTimestamp)\n {\n emit VendingMachineUpgraded(newVendingMachine);\n //slither-disable-next-line reentrancy-no-eth\n tbtcV2.transferOwnership(newVendingMachine);\n tbtcV1.safeTransfer(newVendingMachine, tbtcV1.balanceOf(address(this)));\n newVendingMachine = address(0);\n vendingMachineUpgradeInitiatedTimestamp = 0;\n }\n\n /// @notice Transfers unmint fee update initiator role to another address.\n /// Can be called only by the current unmint fee update initiator.\n /// @param newInitiator The new unmint fee update initiator\n function transferUnmintFeeUpdateInitiatorRole(address newInitiator)\n external\n only(unmintFeeUpdateInitiator)\n {\n require(\n newInitiator != address(0),\n \"New initiator must not be zero address\"\n );\n unmintFeeUpdateInitiator = newInitiator;\n }\n\n /// @notice Transfers vending machine upgrade initiator role to another\n /// address. Can be called only by the current vending machine\n /// upgrade initiator.\n /// @param newInitiator The new vending machine upgrade initiator\n function transferVendingMachineUpgradeInitiatorRole(address newInitiator)\n external\n only(vendingMachineUpgradeInitiator)\n {\n require(\n newInitiator != address(0),\n \"New initiator must not be zero address\"\n );\n vendingMachineUpgradeInitiator = newInitiator;\n }\n\n /// @notice Get the remaining time that needs to pass until unmint fee\n /// update can be finalized by the Governance. If the update has\n /// not been initiated, the function reverts.\n function getRemainingUnmintFeeUpdateTime() external view returns (uint256) {\n return\n GovernanceUtils.getRemainingGovernanceDelay(\n unmintFeeUpdateInitiatedTimestamp,\n GOVERNANCE_DELAY\n );\n }\n\n /// @notice Get the remaining time that needs to pass until vending machine\n /// upgrade can be finalized by the Governance. If the upgrade has\n /// not been initiated, the function reverts.\n function getRemainingVendingMachineUpgradeTime()\n external\n view\n returns (uint256)\n {\n return\n GovernanceUtils.getRemainingGovernanceDelay(\n vendingMachineUpgradeInitiatedTimestamp,\n GOVERNANCE_DELAY\n );\n }\n\n /// @notice Calculates the fee that needs to be paid to the `VendingMachine`\n /// to unmint the given amount of TBTC v2 back into TBTC v1.\n function unmintFeeFor(uint256 amount) public view returns (uint256) {\n return (amount * unmintFee) / FLOATING_POINT_DIVISOR;\n }\n\n function _mint(address tokenOwner, uint256 amount) internal {\n emit Minted(tokenOwner, amount);\n tbtcV1.safeTransferFrom(tokenOwner, address(this), amount);\n tbtcV2.mint(tokenOwner, amount);\n }\n}\n"
288
288
  },
289
+ "contracts/bridge/VendingMachineV2.sol": {
290
+ "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"../token/TBTC.sol\";\n\n/// @title VendingMachineV2\n/// @notice VendingMachineV2 is used to exchange tBTC v1 to tBTC v2 in a 1:1\n/// ratio during the process of tBTC v1 bridge sunsetting. The redeemer\n/// selected by the DAO based on the \"TIP-027b tBTC v1: The Sunsetting\"\n/// proposal will deposit tBTC v2 tokens into VendingMachineV2 so that\n/// outstanding tBTC v1 token owners can upgrade to tBTC v2 tokens.\n/// The redeemer will withdraw the tBTC v1 tokens deposited into the\n/// contract to perform tBTC v1 redemptions.\n/// The redeemer may decide to withdraw their deposited tBTC v2 at any\n/// moment in time. The amount withdrawable is lower than the amount\n/// deposited in case tBTC v1 was exchanged for tBTC v2.\n/// This contract is owned by the redeemer.\ncontract VendingMachineV2 is Ownable {\n using SafeERC20 for IERC20;\n using SafeERC20 for TBTC;\n\n IERC20 public immutable tbtcV1;\n TBTC public immutable tbtcV2;\n\n event Exchanged(address indexed to, uint256 amount);\n event Deposited(address from, uint256 amount);\n event Withdrawn(address token, address to, uint256 amount);\n\n constructor(IERC20 _tbtcV1, TBTC _tbtcV2) {\n tbtcV1 = _tbtcV1;\n tbtcV2 = _tbtcV2;\n }\n\n /// @notice Exchange tBTC v1 for tBTC v2 in a 1:1 ratio.\n /// The caller needs to have at least `amount` of tBTC v1 balance\n /// approved for transfer to the `VendingMachineV2` before calling\n /// this function.\n /// @param amount The amount of tBTC v1 to exchange for tBTC v2.\n function exchange(uint256 amount) external {\n _exchange(msg.sender, amount);\n }\n\n /// @notice Exchange tBTC v1 for tBTC v2 in a 1:1 ratio.\n /// The caller needs to have at least `amount` of tBTC v1 balance\n /// approved for transfer to the `VendingMachineV2` before calling\n /// this function.\n /// @dev This function is a shortcut for `approve` + `exchange`. Only tBTC\n /// v1 token caller is allowed and only tBTC v1 is allowed as a token\n /// to transfer.\n /// @param from tBTC v1 token holder exchanging tBTC v1 to tBTC v2.\n /// @param amount The amount of tBTC v1 to exchange for tBTC v2.\n /// @param token tBTC v1 token address.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata\n ) external {\n require(token == address(tbtcV1), \"Token is not tBTC v1\");\n require(msg.sender == address(tbtcV1), \"Only tBTC v1 caller allowed\");\n _exchange(from, amount);\n }\n\n /// @notice Allows to deposit tBTC v2 tokens to the contract.\n /// VendingMachineV2 can not mint tBTC v2 tokens so tBTC v2 needs\n /// to be deposited into the contract so that tBTC v1 to tBTC v2\n /// exchange can happen.\n /// The caller needs to have at least `amount` of tBTC v2 balance\n /// approved for transfer to the `VendingMachineV2` before calling\n /// this function.\n /// @dev This function is for the redeemer and tBTC v1 operators. This is\n /// NOT a function for tBTC v1 token holders.\n /// @param amount The amount of tBTC v2 to deposit into the contract.\n function depositTbtcV2(uint256 amount) external {\n emit Deposited(msg.sender, amount);\n tbtcV2.safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /// @notice Allows the contract owner to withdraw tokens. This function is\n /// used in two cases: 1) when the redeemer wants to redeem tBTC v1\n /// tokens to perform tBTC v2 redemptions; 2) when the deadline for\n /// tBTC v1 -> tBTC v2 exchange passed and the redeemer wants their\n /// tBTC v2 back.\n /// @dev This function is for the redeemer. This is NOT a function for\n /// tBTC v1 token holders.\n /// @param token The address of a token to withdraw.\n /// @param recipient The address which should receive withdrawn tokens.\n /// @param amount The amount to withdraw.\n function withdrawFunds(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n emit Withdrawn(address(token), recipient, amount);\n token.safeTransfer(recipient, amount);\n }\n\n function _exchange(address tokenOwner, uint256 amount) internal {\n require(\n tbtcV2.balanceOf(address(this)) >= amount,\n \"Not enough tBTC v2 available in the Vending Machine\"\n );\n\n emit Exchanged(tokenOwner, amount);\n tbtcV1.safeTransferFrom(tokenOwner, address(this), amount);\n\n tbtcV2.safeTransfer(tokenOwner, amount);\n }\n}\n"
291
+ },
292
+ "contracts/bridge/VendingMachineV3.sol": {
293
+ "content": "// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.8.17;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport \"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol\";\n\nimport \"../token/TBTC.sol\";\n\n/// @title VendingMachineV3\n/// @notice VendingMachineV3 is used to exchange tBTC v1 to tBTC v2 in a 1:1\n/// ratio after the tBTC v1 bridge sunsetting is completed. Since\n/// tBTC v1 bridge is no longer working, tBTC v1 tokens can not be used\n/// to perform BTC redemptions. This contract allows tBTC v1 owners to\n/// upgrade to tBTC v2 without any deadline. This way, tBTC v1 tokens\n/// left on the market are always backed by Bitcoin. The governance will\n/// deposit tBTC v2 into the contract in the amount equal to tBTC v1\n/// supply. The governance is allowed to withdraw tBTC v2 only if tBTC\n/// v2 left in this contract is enough to cover the upgrade of all tBTC\n/// v1 left on the market. This contract is owned by the governance.\ncontract VendingMachineV3 is Ownable {\n using SafeERC20 for IERC20;\n using SafeERC20 for TBTC;\n\n IERC20 public immutable tbtcV1;\n TBTC public immutable tbtcV2;\n\n event Exchanged(address indexed to, uint256 amount);\n event Deposited(address from, uint256 amount);\n event TbtcV2Withdrawn(address to, uint256 amount);\n event FundsRecovered(address token, address to, uint256 amount);\n\n constructor(IERC20 _tbtcV1, TBTC _tbtcV2) {\n tbtcV1 = _tbtcV1;\n tbtcV2 = _tbtcV2;\n }\n\n /// @notice Exchange tBTC v1 for tBTC v2 in a 1:1 ratio.\n /// The caller needs to have at least `amount` of tBTC v1 balance\n /// approved for transfer to the `VendingMachineV3` before calling\n /// this function.\n /// @param amount The amount of tBTC v1 to exchange for tBTC v2.\n function exchange(uint256 amount) external {\n _exchange(msg.sender, amount);\n }\n\n /// @notice Exchange tBTC v1 for tBTC v2 in a 1:1 ratio.\n /// The caller needs to have at least `amount` of tBTC v1 balance\n /// approved for transfer to the `VendingMachineV3` before calling\n /// this function.\n /// @dev This function is a shortcut for `approve` + `exchange`. Only tBTC\n /// v1 caller is allowed and only tBTC v1 is allowed as a token to\n /// transfer.\n /// @param from tBTC v1 token holder exchanging tBTC v1 to tBTC v2.\n /// @param amount The amount of tBTC v1 to exchange for tBTC v2.\n /// @param token tBTC v1 token address.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata\n ) external {\n require(token == address(tbtcV1), \"Token is not tBTC v1\");\n require(msg.sender == address(tbtcV1), \"Only tBTC v1 caller allowed\");\n _exchange(from, amount);\n }\n\n /// @notice Allows to deposit tBTC v2 tokens to the contract.\n /// `VendingMachineV3` can not mint tBTC v2 tokens so tBTC v2 needs\n /// to be deposited into the contract so that tBTC v1 to tBTC v2\n /// exchange can happen.\n /// The caller needs to have at least `amount` of tBTC v2 balance\n /// approved for transfer to the `VendingMachineV3` before calling\n /// this function.\n /// @dev This function is for the redeemer and tBTC v1 operators. This is\n /// NOT a function for tBTC v1 token holders.\n /// @param amount The amount of tBTC v2 to deposit into the contract.\n function depositTbtcV2(uint256 amount) external {\n emit Deposited(msg.sender, amount);\n tbtcV2.safeTransferFrom(msg.sender, address(this), amount);\n }\n\n /// @notice Allows the governance to withdraw tBTC v2 deposited into this\n /// contract. The governance is allowed to withdraw tBTC v2\n /// only if tBTC v2 left in this contract is enough to cover the\n /// upgrade of all tBTC v1 left on the market.\n /// @param recipient The address which should receive withdrawn tokens.\n /// @param amount The amount to withdraw.\n function withdrawTbtcV2(address recipient, uint256 amount)\n external\n onlyOwner\n {\n require(\n tbtcV1.totalSupply() <= tbtcV2.balanceOf(address(this)) - amount,\n \"tBTC v1 must not be left unbacked\"\n );\n\n emit TbtcV2Withdrawn(recipient, amount);\n tbtcV2.safeTransfer(recipient, amount);\n }\n\n /// @notice Allows the governance to recover ERC20 sent to this contract\n /// by mistake or tBTC v1 locked in the contract to exchange to\n /// tBTC v2. No tBTC v2 can be withdrawn using this function.\n /// @param token The address of a token to recover.\n /// @param recipient The address which should receive recovered tokens.\n /// @param amount The amount to recover.\n function recoverFunds(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyOwner {\n require(\n address(token) != address(tbtcV2),\n \"tBTC v2 tokens can not be recovered, use withdrawTbtcV2 instead\"\n );\n\n emit FundsRecovered(address(token), recipient, amount);\n token.safeTransfer(recipient, amount);\n }\n\n function _exchange(address tokenOwner, uint256 amount) internal {\n require(\n tbtcV2.balanceOf(address(this)) >= amount,\n \"Not enough tBTC v2 available in the Vending Machine\"\n );\n\n emit Exchanged(tokenOwner, amount);\n tbtcV1.safeTransferFrom(tokenOwner, address(this), amount);\n\n tbtcV2.safeTransfer(tokenOwner, amount);\n }\n}\n"
294
+ },
289
295
  "contracts/bridge/Wallets.sol": {
290
296
  "content": "// SPDX-License-Identifier: GPL-3.0-only\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity 0.8.17;\n\nimport {BTCUtils} from \"@keep-network/bitcoin-spv-sol/contracts/BTCUtils.sol\";\nimport {EcdsaDkg} from \"@keep-network/ecdsa/contracts/libraries/EcdsaDkg.sol\";\nimport {Math} from \"@openzeppelin/contracts/utils/math/Math.sol\";\n\nimport \"./BitcoinTx.sol\";\nimport \"./EcdsaLib.sol\";\nimport \"./BridgeState.sol\";\n\n/// @title Wallet library\n/// @notice Library responsible for handling integration between Bridge\n/// contract and ECDSA wallets.\nlibrary Wallets {\n using BTCUtils for bytes;\n\n /// @notice Represents wallet state:\n enum WalletState {\n /// @dev The wallet is unknown to the Bridge.\n Unknown,\n /// @dev The wallet can sweep deposits and accept redemption requests.\n Live,\n /// @dev The wallet was deemed unhealthy and is expected to move their\n /// outstanding funds to another wallet. The wallet can still\n /// fulfill their pending redemption requests although new\n /// redemption requests and new deposit reveals are not accepted.\n MovingFunds,\n /// @dev The wallet moved or redeemed all their funds and is in the\n /// closing period where it is still a subject of fraud challenges\n /// and must defend against them. This state is needed to protect\n /// against deposit frauds on deposits revealed but not swept.\n /// The closing period must be greater that the deposit refund\n /// time plus some time margin.\n Closing,\n /// @dev The wallet finalized the closing period successfully and\n /// can no longer perform any action in the Bridge.\n Closed,\n /// @dev The wallet committed a fraud that was reported, did not move\n /// funds to another wallet before a timeout, or did not sweep\n /// funds moved to if from another wallet before a timeout. The\n /// wallet is blocked and can not perform any actions in the Bridge.\n /// Off-chain coordination with the wallet operators is needed to\n /// recover funds.\n Terminated\n }\n\n /// @notice Holds information about a wallet.\n struct Wallet {\n // Identifier of a ECDSA Wallet registered in the ECDSA Wallet Registry.\n bytes32 ecdsaWalletID;\n // Latest wallet's main UTXO hash computed as\n // keccak256(txHash | txOutputIndex | txOutputValue). The `tx` prefix\n // refers to the transaction which created that main UTXO. The `txHash`\n // is `bytes32` (ordered as in Bitcoin internally), `txOutputIndex`\n // an `uint32`, and `txOutputValue` an `uint64` value.\n bytes32 mainUtxoHash;\n // The total redeemable value of pending redemption requests targeting\n // that wallet.\n uint64 pendingRedemptionsValue;\n // UNIX timestamp the wallet was created at.\n // XXX: Unsigned 32-bit int unix seconds, will break February 7th 2106.\n uint32 createdAt;\n // UNIX timestamp indicating the moment the wallet was requested to\n // move their funds.\n // XXX: Unsigned 32-bit int unix seconds, will break February 7th 2106.\n uint32 movingFundsRequestedAt;\n // UNIX timestamp indicating the moment the wallet's closing period\n // started.\n // XXX: Unsigned 32-bit int unix seconds, will break February 7th 2106.\n uint32 closingStartedAt;\n // Total count of pending moved funds sweep requests targeting this wallet.\n uint32 pendingMovedFundsSweepRequestsCount;\n // Current state of the wallet.\n WalletState state;\n // Moving funds target wallet commitment submitted by the wallet. It\n // is built by applying the keccak256 hash over the list of 20-byte\n // public key hashes of the target wallets.\n bytes32 movingFundsTargetWalletsCommitmentHash;\n // This struct doesn't contain `__gap` property as the structure is stored\n // in a mapping, mappings store values in different slots and they are\n // not contiguous with other values.\n }\n\n event NewWalletRequested();\n\n event NewWalletRegistered(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletMovingFunds(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosing(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletClosed(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n event WalletTerminated(\n bytes32 indexed ecdsaWalletID,\n bytes20 indexed walletPubKeyHash\n );\n\n /// @notice Requests creation of a new wallet. This function just\n /// forms a request and the creation process is performed\n /// asynchronously. Outcome of that process should be delivered\n /// using `registerNewWallet` function.\n /// @param activeWalletMainUtxo Data of the active wallet's main UTXO, as\n /// currently known on the Ethereum chain.\n /// @dev Requirements:\n /// - `activeWalletMainUtxo` components must point to the recent main\n /// UTXO of the given active wallet, as currently known on the\n /// Ethereum chain. If there is no active wallet at the moment, or\n /// the active wallet has no main UTXO, this parameter can be\n /// empty as it is ignored,\n /// - Wallet creation must not be in progress,\n /// - If the active wallet is set, one of the following\n /// conditions must be true:\n /// - The active wallet BTC balance is above the minimum threshold\n /// and the active wallet is old enough, i.e. the creation period\n /// was elapsed since its creation time,\n /// - The active wallet BTC balance is above the maximum threshold.\n function requestNewWallet(\n BridgeState.Storage storage self,\n BitcoinTx.UTXO calldata activeWalletMainUtxo\n ) external {\n require(\n self.ecdsaWalletRegistry.getWalletCreationState() ==\n EcdsaDkg.State.IDLE,\n \"Wallet creation already in progress\"\n );\n\n bytes20 activeWalletPubKeyHash = self.activeWalletPubKeyHash;\n\n // If the active wallet is set, fetch this wallet's details from\n // storage to perform conditions check. The `registerNewWallet`\n // function guarantees an active wallet is always one of the\n // registered ones.\n if (activeWalletPubKeyHash != bytes20(0)) {\n uint64 activeWalletBtcBalance = getWalletBtcBalance(\n self,\n activeWalletPubKeyHash,\n activeWalletMainUtxo\n );\n uint32 activeWalletCreatedAt = self\n .registeredWallets[activeWalletPubKeyHash]\n .createdAt;\n /* solhint-disable-next-line not-rely-on-time */\n bool activeWalletOldEnough = block.timestamp >=\n activeWalletCreatedAt + self.walletCreationPeriod;\n\n require(\n (activeWalletOldEnough &&\n activeWalletBtcBalance >=\n self.walletCreationMinBtcBalance) ||\n activeWalletBtcBalance >= self.walletCreationMaxBtcBalance,\n \"Wallet creation conditions are not met\"\n );\n }\n\n emit NewWalletRequested();\n\n self.ecdsaWalletRegistry.requestNewWallet();\n }\n\n /// @notice Registers a new wallet. This function should be called\n /// after the wallet creation process initiated using\n /// `requestNewWallet` completes and brings the outcomes.\n /// @param ecdsaWalletID Wallet's unique identifier.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Given wallet data must not belong to an already registered wallet.\n function registerNewWallet(\n BridgeState.Storage storage self,\n bytes32 ecdsaWalletID,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external {\n require(\n msg.sender == address(self.ecdsaWalletRegistry),\n \"Caller is not the ECDSA Wallet Registry\"\n );\n\n // Compress wallet's public key and calculate Bitcoin's hash160 of it.\n bytes20 walletPubKeyHash = bytes20(\n EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160View()\n );\n\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n require(\n wallet.state == WalletState.Unknown,\n \"ECDSA wallet has been already registered\"\n );\n wallet.ecdsaWalletID = ecdsaWalletID;\n wallet.state = WalletState.Live;\n /* solhint-disable-next-line not-rely-on-time */\n wallet.createdAt = uint32(block.timestamp);\n\n // Set the freshly created wallet as the new active wallet.\n self.activeWalletPubKeyHash = walletPubKeyHash;\n\n self.liveWalletsCount++;\n\n emit NewWalletRegistered(ecdsaWalletID, walletPubKeyHash);\n }\n\n /// @notice Handles a notification about a wallet redemption timeout.\n /// Triggers the wallet moving funds process only if the wallet is\n /// still in the Live state. That means multiple action timeouts can\n /// be reported for the same wallet but only the first report\n /// requests the wallet to move their funds. Executes slashing if\n /// the wallet is in Live or MovingFunds state. Allows to notify\n /// redemption timeout also for a Terminated wallet in case the\n /// redemption was requested before the wallet got terminated.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The wallet must be in the `Live`, `MovingFunds`,\n /// or `Terminated` state.\n function notifyWalletRedemptionTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n WalletState walletState = wallet.state;\n\n require(\n walletState == WalletState.Live ||\n walletState == WalletState.MovingFunds ||\n walletState == WalletState.Terminated,\n \"Wallet must be in Live or MovingFunds or Terminated state\"\n );\n\n if (\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds\n ) {\n // Slash the wallet operators and reward the notifier\n self.ecdsaWalletRegistry.seize(\n self.redemptionTimeoutSlashingAmount,\n self.redemptionTimeoutNotifierRewardMultiplier,\n msg.sender,\n wallet.ecdsaWalletID,\n walletMembersIDs\n );\n }\n\n if (walletState == WalletState.Live) {\n moveFunds(self, walletPubKeyHash);\n }\n }\n\n /// @notice Handles a notification about a wallet heartbeat failure and\n /// triggers the wallet moving funds process.\n /// @param publicKeyX Wallet's public key's X coordinate.\n /// @param publicKeyY Wallet's public key's Y coordinate.\n /// @dev Requirements:\n /// - The only caller authorized to call this function is `registry`,\n /// - Wallet must be in Live state.\n function notifyWalletHeartbeatFailed(\n BridgeState.Storage storage self,\n bytes32 publicKeyX,\n bytes32 publicKeyY\n ) external {\n require(\n msg.sender == address(self.ecdsaWalletRegistry),\n \"Caller is not the ECDSA Wallet Registry\"\n );\n\n // Compress wallet's public key and calculate Bitcoin's hash160 of it.\n bytes20 walletPubKeyHash = bytes20(\n EcdsaLib.compressPublicKey(publicKeyX, publicKeyY).hash160View()\n );\n\n require(\n self.registeredWallets[walletPubKeyHash].state == WalletState.Live,\n \"Wallet must be in Live state\"\n );\n\n moveFunds(self, walletPubKeyHash);\n }\n\n /// @notice Notifies that the wallet is either old enough or has too few\n /// satoshis left and qualifies to be closed.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMainUtxo Data of the wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @dev Requirements:\n /// - Wallet must not be set as the current active wallet,\n /// - Wallet must exceed the wallet maximum age OR the wallet BTC\n /// balance must be lesser than the minimum threshold. If the latter\n /// case is true, the `walletMainUtxo` components must point to the\n /// recent main UTXO of the given wallet, as currently known on the\n /// Ethereum chain. If the wallet has no main UTXO, this parameter\n /// can be empty as it is ignored since the wallet balance is\n /// assumed to be zero,\n /// - Wallet must be in Live state.\n function notifyWalletCloseable(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo\n ) external {\n require(\n self.activeWalletPubKeyHash != walletPubKeyHash,\n \"Active wallet cannot be considered closeable\"\n );\n\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n require(\n wallet.state == WalletState.Live,\n \"Wallet must be in Live state\"\n );\n\n /* solhint-disable-next-line not-rely-on-time */\n bool walletOldEnough = block.timestamp >=\n wallet.createdAt + self.walletMaxAge;\n\n require(\n walletOldEnough ||\n getWalletBtcBalance(self, walletPubKeyHash, walletMainUtxo) <\n self.walletClosureMinBtcBalance,\n \"Wallet needs to be old enough or have too few satoshis\"\n );\n\n moveFunds(self, walletPubKeyHash);\n }\n\n /// @notice Notifies about the end of the closing period for the given wallet.\n /// Closes the wallet ultimately and notifies the ECDSA registry\n /// about this fact.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The wallet must be in the Closing state,\n /// - The wallet closing period must have elapsed.\n function notifyWalletClosingPeriodElapsed(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n\n require(\n wallet.state == WalletState.Closing,\n \"Wallet must be in Closing state\"\n );\n\n require(\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp >\n wallet.closingStartedAt + self.walletClosingPeriod,\n \"Closing period has not elapsed yet\"\n );\n\n finalizeWalletClosing(self, walletPubKeyHash);\n }\n\n /// @notice Notifies that the wallet completed the moving funds process\n /// successfully. Checks if the funds were moved to the expected\n /// target wallets. Closes the source wallet if everything went\n /// good and reverts otherwise.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param targetWalletsHash 32-byte keccak256 hash over the list of\n /// 20-byte public key hashes of the target wallets actually used\n /// within the moving funds transactions.\n /// @dev Requirements:\n /// - The caller must make sure the moving funds transaction actually\n /// happened on Bitcoin chain and fits the protocol requirements,\n /// - The source wallet must be in the MovingFunds state,\n /// - The target wallets commitment must be submitted by the source\n /// wallet,\n /// - The actual target wallets used in the moving funds transaction\n /// must be exactly the same as the target wallets commitment.\n function notifyWalletFundsMoved(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n bytes32 targetWalletsHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n // Check that the wallet is in the MovingFunds state but don't check\n // if the moving funds timeout is exceeded. That should give a\n // possibility to move funds in case when timeout was hit but was\n // not reported yet.\n require(\n wallet.state == WalletState.MovingFunds,\n \"Wallet must be in MovingFunds state\"\n );\n\n bytes32 targetWalletsCommitmentHash = wallet\n .movingFundsTargetWalletsCommitmentHash;\n\n require(\n targetWalletsCommitmentHash != bytes32(0),\n \"Target wallets commitment not submitted yet\"\n );\n\n // Make sure that the target wallets where funds were moved to are\n // exactly the same as the ones the source wallet committed to.\n require(\n targetWalletsCommitmentHash == targetWalletsHash,\n \"Target wallets don't correspond to the commitment\"\n );\n\n // If funds were moved, the wallet has no longer a main UTXO.\n delete wallet.mainUtxoHash;\n\n beginWalletClosing(self, walletPubKeyHash);\n }\n\n /// @notice Called when a MovingFunds wallet has a balance below the dust\n /// threshold. Begins the wallet closing.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state.\n function notifyWalletMovingFundsBelowDust(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n WalletState walletState = self\n .registeredWallets[walletPubKeyHash]\n .state;\n\n require(\n walletState == Wallets.WalletState.MovingFunds,\n \"Wallet must be in MovingFunds state\"\n );\n\n beginWalletClosing(self, walletPubKeyHash);\n }\n\n /// @notice Called when the timeout for MovingFunds for the wallet elapsed.\n /// Slashes wallet members and terminates the wallet.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The wallet must be in the MovingFunds state.\n function notifyWalletMovingFundsTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs\n ) internal {\n Wallets.Wallet storage wallet = self.registeredWallets[\n walletPubKeyHash\n ];\n\n require(\n wallet.state == Wallets.WalletState.MovingFunds,\n \"Wallet must be in MovingFunds state\"\n );\n\n self.ecdsaWalletRegistry.seize(\n self.movingFundsTimeoutSlashingAmount,\n self.movingFundsTimeoutNotifierRewardMultiplier,\n msg.sender,\n wallet.ecdsaWalletID,\n walletMembersIDs\n );\n\n terminateWallet(self, walletPubKeyHash);\n }\n\n /// @notice Called when a wallet which was asked to sweep funds moved from\n /// another wallet did not provide a sweeping proof before a timeout.\n /// Slashes and terminates the wallet who failed to provide a proof.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet which was\n /// supposed to sweep funds.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @dev Requirements:\n /// - The wallet must be in the `Live`, `MovingFunds`,\n /// or `Terminated` state.\n function notifyWalletMovedFundsSweepTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n WalletState walletState = wallet.state;\n\n require(\n walletState == WalletState.Live ||\n walletState == WalletState.MovingFunds ||\n walletState == WalletState.Terminated,\n \"Wallet must be in Live or MovingFunds or Terminated state\"\n );\n\n if (\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds\n ) {\n self.ecdsaWalletRegistry.seize(\n self.movedFundsSweepTimeoutSlashingAmount,\n self.movedFundsSweepTimeoutNotifierRewardMultiplier,\n msg.sender,\n wallet.ecdsaWalletID,\n walletMembersIDs\n );\n\n terminateWallet(self, walletPubKeyHash);\n }\n }\n\n /// @notice Called when a wallet which was challenged for a fraud did not\n /// defeat the challenge before the timeout. Slashes and terminates\n /// the wallet who failed to defeat the challenge. If the wallet is\n /// already terminated, it does nothing.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet which was\n /// supposed to sweep funds.\n /// @param walletMembersIDs Identifiers of the wallet signing group members.\n /// @param challenger Address of the party which submitted the fraud\n /// challenge.\n /// @dev Requirements:\n /// - The wallet must be in the `Live`, `MovingFunds`, `Closing`\n /// or `Terminated` state.\n function notifyWalletFraudChallengeDefeatTimeout(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n uint32[] calldata walletMembersIDs,\n address challenger\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n WalletState walletState = wallet.state;\n\n if (\n walletState == Wallets.WalletState.Live ||\n walletState == Wallets.WalletState.MovingFunds ||\n walletState == Wallets.WalletState.Closing\n ) {\n self.ecdsaWalletRegistry.seize(\n self.fraudSlashingAmount,\n self.fraudNotifierRewardMultiplier,\n challenger,\n wallet.ecdsaWalletID,\n walletMembersIDs\n );\n\n terminateWallet(self, walletPubKeyHash);\n } else if (walletState == Wallets.WalletState.Terminated) {\n // This is a special case when the wallet was already terminated\n // due to a previous deliberate protocol violation. In that\n // case, this function should be still callable for other fraud\n // challenges timeouts in order to let the challenger unlock its\n // ETH deposit back. However, the wallet termination logic is\n // not called and the challenger is not rewarded.\n } else {\n revert(\n \"Wallet must be in Live or MovingFunds or Closing or Terminated state\"\n );\n }\n }\n\n /// @notice Requests a wallet to move their funds. If the wallet balance\n /// is zero, the wallet closing begins immediately. If the move\n /// funds request refers to the current active wallet, such a wallet\n /// is no longer considered active and the active wallet slot\n /// is unset allowing to trigger a new wallet creation immediately.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The caller must make sure that the wallet is in the Live state.\n function moveFunds(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n\n if (wallet.mainUtxoHash == bytes32(0)) {\n // If the wallet has no main UTXO, that means its BTC balance\n // is zero and the wallet closing should begin immediately.\n beginWalletClosing(self, walletPubKeyHash);\n } else {\n // Otherwise, initialize the moving funds process.\n wallet.state = WalletState.MovingFunds;\n /* solhint-disable-next-line not-rely-on-time */\n wallet.movingFundsRequestedAt = uint32(block.timestamp);\n\n // slither-disable-next-line reentrancy-events\n emit WalletMovingFunds(wallet.ecdsaWalletID, walletPubKeyHash);\n }\n\n if (self.activeWalletPubKeyHash == walletPubKeyHash) {\n // If the move funds request refers to the current active wallet,\n // unset the active wallet and make the wallet creation process\n // possible in order to get a new healthy active wallet.\n delete self.activeWalletPubKeyHash;\n }\n\n self.liveWalletsCount--;\n }\n\n /// @notice Begins the closing period of the given wallet.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The caller must make sure that the wallet is in the\n /// MovingFunds state.\n function beginWalletClosing(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n // Initialize the closing period.\n wallet.state = WalletState.Closing;\n /* solhint-disable-next-line not-rely-on-time */\n wallet.closingStartedAt = uint32(block.timestamp);\n\n // slither-disable-next-line reentrancy-events\n emit WalletClosing(wallet.ecdsaWalletID, walletPubKeyHash);\n }\n\n /// @notice Finalizes the closing period of the given wallet and notifies\n /// the ECDSA registry about this fact.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The caller must make sure that the wallet is in the Closing state.\n function finalizeWalletClosing(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n\n wallet.state = WalletState.Closed;\n\n emit WalletClosed(wallet.ecdsaWalletID, walletPubKeyHash);\n\n self.ecdsaWalletRegistry.closeWallet(wallet.ecdsaWalletID);\n }\n\n /// @notice Terminates the given wallet and notifies the ECDSA registry\n /// about this fact. If the wallet termination refers to the current\n /// active wallet, such a wallet is no longer considered active and\n /// the active wallet slot is unset allowing to trigger a new wallet\n /// creation immediately.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @dev Requirements:\n /// - The caller must make sure that the wallet is in the\n /// Live or MovingFunds or Closing state.\n function terminateWallet(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash\n ) internal {\n Wallet storage wallet = self.registeredWallets[walletPubKeyHash];\n\n if (wallet.state == WalletState.Live) {\n self.liveWalletsCount--;\n }\n\n wallet.state = WalletState.Terminated;\n\n // slither-disable-next-line reentrancy-events\n emit WalletTerminated(wallet.ecdsaWalletID, walletPubKeyHash);\n\n if (self.activeWalletPubKeyHash == walletPubKeyHash) {\n // If termination refers to the current active wallet,\n // unset the active wallet and make the wallet creation process\n // possible in order to get a new healthy active wallet.\n delete self.activeWalletPubKeyHash;\n }\n\n self.ecdsaWalletRegistry.closeWallet(wallet.ecdsaWalletID);\n }\n\n /// @notice Gets BTC balance for given the wallet.\n /// @param walletPubKeyHash 20-byte public key hash of the wallet.\n /// @param walletMainUtxo Data of the wallet's main UTXO, as currently\n /// known on the Ethereum chain.\n /// @return walletBtcBalance Current BTC balance for the given wallet.\n /// @dev Requirements:\n /// - `walletMainUtxo` components must point to the recent main UTXO\n /// of the given wallet, as currently known on the Ethereum chain.\n /// If the wallet has no main UTXO, this parameter can be empty as it\n /// is ignored.\n function getWalletBtcBalance(\n BridgeState.Storage storage self,\n bytes20 walletPubKeyHash,\n BitcoinTx.UTXO calldata walletMainUtxo\n ) internal view returns (uint64 walletBtcBalance) {\n bytes32 walletMainUtxoHash = self\n .registeredWallets[walletPubKeyHash]\n .mainUtxoHash;\n\n // If the wallet has a main UTXO hash set, cross-check it with the\n // provided plain-text parameter and get the transaction output value\n // as BTC balance. Otherwise, the BTC balance is just zero.\n if (walletMainUtxoHash != bytes32(0)) {\n require(\n keccak256(\n abi.encodePacked(\n walletMainUtxo.txHash,\n walletMainUtxo.txOutputIndex,\n walletMainUtxo.txOutputValue\n )\n ) == walletMainUtxoHash,\n \"Invalid wallet main UTXO data\"\n );\n\n walletBtcBalance = walletMainUtxo.txOutputValue;\n }\n\n return walletBtcBalance;\n }\n}\n"
291
297
  },
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "_format": "hh-sol-dbg-1",
3
- "buildInfo": "../../../build-info/250b869971159752d3bb07294d19e052.json"
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
4
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "_format": "hh-sol-dbg-1",
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
+ }
@@ -0,0 +1,245 @@
1
+ {
2
+ "_format": "hh-sol-artifact-1",
3
+ "contractName": "VendingMachineV2",
4
+ "sourceName": "contracts/bridge/VendingMachineV2.sol",
5
+ "abi": [
6
+ {
7
+ "inputs": [
8
+ {
9
+ "internalType": "contract IERC20",
10
+ "name": "_tbtcV1",
11
+ "type": "address"
12
+ },
13
+ {
14
+ "internalType": "contract TBTC",
15
+ "name": "_tbtcV2",
16
+ "type": "address"
17
+ }
18
+ ],
19
+ "stateMutability": "nonpayable",
20
+ "type": "constructor"
21
+ },
22
+ {
23
+ "anonymous": false,
24
+ "inputs": [
25
+ {
26
+ "indexed": false,
27
+ "internalType": "address",
28
+ "name": "from",
29
+ "type": "address"
30
+ },
31
+ {
32
+ "indexed": false,
33
+ "internalType": "uint256",
34
+ "name": "amount",
35
+ "type": "uint256"
36
+ }
37
+ ],
38
+ "name": "Deposited",
39
+ "type": "event"
40
+ },
41
+ {
42
+ "anonymous": false,
43
+ "inputs": [
44
+ {
45
+ "indexed": true,
46
+ "internalType": "address",
47
+ "name": "to",
48
+ "type": "address"
49
+ },
50
+ {
51
+ "indexed": false,
52
+ "internalType": "uint256",
53
+ "name": "amount",
54
+ "type": "uint256"
55
+ }
56
+ ],
57
+ "name": "Exchanged",
58
+ "type": "event"
59
+ },
60
+ {
61
+ "anonymous": false,
62
+ "inputs": [
63
+ {
64
+ "indexed": true,
65
+ "internalType": "address",
66
+ "name": "previousOwner",
67
+ "type": "address"
68
+ },
69
+ {
70
+ "indexed": true,
71
+ "internalType": "address",
72
+ "name": "newOwner",
73
+ "type": "address"
74
+ }
75
+ ],
76
+ "name": "OwnershipTransferred",
77
+ "type": "event"
78
+ },
79
+ {
80
+ "anonymous": false,
81
+ "inputs": [
82
+ {
83
+ "indexed": false,
84
+ "internalType": "address",
85
+ "name": "token",
86
+ "type": "address"
87
+ },
88
+ {
89
+ "indexed": false,
90
+ "internalType": "address",
91
+ "name": "to",
92
+ "type": "address"
93
+ },
94
+ {
95
+ "indexed": false,
96
+ "internalType": "uint256",
97
+ "name": "amount",
98
+ "type": "uint256"
99
+ }
100
+ ],
101
+ "name": "Withdrawn",
102
+ "type": "event"
103
+ },
104
+ {
105
+ "inputs": [
106
+ {
107
+ "internalType": "uint256",
108
+ "name": "amount",
109
+ "type": "uint256"
110
+ }
111
+ ],
112
+ "name": "depositTbtcV2",
113
+ "outputs": [],
114
+ "stateMutability": "nonpayable",
115
+ "type": "function"
116
+ },
117
+ {
118
+ "inputs": [
119
+ {
120
+ "internalType": "uint256",
121
+ "name": "amount",
122
+ "type": "uint256"
123
+ }
124
+ ],
125
+ "name": "exchange",
126
+ "outputs": [],
127
+ "stateMutability": "nonpayable",
128
+ "type": "function"
129
+ },
130
+ {
131
+ "inputs": [],
132
+ "name": "owner",
133
+ "outputs": [
134
+ {
135
+ "internalType": "address",
136
+ "name": "",
137
+ "type": "address"
138
+ }
139
+ ],
140
+ "stateMutability": "view",
141
+ "type": "function"
142
+ },
143
+ {
144
+ "inputs": [
145
+ {
146
+ "internalType": "address",
147
+ "name": "from",
148
+ "type": "address"
149
+ },
150
+ {
151
+ "internalType": "uint256",
152
+ "name": "amount",
153
+ "type": "uint256"
154
+ },
155
+ {
156
+ "internalType": "address",
157
+ "name": "token",
158
+ "type": "address"
159
+ },
160
+ {
161
+ "internalType": "bytes",
162
+ "name": "",
163
+ "type": "bytes"
164
+ }
165
+ ],
166
+ "name": "receiveApproval",
167
+ "outputs": [],
168
+ "stateMutability": "nonpayable",
169
+ "type": "function"
170
+ },
171
+ {
172
+ "inputs": [],
173
+ "name": "renounceOwnership",
174
+ "outputs": [],
175
+ "stateMutability": "nonpayable",
176
+ "type": "function"
177
+ },
178
+ {
179
+ "inputs": [],
180
+ "name": "tbtcV1",
181
+ "outputs": [
182
+ {
183
+ "internalType": "contract IERC20",
184
+ "name": "",
185
+ "type": "address"
186
+ }
187
+ ],
188
+ "stateMutability": "view",
189
+ "type": "function"
190
+ },
191
+ {
192
+ "inputs": [],
193
+ "name": "tbtcV2",
194
+ "outputs": [
195
+ {
196
+ "internalType": "contract TBTC",
197
+ "name": "",
198
+ "type": "address"
199
+ }
200
+ ],
201
+ "stateMutability": "view",
202
+ "type": "function"
203
+ },
204
+ {
205
+ "inputs": [
206
+ {
207
+ "internalType": "address",
208
+ "name": "newOwner",
209
+ "type": "address"
210
+ }
211
+ ],
212
+ "name": "transferOwnership",
213
+ "outputs": [],
214
+ "stateMutability": "nonpayable",
215
+ "type": "function"
216
+ },
217
+ {
218
+ "inputs": [
219
+ {
220
+ "internalType": "contract IERC20",
221
+ "name": "token",
222
+ "type": "address"
223
+ },
224
+ {
225
+ "internalType": "address",
226
+ "name": "recipient",
227
+ "type": "address"
228
+ },
229
+ {
230
+ "internalType": "uint256",
231
+ "name": "amount",
232
+ "type": "uint256"
233
+ }
234
+ ],
235
+ "name": "withdrawFunds",
236
+ "outputs": [],
237
+ "stateMutability": "nonpayable",
238
+ "type": "function"
239
+ }
240
+ ],
241
+ "bytecode": "0x60c060405234801561001057600080fd5b50604051610d8d380380610d8d83398101604081905261002f916100b7565b6100383361004f565b6001600160a01b039182166080521660a0526100f1565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146100b457600080fd5b50565b600080604083850312156100ca57600080fd5b82516100d58161009f565b60208401519092506100e68161009f565b809150509250929050565b60805160a051610c4e61013f600039600081816101440152818161036e0152818161055601526106c201526000818160c20152818161021b015281816102aa015261068d0152610c4e6000f3fe608060405234801561001057600080fd5b50600436106100a35760003560e01c80638da5cb5b1161007657806395de5a411161005b57806395de5a411461013f578063d46219ee14610166578063f2fde38b1461017957600080fd5b80638da5cb5b1461011b5780638f4ffcb11461012c57600080fd5b80631c20fadd146100a85780633678252f146100bd5780635355655914610100578063715018a614610113575b600080fd5b6100bb6100b6366004610a4d565b61018c565b005b6100e47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b6100bb61010e366004610a8e565b6101f8565b6100bb610205565b6000546001600160a01b03166100e4565b6100bb61013a366004610aa7565b610219565b6100e47f000000000000000000000000000000000000000000000000000000000000000081565b6100bb610174366004610a8e565b610328565b6100bb610187366004610b46565b610396565b610194610423565b604080516001600160a01b038086168252841660208201529081018290527fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb9060600160405180910390a16101f36001600160a01b038416838361047d565b505050565b6102023382610526565b50565b61020d610423565b61021760006106ed565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b03161461029f5760405162461bcd60e51b815260206004820152601460248201527f546f6b656e206973206e6f74207442544320763100000000000000000000000060448201526064015b60405180910390fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103175760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920744254432076312063616c6c657220616c6c6f77656400000000006044820152606401610296565b6103218585610526565b5050505050565b60408051338152602081018390527f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4910160405180910390a16102026001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084610755565b61039e610423565b6001600160a01b03811661041a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610296565b610202816106ed565b6000546001600160a01b031633146102175760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610296565b6040516001600160a01b0383166024820152604481018290526101f39084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526107ac565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156105a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c99190610b6a565b101561063d5760405162461bcd60e51b815260206004820152603360248201527f4e6f7420656e6f756768207442544320763220617661696c61626c6520696e2060448201527f7468652056656e64696e67204d616368696e65000000000000000000000000006064820152608401610296565b816001600160a01b03167f6ea412b125ed242bc34c304877264761547306c2429c03d7fd1b477aa61a3f5d8260405161067891815260200190565b60405180910390a26106b56001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016833084610755565b6106e96001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016838361047d565b5050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03808516602483015283166044820152606481018290526107a69085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016104c2565b50505050565b6000610801826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166108919092919063ffffffff16565b8051909150156101f3578080602001905181019061081f9190610b83565b6101f35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610296565b60606108a084846000856108a8565b949350505050565b6060824710156109205760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610296565b600080866001600160a01b0316858760405161093c9190610bc9565b60006040518083038185875af1925050503d8060008114610979576040519150601f19603f3d011682016040523d82523d6000602084013e61097e565b606091505b509150915061098f8783838761099a565b979650505050505050565b60608315610a09578251600003610a02576001600160a01b0385163b610a025760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610296565b50816108a0565b6108a08383815115610a1e5781518083602001fd5b8060405162461bcd60e51b81526004016102969190610be5565b6001600160a01b038116811461020257600080fd5b600080600060608486031215610a6257600080fd5b8335610a6d81610a38565b92506020840135610a7d81610a38565b929592945050506040919091013590565b600060208284031215610aa057600080fd5b5035919050565b600080600080600060808688031215610abf57600080fd5b8535610aca81610a38565b9450602086013593506040860135610ae181610a38565b9250606086013567ffffffffffffffff80821115610afe57600080fd5b818801915088601f830112610b1257600080fd5b813581811115610b2157600080fd5b896020828501011115610b3357600080fd5b9699959850939650602001949392505050565b600060208284031215610b5857600080fd5b8135610b6381610a38565b9392505050565b600060208284031215610b7c57600080fd5b5051919050565b600060208284031215610b9557600080fd5b81518015158114610b6357600080fd5b60005b83811015610bc0578181015183820152602001610ba8565b50506000910152565b60008251610bdb818460208701610ba5565b9190910192915050565b6020815260008251806020840152610c04816040850160208701610ba5565b601f01601f1916919091016040019291505056fea264697066735822122065f1458fb9a48d2637951d85d5856300fd425b137d155180d7e136e86f0dde4164736f6c63430008110033",
242
+ "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100a35760003560e01c80638da5cb5b1161007657806395de5a411161005b57806395de5a411461013f578063d46219ee14610166578063f2fde38b1461017957600080fd5b80638da5cb5b1461011b5780638f4ffcb11461012c57600080fd5b80631c20fadd146100a85780633678252f146100bd5780635355655914610100578063715018a614610113575b600080fd5b6100bb6100b6366004610a4d565b61018c565b005b6100e47f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b03909116815260200160405180910390f35b6100bb61010e366004610a8e565b6101f8565b6100bb610205565b6000546001600160a01b03166100e4565b6100bb61013a366004610aa7565b610219565b6100e47f000000000000000000000000000000000000000000000000000000000000000081565b6100bb610174366004610a8e565b610328565b6100bb610187366004610b46565b610396565b610194610423565b604080516001600160a01b038086168252841660208201529081018290527fd1c19fbcd4551a5edfb66d43d2e337c04837afda3482b42bdf569a8fccdae5fb9060600160405180910390a16101f36001600160a01b038416838361047d565b505050565b6102023382610526565b50565b61020d610423565b61021760006106ed565b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316836001600160a01b03161461029f5760405162461bcd60e51b815260206004820152601460248201527f546f6b656e206973206e6f74207442544320763100000000000000000000000060448201526064015b60405180910390fd5b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146103175760405162461bcd60e51b815260206004820152601b60248201527f4f6e6c7920744254432076312063616c6c657220616c6c6f77656400000000006044820152606401610296565b6103218585610526565b5050505050565b60408051338152602081018390527f2da466a7b24304f47e87fa2e1e5a81b9831ce54fec19055ce277ca2f39ba42c4910160405180910390a16102026001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016333084610755565b61039e610423565b6001600160a01b03811661041a5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610296565b610202816106ed565b6000546001600160a01b031633146102175760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610296565b6040516001600160a01b0383166024820152604481018290526101f39084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526107ac565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa1580156105a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105c99190610b6a565b101561063d5760405162461bcd60e51b815260206004820152603360248201527f4e6f7420656e6f756768207442544320763220617661696c61626c6520696e2060448201527f7468652056656e64696e67204d616368696e65000000000000000000000000006064820152608401610296565b816001600160a01b03167f6ea412b125ed242bc34c304877264761547306c2429c03d7fd1b477aa61a3f5d8260405161067891815260200190565b60405180910390a26106b56001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016833084610755565b6106e96001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016838361047d565b5050565b600080546001600160a01b038381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6040516001600160a01b03808516602483015283166044820152606481018290526107a69085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016104c2565b50505050565b6000610801826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166108919092919063ffffffff16565b8051909150156101f3578080602001905181019061081f9190610b83565b6101f35760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610296565b60606108a084846000856108a8565b949350505050565b6060824710156109205760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610296565b600080866001600160a01b0316858760405161093c9190610bc9565b60006040518083038185875af1925050503d8060008114610979576040519150601f19603f3d011682016040523d82523d6000602084013e61097e565b606091505b509150915061098f8783838761099a565b979650505050505050565b60608315610a09578251600003610a02576001600160a01b0385163b610a025760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610296565b50816108a0565b6108a08383815115610a1e5781518083602001fd5b8060405162461bcd60e51b81526004016102969190610be5565b6001600160a01b038116811461020257600080fd5b600080600060608486031215610a6257600080fd5b8335610a6d81610a38565b92506020840135610a7d81610a38565b929592945050506040919091013590565b600060208284031215610aa057600080fd5b5035919050565b600080600080600060808688031215610abf57600080fd5b8535610aca81610a38565b9450602086013593506040860135610ae181610a38565b9250606086013567ffffffffffffffff80821115610afe57600080fd5b818801915088601f830112610b1257600080fd5b813581811115610b2157600080fd5b896020828501011115610b3357600080fd5b9699959850939650602001949392505050565b600060208284031215610b5857600080fd5b8135610b6381610a38565b9392505050565b600060208284031215610b7c57600080fd5b5051919050565b600060208284031215610b9557600080fd5b81518015158114610b6357600080fd5b60005b83811015610bc0578181015183820152602001610ba8565b50506000910152565b60008251610bdb818460208701610ba5565b9190910192915050565b6020815260008251806020840152610c04816040850160208701610ba5565b601f01601f1916919091016040019291505056fea264697066735822122065f1458fb9a48d2637951d85d5856300fd425b137d155180d7e136e86f0dde4164736f6c63430008110033",
243
+ "linkReferences": {},
244
+ "deployedLinkReferences": {}
245
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "_format": "hh-sol-dbg-1",
3
+ "buildInfo": "../../../build-info/5792f1308683b73d1f1f31b97fe7d59d.json"
4
+ }