@keep-network/tbtc-v2 0.1.1-dev.84 → 0.1.1-dev.87
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/artifacts/Bank.json +3 -3
- package/artifacts/Bridge.json +5 -5
- package/artifacts/Deposit.json +2 -2
- package/artifacts/DepositSweep.json +2 -2
- package/artifacts/EcdsaDkgValidator.json +1 -1
- package/artifacts/EcdsaInactivity.json +1 -1
- package/artifacts/EcdsaSortitionPool.json +2 -2
- package/artifacts/Fraud.json +2 -2
- package/artifacts/KeepRegistry.json +1 -1
- package/artifacts/KeepStake.json +2 -2
- package/artifacts/KeepToken.json +2 -2
- package/artifacts/KeepTokenStaking.json +1 -1
- package/artifacts/MovingFunds.json +2 -2
- package/artifacts/NuCypherStakingEscrow.json +1 -1
- package/artifacts/NuCypherToken.json +2 -2
- package/artifacts/RandomBeaconStub.json +1 -1
- package/artifacts/Redemption.json +2 -2
- package/artifacts/ReimbursementPool.json +2 -2
- package/artifacts/Relay.json +2 -2
- package/artifacts/T.json +2 -2
- package/artifacts/TBTC.json +3 -3
- package/artifacts/TBTCToken.json +3 -3
- package/artifacts/TBTCVault.json +212 -13
- package/artifacts/TokenStaking.json +1 -1
- package/artifacts/TokenholderGovernor.json +9 -9
- package/artifacts/TokenholderTimelock.json +8 -8
- package/artifacts/VendingMachine.json +3 -3
- package/artifacts/VendingMachineKeep.json +1 -1
- package/artifacts/VendingMachineNuCypher.json +1 -1
- package/artifacts/WalletRegistry.json +5 -5
- package/artifacts/WalletRegistryGovernance.json +2 -2
- package/artifacts/Wallets.json +2 -2
- package/artifacts/solcInputs/{0f79d51f4c2e0d6bf7d919933c677b91.json → b91d5d2f63dffec051f14a2922c0abf4.json} +7 -7
- package/build/contracts/GovernanceUtils.sol/GovernanceUtils.dbg.json +1 -1
- package/build/contracts/bank/Bank.sol/Bank.dbg.json +1 -1
- package/build/contracts/bank/IReceiveBalanceApproval.sol/IReceiveBalanceApproval.dbg.json +1 -1
- package/build/contracts/bridge/BitcoinTx.sol/BitcoinTx.dbg.json +1 -1
- package/build/contracts/bridge/Bridge.sol/Bridge.dbg.json +1 -1
- package/build/contracts/bridge/BridgeState.sol/BridgeState.dbg.json +1 -1
- package/build/contracts/bridge/Deposit.sol/Deposit.dbg.json +1 -1
- package/build/contracts/bridge/DepositSweep.sol/DepositSweep.dbg.json +1 -1
- package/build/contracts/bridge/EcdsaLib.sol/EcdsaLib.dbg.json +1 -1
- package/build/contracts/bridge/Fraud.sol/Fraud.dbg.json +1 -1
- package/build/contracts/bridge/Heartbeat.sol/Heartbeat.dbg.json +1 -1
- package/build/contracts/bridge/IRelay.sol/IRelay.dbg.json +1 -1
- package/build/contracts/bridge/MovingFunds.sol/MovingFunds.dbg.json +1 -1
- package/build/contracts/bridge/Redemption.sol/OutboundTx.dbg.json +1 -1
- package/build/contracts/bridge/Redemption.sol/Redemption.dbg.json +1 -1
- package/build/contracts/bridge/VendingMachine.sol/VendingMachine.dbg.json +1 -1
- package/build/contracts/bridge/Wallets.sol/Wallets.dbg.json +1 -1
- package/build/contracts/token/TBTC.sol/TBTC.dbg.json +1 -1
- package/build/contracts/vault/DonationVault.sol/DonationVault.dbg.json +1 -1
- package/build/contracts/vault/IVault.sol/IVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.dbg.json +1 -1
- package/build/contracts/vault/TBTCVault.sol/TBTCVault.json +144 -2
- package/contracts/vault/TBTCVault.sol +118 -29
- package/deploy/11_initialize_wallet_owner.ts +18 -0
- package/export.json +142 -0
- package/package.json +1 -1
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n"
|
|
18
18
|
},
|
|
19
19
|
"contracts/vault/TBTCVault.sol": {
|
|
20
|
-
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\n\nimport \"./IVault.sol\";\nimport \"../bank/Bank.sol\";\nimport \"../token/TBTC.sol\";\n\n/// @title TBTC application vault\n/// @notice TBTC is a fully Bitcoin-backed ERC-20 token pegged to the price of\n/// Bitcoin. It facilitates Bitcoin holders to act on the Ethereum\n/// blockchain and access the decentralized finance (DeFi) ecosystem.\n/// TBTC Vault mints and unmints TBTC based on Bitcoin balances in the\n/// Bank.\n/// @dev TBTC Vault is the owner of TBTC token contract and is the only contract\n/// minting the token.\ncontract TBTCVault is IVault, Governable {\n Bank public bank;\n TBTC public tbtcToken;\n\n event Minted(address indexed to, uint256 amount);\n\n event Unminted(address indexed from, uint256 amount);\n\n modifier onlyBank() {\n require(msg.sender == address(bank), \"Caller is not the Bank\");\n _;\n }\n\n constructor(Bank _bank, TBTC _tbtcToken) {\n require(\n address(_bank) != address(0),\n \"Bank can not be the zero address\"\n );\n\n require(\n address(_tbtcToken) != address(0),\n \"TBTC token can not be the zero address\"\n );\n\n bank = _bank;\n tbtcToken = _tbtcToken;\n\n _transferGovernance(msg.sender);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyGovernance {\n tbtcToken.recoverERC20(token, recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyGovernance {\n tbtcToken.recoverERC721(token, recipient, tokenId, data);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from caller\n /// to TBTC Vault, and mints `amount` of TBTC to the caller.\n /// @dev TBTC Vault must have an allowance for caller's balance in the Bank\n /// for at least `amount`.\n /// @param amount Amount of TBTC to mint.\n function mint(uint256 amount) external {\n address minter = msg.sender;\n require(\n bank.balanceOf(minter) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(minter, amount);\n bank.transferBalanceFrom(minter, address(this), amount);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from the caller\n /// to TBTC Vault and mints `amount` of TBTC to the caller.\n /// @dev Can only be called by the Bank via `approveBalanceAndCall`.\n /// @param owner The owner who approved their Bank balance.\n /// @param amount Amount of TBTC to mint.\n function receiveBalanceApproval(\n address owner,\n uint256 amount,\n bytes calldata\n ) external override onlyBank {\n require(\n bank.balanceOf(owner) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(owner, amount);\n bank.transferBalanceFrom(owner, address(this), amount);\n }\n\n /// @notice Mints the same amount of TBTC as the deposited amount for each\n /// depositor in the array. Can only be called by the Bank after the\n /// Bridge swept deposits and Bank increased balance for the\n /// vault.\n /// @dev Fails if `depositors` array is empty. Expects the length of\n /// `depositors` and `depositedAmounts` is the same.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedAmounts\n ) external override onlyBank {\n require(depositors.length != 0, \"No depositors specified\");\n for (uint256 i = 0; i < depositors.length; i++) {\n _mint(depositors[i], depositedAmounts[i]);\n }\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` back to the caller's balance in the Bank.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint.\n function unmint(uint256 amount) external {\n _unmint(msg.sender, amount);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` of Bank balance to the Bridge requesting redemption\n /// based on the provided `redemptionData`.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint and request to redeem in Bridge.\n /// @param redemptionData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function.\n function unmintAndRedeem(uint256 amount, bytes calldata redemptionData)\n external\n {\n _unmintAndRedeem(msg.sender, amount, redemptionData);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance. If `extraData`\n /// is empty, transfers `amount` back to the caller's balance in the\n /// Bank. If `extraData` is not empty, requests redemption in the\n /// Bridge using the `extraData` as a `redemptionData` parameter to\n /// Bridge's `receiveBalanceApproval` function.\n /// @dev This function is doing the same as `unmint` or `unmintAndRedeem`\n /// (depending on `extraData` parameter) but it allows to execute\n /// unminting without a separate approval transaction. The function can\n /// be called only via `approveAndCall` of TBTC token.\n /// @param from TBTC token holder executing unminting.\n /// @param amount Amount of TBTC to unmint.\n /// @param token TBTC token address.\n /// @param extraData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function. If empty, `receiveApproval` is not requesting a\n /// redemption of Bank balance but is instead performing just TBTC\n /// unminting to a Bank balance.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata extraData\n ) external {\n require(token == address(tbtcToken), \"Token is not TBTC\");\n require(msg.sender == token, \"Only TBTC caller allowed\");\n if (extraData.length == 0) {\n _unmint(from, amount);\n } else {\n _unmintAndRedeem(from, amount, extraData);\n }\n }\n\n // slither-disable-next-line calls-loop\n function _mint(address minter, uint256 amount) internal {\n emit Minted(minter, amount);\n tbtcToken.mint(minter, amount);\n }\n\n function _unmint(address unminter, uint256 amount) internal {\n emit Unminted(unminter, amount);\n tbtcToken.burnFrom(unminter, amount);\n bank.transferBalance(unminter, amount);\n }\n\n function _unmintAndRedeem(\n address redeemer,\n uint256 amount,\n bytes calldata redemptionData\n ) internal {\n emit Unminted(redeemer, amount);\n tbtcToken.burnFrom(redeemer, amount);\n bank.approveBalanceAndCall(bank.bridge(), amount, redemptionData);\n }\n}\n"
|
|
20
|
+
"content": "// SPDX-License-Identifier: MIT\n\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ██████████████ ▐████▌ ██████████████\n// ██████████████ ▐████▌ ██████████████\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n// ▐████▌ ▐████▌\n\npragma solidity ^0.8.9;\n\nimport \"@keep-network/random-beacon/contracts/Governable.sol\";\n\nimport \"./IVault.sol\";\nimport \"../bank/Bank.sol\";\nimport \"../token/TBTC.sol\";\nimport \"../GovernanceUtils.sol\";\n\n/// @title TBTC application vault\n/// @notice TBTC is a fully Bitcoin-backed ERC-20 token pegged to the price of\n/// Bitcoin. It facilitates Bitcoin holders to act on the Ethereum\n/// blockchain and access the decentralized finance (DeFi) ecosystem.\n/// TBTC Vault mints and unmints TBTC based on Bitcoin balances in the\n/// Bank.\n/// @dev TBTC Vault is the owner of TBTC token contract and is the only contract\n/// minting the token.\ncontract TBTCVault is IVault, Governable {\n using SafeERC20 for IERC20;\n\n /// @notice The time delay that needs to pass between initializing and\n /// finalizing upgrade to a new vault. The time delay forces the\n /// upgrading party to reflect on the vault address it is upgrading\n /// to and lets all TBTC holders notice the planned\n /// upgrade.\n uint256 public constant UPGRADE_GOVERNANCE_DELAY = 24 hours;\n\n Bank public bank;\n TBTC public tbtcToken;\n\n /// @notice The address of a new TBTC vault. Set only when the upgrade\n /// process is pending. Once the upgrade gets finalized, the new\n /// TBTC vault will become an owner of TBTC token.\n address public newVault;\n /// @notice The timestamp at which an upgrade to a new TBTC vault was\n /// initiated. Set only when the upgrade process is pending.\n uint256 public upgradeInitiatedTimestamp;\n\n event Minted(address indexed to, uint256 amount);\n event Unminted(address indexed from, uint256 amount);\n\n event UpgradeInitiated(address newVault, uint256 timestamp);\n event UpgradeFinalized(address newVault);\n\n modifier onlyBank() {\n require(msg.sender == address(bank), \"Caller is not the Bank\");\n _;\n }\n\n modifier onlyAfterUpgradeGovernanceDelay() {\n GovernanceUtils.onlyAfterGovernanceDelay(\n upgradeInitiatedTimestamp,\n UPGRADE_GOVERNANCE_DELAY\n );\n _;\n }\n\n constructor(Bank _bank, TBTC _tbtcToken) {\n require(\n address(_bank) != address(0),\n \"Bank can not be the zero address\"\n );\n\n require(\n address(_tbtcToken) != address(0),\n \"TBTC token can not be the zero address\"\n );\n\n bank = _bank;\n tbtcToken = _tbtcToken;\n\n _transferGovernance(msg.sender);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from caller\n /// to TBTC Vault, and mints `amount` of TBTC to the caller.\n /// @dev TBTC Vault must have an allowance for caller's balance in the Bank\n /// for at least `amount`.\n /// @param amount Amount of TBTC to mint.\n function mint(uint256 amount) external {\n address minter = msg.sender;\n require(\n bank.balanceOf(minter) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(minter, amount);\n bank.transferBalanceFrom(minter, address(this), amount);\n }\n\n /// @notice Transfers the given `amount` of the Bank balance from the caller\n /// to TBTC Vault and mints `amount` of TBTC to the caller.\n /// @dev Can only be called by the Bank via `approveBalanceAndCall`.\n /// @param owner The owner who approved their Bank balance.\n /// @param amount Amount of TBTC to mint.\n function receiveBalanceApproval(\n address owner,\n uint256 amount,\n bytes calldata\n ) external override onlyBank {\n require(\n bank.balanceOf(owner) >= amount,\n \"Amount exceeds balance in the bank\"\n );\n _mint(owner, amount);\n bank.transferBalanceFrom(owner, address(this), amount);\n }\n\n /// @notice Mints the same amount of TBTC as the deposited amount for each\n /// depositor in the array. Can only be called by the Bank after the\n /// Bridge swept deposits and Bank increased balance for the\n /// vault.\n /// @dev Fails if `depositors` array is empty. Expects the length of\n /// `depositors` and `depositedAmounts` is the same.\n function receiveBalanceIncrease(\n address[] calldata depositors,\n uint256[] calldata depositedAmounts\n ) external override onlyBank {\n require(depositors.length != 0, \"No depositors specified\");\n for (uint256 i = 0; i < depositors.length; i++) {\n _mint(depositors[i], depositedAmounts[i]);\n }\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` back to the caller's balance in the Bank.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint.\n function unmint(uint256 amount) external {\n _unmint(msg.sender, amount);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance and transfers\n /// `amount` of Bank balance to the Bridge requesting redemption\n /// based on the provided `redemptionData`.\n /// @dev Caller must have at least `amount` of TBTC approved to\n /// TBTC Vault.\n /// @param amount Amount of TBTC to unmint and request to redeem in Bridge.\n /// @param redemptionData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function.\n function unmintAndRedeem(uint256 amount, bytes calldata redemptionData)\n external\n {\n _unmintAndRedeem(msg.sender, amount, redemptionData);\n }\n\n /// @notice Burns `amount` of TBTC from the caller's balance. If `extraData`\n /// is empty, transfers `amount` back to the caller's balance in the\n /// Bank. If `extraData` is not empty, requests redemption in the\n /// Bridge using the `extraData` as a `redemptionData` parameter to\n /// Bridge's `receiveBalanceApproval` function.\n /// @dev This function is doing the same as `unmint` or `unmintAndRedeem`\n /// (depending on `extraData` parameter) but it allows to execute\n /// unminting without a separate approval transaction. The function can\n /// be called only via `approveAndCall` of TBTC token.\n /// @param from TBTC token holder executing unminting.\n /// @param amount Amount of TBTC to unmint.\n /// @param token TBTC token address.\n /// @param extraData Redemption data in a format expected from\n /// `redemptionData` parameter of Bridge's `receiveBalanceApproval`\n /// function. If empty, `receiveApproval` is not requesting a\n /// redemption of Bank balance but is instead performing just TBTC\n /// unminting to a Bank balance.\n function receiveApproval(\n address from,\n uint256 amount,\n address token,\n bytes calldata extraData\n ) external {\n require(token == address(tbtcToken), \"Token is not TBTC\");\n require(msg.sender == token, \"Only TBTC caller allowed\");\n if (extraData.length == 0) {\n _unmint(from, amount);\n } else {\n _unmintAndRedeem(from, amount, extraData);\n }\n }\n\n /// @notice Initiates vault upgrade process. The upgrade process needs to be\n /// finalized with a call to `finalizeUpgrade` function after the\n /// `UPGRADE_GOVERNANCE_DELAY` passes. Only the governance can\n /// initiate the upgrade.\n /// @param _newVault The new vault address.\n function initiateUpgrade(address _newVault) external onlyGovernance {\n require(_newVault != address(0), \"New vault address cannot be zero\");\n /* solhint-disable-next-line not-rely-on-time */\n emit UpgradeInitiated(_newVault, block.timestamp);\n /* solhint-disable-next-line not-rely-on-time */\n upgradeInitiatedTimestamp = block.timestamp;\n newVault = _newVault;\n }\n\n /// @notice Allows the governance to finalize vault upgrade process. The\n /// upgrade process needs to be first initiated with a call to\n /// `initiateUpgrade` and the `UPGRADE_GOVERNANCE_DELAY` needs to\n /// pass. Once the upgrade is finalized, the new vault will become\n /// an owner of TBTC token.\n function finalizeUpgrade()\n external\n onlyGovernance\n onlyAfterUpgradeGovernanceDelay\n {\n emit UpgradeFinalized(newVault);\n // slither-disable-next-line reentrancy-no-eth\n tbtcToken.transferOwnership(newVault);\n newVault = address(0);\n upgradeInitiatedTimestamp = 0;\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20FromToken(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyGovernance {\n tbtcToken.recoverERC20(token, recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the TBTC token contract address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721FromToken(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyGovernance {\n tbtcToken.recoverERC721(token, recipient, tokenId, data);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC20\n /// token sent - mistakenly or not - to the vault address. This\n /// function should be used to withdraw TBTC v1 tokens transferred\n /// to TBTCVault as a result of VendingMachine > TBTCVault upgrade.\n /// @param token Address of the recovered ERC20 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param amount Recovered amount.\n function recoverERC20(\n IERC20 token,\n address recipient,\n uint256 amount\n ) external onlyGovernance {\n token.safeTransfer(recipient, amount);\n }\n\n /// @notice Allows the governance of the TBTCVault to recover any ERC721\n /// token sent mistakenly to the vault address.\n /// @param token Address of the recovered ERC721 token contract.\n /// @param recipient Address the recovered token should be sent to.\n /// @param tokenId Identifier of the recovered token.\n /// @param data Additional data.\n function recoverERC721(\n IERC721 token,\n address recipient,\n uint256 tokenId,\n bytes calldata data\n ) external onlyGovernance {\n token.safeTransferFrom(address(this), recipient, tokenId, data);\n }\n\n // slither-disable-next-line calls-loop\n function _mint(address minter, uint256 amount) internal {\n emit Minted(minter, amount);\n tbtcToken.mint(minter, amount);\n }\n\n function _unmint(address unminter, uint256 amount) internal {\n emit Unminted(unminter, amount);\n tbtcToken.burnFrom(unminter, amount);\n bank.transferBalance(unminter, amount);\n }\n\n function _unmintAndRedeem(\n address redeemer,\n uint256 amount,\n bytes calldata redemptionData\n ) internal {\n emit Unminted(redeemer, amount);\n tbtcToken.burnFrom(redeemer, amount);\n bank.approveBalanceAndCall(bank.bridge(), amount, redemptionData);\n }\n}\n"
|
|
21
21
|
},
|
|
22
22
|
"@keep-network/random-beacon/contracts/Governable.sol": {
|
|
23
23
|
"content": "// SPDX-License-Identifier: MIT\n//\n// ▓▓▌ ▓▓ ▐▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▌▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓ ▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓ ▐▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▄▄▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▄▄▄▄ ▓▓▓▓▓▓▄▄▄▄ ▐▓▓▓▓▓▌ ▐▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓▓▓▓▀ ▐▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▀▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓▀▀▀▀ ▓▓▓▓▓▓▀▀▀▀ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▀\n// ▓▓▓▓▓▓ ▀▓▓▓▓▓▓▄ ▐▓▓▓▓▓▓ ▓▓▓▓▓ ▓▓▓▓▓▓ ▓▓▓▓▓ ▐▓▓▓▓▓▌\n// ▓▓▓▓▓▓▓▓▓▓ █▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n// ▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓\n//\n// Trust math, not hardware.\n\npragma solidity ^0.8.9;\n\n/// @notice Governable contract.\n/// @dev A constructor is not defined, which makes the contract compatible with\n/// upgradable proxies. This requires calling explicitly `_transferGovernance`\n/// function in a child contract.\nabstract contract Governable {\n // Governance of the contract\n // The variable should be initialized by the implementing contract.\n // slither-disable-next-line uninitialized-state\n address public governance;\n\n // Reserved storage space in case we need to add more variables,\n // since there are upgradeable contracts that inherit from this one.\n // See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps\n // slither-disable-next-line unused-state\n uint256[49] private __gap;\n\n event GovernanceTransferred(address oldGovernance, address newGovernance);\n\n modifier onlyGovernance() virtual {\n require(governance == msg.sender, \"Caller is not the governance\");\n _;\n }\n\n /// @notice Transfers governance of the contract to `newGovernance`.\n function transferGovernance(address newGovernance)\n external\n virtual\n onlyGovernance\n {\n require(\n newGovernance != address(0),\n \"New governance is the zero address\"\n );\n _transferGovernance(newGovernance);\n }\n\n function _transferGovernance(address newGovernance) internal virtual {\n address oldGovernance = governance;\n governance = newGovernance;\n emit GovernanceTransferred(oldGovernance, newGovernance);\n }\n}\n"
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"contracts/token/TBTC.sol": {
|
|
26
26
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol\";\nimport \"@thesis/solidity-contracts/contracts/token/MisfundRecovery.sol\";\n\ncontract TBTC is ERC20WithPermit, MisfundRecovery {\n constructor() ERC20WithPermit(\"tBTC v2\", \"tBTC\") {}\n}\n"
|
|
27
27
|
},
|
|
28
|
+
"contracts/GovernanceUtils.sol": {
|
|
29
|
+
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nlibrary GovernanceUtils {\n /// @notice Reverts if the governance delay has not passed since\n /// the change initiated time or if the change has not been\n /// initiated.\n /// @param changeInitiatedTimestamp The timestamp at which the change has\n /// been initiated.\n /// @param delay Governance delay.\n function onlyAfterGovernanceDelay(\n uint256 changeInitiatedTimestamp,\n uint256 delay\n ) internal view {\n require(changeInitiatedTimestamp > 0, \"Change not initiated\");\n require(\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp - changeInitiatedTimestamp >= delay,\n \"Governance delay has not elapsed\"\n );\n }\n\n /// @notice Gets the time remaining until the governable parameter update\n /// can be committed.\n /// @param changeInitiatedTimestamp Timestamp indicating the beginning of\n /// the change.\n /// @param delay Governance delay.\n /// @return Remaining time in seconds.\n function getRemainingGovernanceDelay(\n uint256 changeInitiatedTimestamp,\n uint256 delay\n ) internal view returns (uint256) {\n require(changeInitiatedTimestamp > 0, \"Change not initiated\");\n /* solhint-disable-next-line not-rely-on-time */\n uint256 elapsed = block.timestamp - changeInitiatedTimestamp;\n if (elapsed >= delay) {\n return 0;\n } else {\n return delay - elapsed;\n }\n }\n}\n"
|
|
30
|
+
},
|
|
28
31
|
"@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol": {
|
|
29
32
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.4;\n\nimport \"@openzeppelin/contracts/access/Ownable.sol\";\n\nimport \"./IERC20WithPermit.sol\";\nimport \"./IReceiveApproval.sol\";\n\n/// @title ERC20WithPermit\n/// @notice Burnable ERC20 token with EIP2612 permit functionality. User can\n/// authorize a transfer of their token with a signature conforming\n/// EIP712 standard instead of an on-chain transaction from their\n/// address. Anyone can submit this signature on the user's behalf by\n/// calling the permit function, as specified in EIP2612 standard,\n/// paying gas fees, and possibly performing other actions in the same\n/// transaction.\ncontract ERC20WithPermit is IERC20WithPermit, Ownable {\n /// @notice The amount of tokens owned by the given account.\n mapping(address => uint256) public override balanceOf;\n\n /// @notice The remaining number of tokens that spender will be\n /// allowed to spend on behalf of owner through `transferFrom` and\n /// `burnFrom`. This is zero by default.\n mapping(address => mapping(address => uint256)) public override allowance;\n\n /// @notice Returns the current nonce for EIP2612 permission for the\n /// provided token owner for a replay protection. Used to construct\n /// EIP2612 signature provided to `permit` function.\n mapping(address => uint256) public override nonce;\n\n uint256 public immutable cachedChainId;\n bytes32 public immutable cachedDomainSeparator;\n\n /// @notice Returns EIP2612 Permit message hash. Used to construct EIP2612\n /// signature provided to `permit` function.\n bytes32 public constant override PERMIT_TYPEHASH =\n keccak256(\n \"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)\"\n );\n\n /// @notice The amount of tokens in existence.\n uint256 public override totalSupply;\n\n /// @notice The name of the token.\n string public override name;\n\n /// @notice The symbol of the token.\n string public override symbol;\n\n /// @notice The decimals places of the token.\n uint8 public constant override decimals = 18;\n\n constructor(string memory _name, string memory _symbol) {\n name = _name;\n symbol = _symbol;\n\n cachedChainId = block.chainid;\n cachedDomainSeparator = buildDomainSeparator();\n }\n\n /// @notice Moves `amount` tokens from the caller's account to `recipient`.\n /// @return True if the operation succeeded, reverts otherwise.\n /// @dev Requirements:\n /// - `recipient` cannot be the zero address,\n /// - the caller must have a balance of at least `amount`.\n function transfer(address recipient, uint256 amount)\n external\n override\n returns (bool)\n {\n _transfer(msg.sender, recipient, amount);\n return true;\n }\n\n /// @notice Moves `amount` tokens from `spender` to `recipient` using the\n /// allowance mechanism. `amount` is then deducted from the caller's\n /// allowance unless the allowance was made for `type(uint256).max`.\n /// @return True if the operation succeeded, reverts otherwise.\n /// @dev Requirements:\n /// - `spender` and `recipient` cannot be the zero address,\n /// - `spender` must have a balance of at least `amount`,\n /// - the caller must have allowance for `spender`'s tokens of at least\n /// `amount`.\n function transferFrom(\n address spender,\n address recipient,\n uint256 amount\n ) external override returns (bool) {\n uint256 currentAllowance = allowance[spender][msg.sender];\n if (currentAllowance != type(uint256).max) {\n require(\n currentAllowance >= amount,\n \"Transfer amount exceeds allowance\"\n );\n _approve(spender, msg.sender, currentAllowance - amount);\n }\n _transfer(spender, recipient, amount);\n return true;\n }\n\n /// @notice EIP2612 approval made with secp256k1 signature.\n /// Users can authorize a transfer of their tokens with a signature\n /// conforming EIP712 standard, rather than an on-chain transaction\n /// from their address. Anyone can submit this signature on the\n /// user's behalf by calling the permit function, paying gas fees,\n /// and possibly performing other actions in the same transaction.\n /// @dev The deadline argument can be set to `type(uint256).max to create\n /// permits that effectively never expire. If the `amount` is set\n /// to `type(uint256).max` then `transferFrom` and `burnFrom` will\n /// not reduce an allowance.\n function permit(\n address owner,\n address spender,\n uint256 amount,\n uint256 deadline,\n uint8 v,\n bytes32 r,\n bytes32 s\n ) external override {\n /* solhint-disable-next-line not-rely-on-time */\n require(deadline >= block.timestamp, \"Permission expired\");\n\n // Validate `s` and `v` values for a malleability concern described in EIP2.\n // Only signatures with `s` value in the lower half of the secp256k1\n // curve's order and `v` value of 27 or 28 are considered valid.\n require(\n uint256(s) <=\n 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,\n \"Invalid signature 's' value\"\n );\n require(v == 27 || v == 28, \"Invalid signature 'v' value\");\n\n bytes32 digest = keccak256(\n abi.encodePacked(\n \"\\x19\\x01\",\n DOMAIN_SEPARATOR(),\n keccak256(\n abi.encode(\n PERMIT_TYPEHASH,\n owner,\n spender,\n amount,\n nonce[owner]++,\n deadline\n )\n )\n )\n );\n address recoveredAddress = ecrecover(digest, v, r, s);\n require(\n recoveredAddress != address(0) && recoveredAddress == owner,\n \"Invalid signature\"\n );\n _approve(owner, spender, amount);\n }\n\n /// @notice Creates `amount` tokens and assigns them to `account`,\n /// increasing the total supply.\n /// @dev Requirements:\n /// - `recipient` cannot be the zero address.\n function mint(address recipient, uint256 amount) external onlyOwner {\n require(recipient != address(0), \"Mint to the zero address\");\n\n beforeTokenTransfer(address(0), recipient, amount);\n\n totalSupply += amount;\n balanceOf[recipient] += amount;\n emit Transfer(address(0), recipient, amount);\n }\n\n /// @notice Destroys `amount` tokens from the caller.\n /// @dev Requirements:\n /// - the caller must have a balance of at least `amount`.\n function burn(uint256 amount) external override {\n _burn(msg.sender, amount);\n }\n\n /// @notice Destroys `amount` of tokens from `account` using the allowance\n /// mechanism. `amount` is then deducted from the caller's allowance\n /// unless the allowance was made for `type(uint256).max`.\n /// @dev Requirements:\n /// - `account` must have a balance of at least `amount`,\n /// - the caller must have allowance for `account`'s tokens of at least\n /// `amount`.\n function burnFrom(address account, uint256 amount) external override {\n uint256 currentAllowance = allowance[account][msg.sender];\n if (currentAllowance != type(uint256).max) {\n require(\n currentAllowance >= amount,\n \"Burn amount exceeds allowance\"\n );\n _approve(account, msg.sender, currentAllowance - amount);\n }\n _burn(account, amount);\n }\n\n /// @notice Calls `receiveApproval` function on spender previously approving\n /// the spender to withdraw from the caller multiple times, up to\n /// the `amount` amount. If this function is called again, it\n /// overwrites the current allowance with `amount`. Reverts if the\n /// approval reverted or if `receiveApproval` call on the spender\n /// reverted.\n /// @return True if both approval and `receiveApproval` calls succeeded.\n /// @dev If the `amount` is set to `type(uint256).max` then\n /// `transferFrom` and `burnFrom` will not reduce an allowance.\n function approveAndCall(\n address spender,\n uint256 amount,\n bytes memory extraData\n ) external override returns (bool) {\n if (approve(spender, amount)) {\n IReceiveApproval(spender).receiveApproval(\n msg.sender,\n amount,\n address(this),\n extraData\n );\n return true;\n }\n return false;\n }\n\n /// @notice Sets `amount` as the allowance of `spender` over the caller's\n /// tokens.\n /// @return True if the operation succeeded.\n /// @dev If the `amount` is set to `type(uint256).max` then\n /// `transferFrom` and `burnFrom` will not reduce an allowance.\n /// Beware that changing an allowance with this method brings the risk\n /// that someone may use both the old and the new allowance by\n /// unfortunate transaction ordering. One possible solution to mitigate\n /// this race condition is to first reduce the spender's allowance to 0\n /// and set the desired value afterwards:\n /// https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n function approve(address spender, uint256 amount)\n public\n override\n returns (bool)\n {\n _approve(msg.sender, spender, amount);\n return true;\n }\n\n /// @notice Returns hash of EIP712 Domain struct with the token name as\n /// a signing domain and token contract as a verifying contract.\n /// Used to construct EIP2612 signature provided to `permit`\n /// function.\n /* solhint-disable-next-line func-name-mixedcase */\n function DOMAIN_SEPARATOR() public view override returns (bytes32) {\n // As explained in EIP-2612, if the DOMAIN_SEPARATOR contains the\n // chainId and is defined at contract deployment instead of\n // reconstructed for every signature, there is a risk of possible replay\n // attacks between chains in the event of a future chain split.\n // To address this issue, we check the cached chain ID against the\n // current one and in case they are different, we build domain separator\n // from scratch.\n if (block.chainid == cachedChainId) {\n return cachedDomainSeparator;\n } else {\n return buildDomainSeparator();\n }\n }\n\n /// @dev Hook that is called before any transfer of tokens. This includes\n /// minting and burning.\n ///\n /// Calling conditions:\n /// - when `from` and `to` are both non-zero, `amount` of `from`'s tokens\n /// will be to transferred to `to`.\n /// - when `from` is zero, `amount` tokens will be minted for `to`.\n /// - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n /// - `from` and `to` are never both zero.\n // slither-disable-next-line dead-code\n function beforeTokenTransfer(\n address from,\n address to,\n uint256 amount\n ) internal virtual {}\n\n function _burn(address account, uint256 amount) internal {\n uint256 currentBalance = balanceOf[account];\n require(currentBalance >= amount, \"Burn amount exceeds balance\");\n\n beforeTokenTransfer(account, address(0), amount);\n\n balanceOf[account] = currentBalance - amount;\n totalSupply -= amount;\n emit Transfer(account, address(0), amount);\n }\n\n function _transfer(\n address spender,\n address recipient,\n uint256 amount\n ) private {\n require(spender != address(0), \"Transfer from the zero address\");\n require(recipient != address(0), \"Transfer to the zero address\");\n require(recipient != address(this), \"Transfer to the token address\");\n\n beforeTokenTransfer(spender, recipient, amount);\n\n uint256 spenderBalance = balanceOf[spender];\n require(spenderBalance >= amount, \"Transfer amount exceeds balance\");\n balanceOf[spender] = spenderBalance - amount;\n balanceOf[recipient] += amount;\n emit Transfer(spender, recipient, amount);\n }\n\n function _approve(\n address owner,\n address spender,\n uint256 amount\n ) private {\n require(owner != address(0), \"Approve from the zero address\");\n require(spender != address(0), \"Approve to the zero address\");\n allowance[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n function buildDomainSeparator() private view returns (bytes32) {\n return\n keccak256(\n abi.encode(\n keccak256(\n \"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)\"\n ),\n keccak256(bytes(name)),\n keccak256(bytes(\"1\")),\n block.chainid,\n address(this)\n )\n );\n }\n}\n"
|
|
30
33
|
},
|
|
@@ -58,6 +61,9 @@
|
|
|
58
61
|
"@openzeppelin/contracts/utils/introspection/IERC165.sol": {
|
|
59
62
|
"content": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n"
|
|
60
63
|
},
|
|
64
|
+
"contracts/bridge/VendingMachine.sol": {
|
|
65
|
+
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\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 initator\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"
|
|
66
|
+
},
|
|
61
67
|
"contracts/test/ReceiveApprovalStub.sol": {
|
|
62
68
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"../token/TBTC.sol\";\n\ncontract ReceiveApprovalStub is IReceiveApproval {\n bool public shouldRevert;\n\n event ApprovalReceived(\n address from,\n uint256 value,\n address token,\n bytes extraData\n );\n\n function receiveApproval(\n address from,\n uint256 value,\n address token,\n bytes calldata extraData\n ) external override {\n if (shouldRevert) {\n revert(\"i am your father luke\");\n }\n\n emit ApprovalReceived(from, value, token, extraData);\n }\n\n function setShouldRevert(bool _shouldRevert) external {\n shouldRevert = _shouldRevert;\n }\n}\n"
|
|
63
69
|
},
|
|
@@ -238,12 +244,6 @@
|
|
|
238
244
|
"contracts/hardhat-dependency-compiler/@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol": {
|
|
239
245
|
"content": "// SPDX-License-Identifier: UNLICENSED\npragma solidity >0.0.0;\nimport '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';\n"
|
|
240
246
|
},
|
|
241
|
-
"contracts/bridge/VendingMachine.sol": {
|
|
242
|
-
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\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 initator\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"
|
|
243
|
-
},
|
|
244
|
-
"contracts/GovernanceUtils.sol": {
|
|
245
|
-
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nlibrary GovernanceUtils {\n /// @notice Reverts if the governance delay has not passed since\n /// the change initiated time or if the change has not been\n /// initiated.\n /// @param changeInitiatedTimestamp The timestamp at which the change has\n /// been initiated.\n /// @param delay Governance delay.\n function onlyAfterGovernanceDelay(\n uint256 changeInitiatedTimestamp,\n uint256 delay\n ) internal view {\n require(changeInitiatedTimestamp > 0, \"Change not initiated\");\n require(\n /* solhint-disable-next-line not-rely-on-time */\n block.timestamp - changeInitiatedTimestamp >= delay,\n \"Governance delay has not elapsed\"\n );\n }\n\n /// @notice Gets the time remaining until the governable parameter update\n /// can be committed.\n /// @param changeInitiatedTimestamp Timestamp indicating the beginning of\n /// the change.\n /// @param delay Governance delay.\n /// @return Remaining time in seconds.\n function getRemainingGovernanceDelay(\n uint256 changeInitiatedTimestamp,\n uint256 delay\n ) internal view returns (uint256) {\n require(changeInitiatedTimestamp > 0, \"Change not initiated\");\n /* solhint-disable-next-line not-rely-on-time */\n uint256 elapsed = block.timestamp - changeInitiatedTimestamp;\n if (elapsed >= delay) {\n return 0;\n } else {\n return delay - elapsed;\n }\n }\n}\n"
|
|
246
|
-
},
|
|
247
247
|
"contracts/test/TestERC20.sol": {
|
|
248
248
|
"content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.9;\n\nimport \"@thesis/solidity-contracts/contracts/token/ERC20WithPermit.sol\";\n\ncontract TestERC20 is ERC20WithPermit {\n string public constant NAME = \"Test ERC20 Token\";\n string public constant SYMBOL = \"TT\";\n\n constructor() ERC20WithPermit(NAME, SYMBOL) {}\n}\n"
|
|
249
249
|
},
|