From 57e485bf211dffa2395827a9a8db8783fce48668 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Tue, 21 Dec 2021 11:46:37 +1000 Subject: [PATCH 1/8] separating VaultRegistry, ICollateral from OneBtc to reduce the contract size --- contract/bridge/contracts/Collateral.sol | 10 +- contract/bridge/contracts/IVaultRegistry.sol | 58 +++++++ contract/bridge/contracts/Issue.sol | 33 ++-- contract/bridge/contracts/OneBtc.sol | 27 +-- contract/bridge/contracts/Redeem.sol | 21 +-- contract/bridge/contracts/Replace.sol | 47 +++--- contract/bridge/contracts/VaultRegistry.sol | 154 ++++++++++++++---- .../CollateralTestWrapper.sol | 0 .../ExchangeRateOracleWrapper.sol | 0 .../VaultRegistryTestWrapper.sol | 0 contract/bridge/yarn.lock | 33 ++++ 11 files changed, 283 insertions(+), 100 deletions(-) create mode 100644 contract/bridge/contracts/IVaultRegistry.sol rename contract/bridge/{contracts/test => test_test}/CollateralTestWrapper.sol (100%) rename contract/bridge/{contracts/test => test_test}/ExchangeRateOracleWrapper.sol (100%) rename contract/bridge/{contracts/test => test_test}/VaultRegistryTestWrapper.sol (100%) diff --git a/contract/bridge/contracts/Collateral.sol b/contract/bridge/contracts/Collateral.sol index 8a9afd2..6e762c5 100644 --- a/contract/bridge/contracts/Collateral.sol +++ b/contract/bridge/contracts/Collateral.sol @@ -18,7 +18,7 @@ abstract contract ICollateral { return address(this).balance; } - function lockCollateral(address sender, uint256 amount) internal { + function lockCollateral(address sender, uint256 amount) internal virtual { require(msg.value >= amount, "Invalid collateral"); CollateralBalances[sender] = CollateralBalances[sender].add(amount); emit LockCollateral(sender, amount); @@ -39,7 +39,7 @@ abstract contract ICollateral { require(sent, "Transfer failed."); } - function releaseCollateral(address sender, uint256 amount) internal { + function releaseCollateral(address sender, uint256 amount) internal virtual { release(sender, sender, amount); emit ReleaseCollateral(sender, amount); } @@ -48,7 +48,7 @@ abstract contract ICollateral { address from, address to, uint256 amount - ) internal { + ) internal virtual { release(from, to, amount); emit SlashCollateral(from, to, amount); } @@ -65,7 +65,7 @@ abstract contract ICollateral { return CollateralBalances[vaultId]; } - function useCollateralInc(address vaultId, uint256 amount) internal { + function useCollateralInc(address vaultId, uint256 amount) internal virtual { CollateralUsed[vaultId] = CollateralUsed[vaultId].add(amount); require( CollateralBalances[vaultId] >= CollateralUsed[vaultId], @@ -73,7 +73,7 @@ abstract contract ICollateral { ); } - function useCollateralDec(address vaultId, uint256 amount) internal { + function useCollateralDec(address vaultId, uint256 amount) internal virtual { require(CollateralUsed[vaultId] >= amount, "Insufficient collateral"); CollateralUsed[vaultId] = CollateralUsed[vaultId].sub(amount); } diff --git a/contract/bridge/contracts/IVaultRegistry.sol b/contract/bridge/contracts/IVaultRegistry.sol new file mode 100644 index 0000000..2fdd2fa --- /dev/null +++ b/contract/bridge/contracts/IVaultRegistry.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.12; + +interface IVaultRegistry { + // VaultRegistry + struct Vault { + uint256 btcPublicKeyX; + uint256 btcPublicKeyY; + uint256 collateral; + uint256 issued; + uint256 toBeIssued; + uint256 toBeRedeemed; + uint256 replaceCollateral; + uint256 toBeReplaced; + uint256 liquidatedCollateral; + } + + // // set functions for Vault + // function setBtcPublicKeyX(address, uint256) external; + // function setBtcPublicKeyY(address, uint256) external; + // function setCollateral(address, uint256) external; + // function setIssued(address, uint256) external; + // function setToBeIssued(address, uint256) external; + // function setToBeRedeemed(address, uint256) external; + // function setReplaceCollateral(address, uint256) external; + // function setToBeReplaced(address, uint256) external; + // function setLiquidatedCollateral(address, uint256) external; + + // // set function for vaultDepositAddress + // function setVaultDepositAddress(address, address, bool) external; + + // interfaces for VaultRegistry + function vaults(address) external returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); + function vaultDepositAddress(address, address) external returns(bool); + function registerVault(uint256, uint256) external; + function registerDepositAddress(address, uint256) external returns(address); + function insertVaultDepositAddress(address, uint256, uint256, uint256) external returns(address); + function updatePublicKey(uint256, uint256) external; + function decreaseToBeIssuedTokens(address, uint256) external; + function tryIncreaseToBeIssuedTokens(address, uint256) external returns(bool); + function tryIncreaseToBeRedeemedTokens(address, uint256) external returns(bool); + function redeemTokens(address, uint256) external; + function issuableTokens(address) external view returns(uint256); + function issueTokens(address, uint256) external; + function calculateCollateral(uint256, uint256, uint256) external view returns(uint256); + function requestableToBeReplacedTokens(address) external returns(uint256); + function tryIncreaseToBeReplacedTokens(address, uint256, uint256) external returns(uint256, uint256); + function decreaseToBeReplacedTokens(address, uint256) external returns(uint256, uint256); + function replaceTokens(address, address, uint256, uint256) external; + function tryDepositCollateral(address, uint256) external; + function liquidateVaul(address, address) external; + function lockCollateral(address, uint256) external; + function releaseCollateral(address, uint256) external; + function slashCollateral(address, address, uint256) external; + function useCollateralInc(address, uint256) external; + function useCollateralDec(address, uint256) external; +} diff --git a/contract/bridge/contracts/Issue.sol b/contract/bridge/contracts/Issue.sol index 77de9e7..c76dbb0 100644 --- a/contract/bridge/contracts/Issue.sol +++ b/contract/bridge/contracts/Issue.sol @@ -7,10 +7,9 @@ import "@interlay/bitcoin-spv-sol/contracts/BTCUtils.sol"; import "@interlay/bitcoin-spv-sol/contracts/BytesLib.sol"; import "./Request.sol"; import "./TxValidate.sol"; -import "./Collateral.sol"; -import "./VaultRegistry.sol"; +import "./IVaultRegistry.sol"; -abstract contract Issue is VaultRegistry, Request { +abstract contract Issue is Request { using BTCUtils for bytes; using BytesLib for bytes; using SafeMathUpgradeable for uint256; @@ -93,6 +92,7 @@ abstract contract Issue is VaultRegistry, Request { } function _requestIssue( + IVaultRegistry vaultRegistry, address payable requester, uint256 amountRequested, address vaultId, @@ -103,11 +103,11 @@ abstract contract Issue is VaultRegistry, Request { "Insufficient collateral" ); require( - VaultRegistry.tryIncreaseToBeIssuedTokens(vaultId, amountRequested), + vaultRegistry.tryIncreaseToBeIssuedTokens(vaultId, amountRequested), "Amount requested exceeds vault limit" ); uint256 issueId = getIssueId(requester); - address btcAddress = VaultRegistry.registerDepositAddress( + address btcAddress = vaultRegistry.registerDepositAddress( vaultId, issueId ); @@ -127,7 +127,7 @@ abstract contract Issue is VaultRegistry, Request { request.btcHeight = 0; request.status = RequestStatus.Pending; } - ICollateral.lockCollateral( + vaultRegistry.lockCollateral( request.requester, request.griefingCollateral ); @@ -142,6 +142,7 @@ abstract contract Issue is VaultRegistry, Request { } function _executeIssue( + IVaultRegistry vaultRegistry, address requester, uint256 issueId, bytes memory _vout @@ -162,20 +163,20 @@ abstract contract Issue is VaultRegistry, Request { // only the requester of the issue can execute payments with different amounts require(msg.sender == request.requester, "Invalid executor"); uint256 deficit = expectedTotalAmount - amountTransferred; - VaultRegistry.decreaseToBeIssuedTokens(request.vault, deficit); - uint256 releasedCollateral = VaultRegistry.calculateCollateral( + vaultRegistry.decreaseToBeIssuedTokens(request.vault, deficit); + uint256 releasedCollateral = vaultRegistry.calculateCollateral( request.griefingCollateral, amountTransferred, expectedTotalAmount ); - ICollateral.releaseCollateral( + vaultRegistry.releaseCollateral( request.requester, releasedCollateral ); uint256 slashedCollateral = request.griefingCollateral.sub( releasedCollateral ); - ICollateral.slashCollateral( + vaultRegistry.slashCollateral( request.requester, request.vault, slashedCollateral @@ -187,14 +188,14 @@ abstract contract Issue is VaultRegistry, Request { slashedCollateral ); } else { - ICollateral.releaseCollateral( + vaultRegistry.releaseCollateral( request.requester, request.griefingCollateral ); if (amountTransferred > expectedTotalAmount) { uint256 surplusBtc = amountTransferred.sub(expectedTotalAmount); if ( - VaultRegistry.tryIncreaseToBeIssuedTokens( + vaultRegistry.tryIncreaseToBeIssuedTokens( request.vault, surplusBtc ) @@ -208,7 +209,7 @@ abstract contract Issue is VaultRegistry, Request { } } uint256 total = request.amount.add(request.fee); - VaultRegistry.issueTokens(request.vault, total); + vaultRegistry.issueTokens(request.vault, total); issueOneBTC(request.vault, request.fee); issueOneBTC(request.requester, request.amount); request.status = RequestStatus.Completed; @@ -224,7 +225,7 @@ abstract contract Issue is VaultRegistry, Request { ); } - function _cancelIssue(address requester, uint256 issueId) internal { + function _cancelIssue(IVaultRegistry vaultRegistry, address requester, uint256 issueId) internal { IssueRequest storage request = issueRequests[requester][issueId]; require( request.status == RequestStatus.Pending, @@ -235,12 +236,12 @@ abstract contract Issue is VaultRegistry, Request { "Time not expired" ); request.status = RequestStatus.Cancelled; - ICollateral.slashCollateral( + vaultRegistry.slashCollateral( request.requester, request.vault, request.griefingCollateral ); - VaultRegistry.decreaseToBeIssuedTokens( + vaultRegistry.decreaseToBeIssuedTokens( request.vault, request.amount + request.fee ); diff --git a/contract/bridge/contracts/OneBtc.sol b/contract/bridge/contracts/OneBtc.sol index 222e397..62379dc 100644 --- a/contract/bridge/contracts/OneBtc.sol +++ b/contract/bridge/contracts/OneBtc.sol @@ -10,15 +10,19 @@ import {Redeem} from "./Redeem.sol"; import {Replace} from "./Replace.sol"; import {IRelay} from "./IRelay.sol"; import "./IExchangeRateOracle.sol"; +import "./IVaultRegistry.sol"; contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { IRelay public relay; + IVaultRegistry public vaultRegistry; + IExchangeRateOracle oracle; - function initialize(IRelay _relay, IExchangeRateOracle _oracle) external initializer { + function initialize(IRelay _relay, IExchangeRateOracle _oracle, IVaultRegistry _vaultRegistry) external initializer { __ERC20_init("Harmony Bitcoin", "1BTC"); _setupDecimals(8); relay = _relay; oracle = _oracle; + vaultRegistry = _vaultRegistry; } function verifyTx( @@ -38,8 +42,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { 1, true ); - TransactionUtils.Transaction memory btcTx = - TransactionUtils.extractTx(rawTx); + TransactionUtils.Transaction memory btcTx = TransactionUtils.extractTx(rawTx); require(btcTx.locktime == 0, "Locktime must be zero"); // check version? // btcTx.version @@ -50,7 +53,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { external payable { - Issue._requestIssue(msg.sender, amountRequested, vaultId, msg.value); + Issue._requestIssue(vaultRegistry, msg.sender, amountRequested, vaultId, msg.value); } function executeIssue( @@ -66,11 +69,11 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { bytes memory _vout = verifyTx(height, index, rawTx, header, merkleProof); - Issue._executeIssue(requester, issueId, _vout); + Issue._executeIssue(vaultRegistry, requester, issueId, _vout); } function cancelIssue(address requester, uint256 issueId) external { - Issue._cancelIssue(requester, issueId); + Issue._cancelIssue(vaultRegistry, requester, issueId); } function requestRedeem( @@ -78,7 +81,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { address btcAddress, address vaultId ) external { - Redeem._requestRedeem(msg.sender, amountOneBtc, btcAddress, vaultId); + Redeem._requestRedeem(vaultRegistry, msg.sender, amountOneBtc, btcAddress, vaultId); } function executeRedeem( @@ -93,11 +96,11 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { bytes memory _vout = verifyTx(height, index, rawTx, header, merkleProof); - Redeem._executeRedeem(requester, redeemId, _vout); + Redeem._executeRedeem(vaultRegistry, requester, redeemId, _vout); } function cancelRedeem(address requester, uint256 redeemId) external { - Redeem._cancelRedeem(requester, redeemId); + Redeem._cancelRedeem(vaultRegistry, requester, redeemId); } function lockOneBTC(address from, uint256 amount) @@ -130,7 +133,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { uint256 btcAmount, uint256 griefingCollateral ) external payable { - Replace._requestReplace(oldVaultId, btcAmount, griefingCollateral); + Replace._requestReplace(vaultRegistry, oldVaultId, btcAmount, griefingCollateral); } function acceptReplace( @@ -142,6 +145,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { uint256 btcPublicKeyY ) external payable { Replace._acceptReplace( + vaultRegistry, oldVaultId, newVaultId, btcAmount, @@ -152,6 +156,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { } function executeReplace( + IVaultRegistry vaultRegistry, uint256 replaceId, bytes calldata merkleProof, bytes calldata rawTx, // avoid compiler error: stack too deep @@ -161,6 +166,6 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { bytes calldata header ) external { bytes memory _vout = verifyTx(height, index, rawTx, header, merkleProof); - Replace._executeReplace(replaceId, _vout); + Replace._executeReplace(vaultRegistry, replaceId, _vout); } } diff --git a/contract/bridge/contracts/Redeem.sol b/contract/bridge/contracts/Redeem.sol index d071d7e..4608fee 100644 --- a/contract/bridge/contracts/Redeem.sol +++ b/contract/bridge/contracts/Redeem.sol @@ -6,10 +6,9 @@ import {BTCUtils} from "@interlay/bitcoin-spv-sol/contracts/BTCUtils.sol"; import {BytesLib} from "@interlay/bitcoin-spv-sol/contracts/BytesLib.sol"; import {Request} from "./Request.sol"; import {TxValidate} from "./TxValidate.sol"; -import {ICollateral} from "./Collateral.sol"; -import {VaultRegistry} from "./VaultRegistry.sol"; +import "./IVaultRegistry.sol"; -abstract contract Redeem is VaultRegistry, Request { +abstract contract Redeem is Request { using BTCUtils for bytes; using BytesLib for bytes; @@ -73,6 +72,7 @@ abstract contract Redeem is VaultRegistry, Request { } function _requestRedeem( + IVaultRegistry vaultRegistry, address requester, uint256 amountOneBtc, address btcAddress, @@ -86,7 +86,7 @@ abstract contract Redeem is VaultRegistry, Request { uint256 redeemId = getRedeemId(requester); require( - VaultRegistry.tryIncreaseToBeRedeemedTokens(vaultId, toBeBurnedBtc), + vaultRegistry.tryIncreaseToBeRedeemedTokens(vaultId, toBeBurnedBtc), "Insufficient tokens committed" ); // TODO: decrease collateral @@ -106,7 +106,7 @@ abstract contract Redeem is VaultRegistry, Request { //request.btcHeight request.status = RequestStatus.Pending; } - ICollateral.useCollateralInc(vaultId, request.amountOne); + vaultRegistry.useCollateralInc(vaultId, request.amountOne); emit RedeemRequested( redeemId, requester, @@ -118,6 +118,7 @@ abstract contract Redeem is VaultRegistry, Request { } function _executeRedeem( + IVaultRegistry vaultRegistry, address requester, uint256 redeemId, bytes memory _vout @@ -136,8 +137,8 @@ abstract contract Redeem is VaultRegistry, Request { burnLockedOneBTC(request.amountBtc); releaseLockedOneBTC(request.vault, request.fee); request.status = RequestStatus.Completed; - ICollateral.useCollateralDec(request.vault, request.amountOne); - VaultRegistry.redeemTokens( + vaultRegistry.useCollateralDec(request.vault, request.amountOne); + vaultRegistry.redeemTokens( request.vault, request.amountBtc + request.transferFeeBtc ); @@ -151,7 +152,7 @@ abstract contract Redeem is VaultRegistry, Request { ); } - function _cancelRedeem(address requester, uint256 redeemId) internal { + function _cancelRedeem(IVaultRegistry vaultRegistry, address requester, uint256 redeemId) internal { RedeemRequest storage request = redeemRequests[requester][redeemId]; require( request.status == RequestStatus.Pending, @@ -164,8 +165,8 @@ abstract contract Redeem is VaultRegistry, Request { request.status = RequestStatus.Cancelled; releaseLockedOneBTC(request.requester, request.amountBtc + request.fee); - ICollateral.useCollateralDec(request.vault, request.amountOne); - ICollateral.slashCollateral( + vaultRegistry.useCollateralDec(request.vault, request.amountOne); + vaultRegistry.slashCollateral( request.vault, request.requester, request.amountOne diff --git a/contract/bridge/contracts/Replace.sol b/contract/bridge/contracts/Replace.sol index 9795400..7fc0dcf 100644 --- a/contract/bridge/contracts/Replace.sol +++ b/contract/bridge/contracts/Replace.sol @@ -7,10 +7,9 @@ import {BTCUtils} from "@interlay/bitcoin-spv-sol/contracts/BTCUtils.sol"; import {BytesLib} from "@interlay/bitcoin-spv-sol/contracts/BytesLib.sol"; import {Request} from "./Request.sol"; import {TxValidate} from "./TxValidate.sol"; -import {ICollateral} from "./Collateral.sol"; -import {VaultRegistry} from "./VaultRegistry.sol"; +import "./IVaultRegistry.sol"; -abstract contract Replace is VaultRegistry, Request { +abstract contract Replace is Request { // using BTCUtils for bytes; // using BytesLib for bytes; @@ -72,6 +71,7 @@ abstract contract Replace is VaultRegistry, Request { } function _requestReplace( + IVaultRegistry vaultRegistry, address payable oldVaultId, uint256 btcAmount, uint256 griefingCollateral @@ -83,7 +83,7 @@ abstract contract Replace is VaultRegistry, Request { // TODO: SECURITY CHECK (The oldVault MUST NOT be banned) - uint256 requestableTokens = VaultRegistry.requestableToBeReplacedTokens( + uint256 requestableTokens = vaultRegistry.requestableToBeReplacedTokens( oldVaultId ); uint256 toBeReplacedIncrease = MathUpgradeable.min(requestableTokens, btcAmount); @@ -91,7 +91,7 @@ abstract contract Replace is VaultRegistry, Request { uint256 replaceCollateralIncrease = griefingCollateral; if (btcAmount > 0) { - replaceCollateralIncrease = VaultRegistry.calculateCollateral( + replaceCollateralIncrease = vaultRegistry.calculateCollateral( griefingCollateral, toBeReplacedIncrease, btcAmount @@ -101,7 +101,7 @@ abstract contract Replace is VaultRegistry, Request { ( uint256 totalToBeReplaced, uint256 totalGriefingCollateral - ) = VaultRegistry.tryIncreaseToBeReplacedTokens( + ) = vaultRegistry.tryIncreaseToBeReplacedTokens( oldVaultId, toBeReplacedIncrease, replaceCollateralIncrease @@ -121,7 +121,7 @@ abstract contract Replace is VaultRegistry, Request { ); // Lock the oldVault’s griefing collateral. Note that this directly locks the amount - ICollateral.lockCollateral(oldVaultId, replaceCollateralIncrease); + vaultRegistry.lockCollateral(oldVaultId, replaceCollateralIncrease); emit RequestReplace( oldVaultId, @@ -130,14 +130,14 @@ abstract contract Replace is VaultRegistry, Request { ); } - function _withdrawReplace(address oldVaultId, uint256 btcAmount) internal { + function _withdrawReplace(IVaultRegistry vaultRegistry, address oldVaultId, uint256 btcAmount) internal { require(msg.sender == oldVaultId, "Sender should be old vault owner"); // TODO: SECURITY CHECK (The oldVault MUST NOT be banned) - (uint256 withdrawnTokens, uint256 toWithdrawCollateral) = VaultRegistry + (uint256 withdrawnTokens, uint256 toWithdrawCollateral) = vaultRegistry .decreaseToBeReplacedTokens(oldVaultId, btcAmount); - ICollateral.releaseCollateral(oldVaultId, toWithdrawCollateral); + vaultRegistry.releaseCollateral(oldVaultId, toWithdrawCollateral); require(withdrawnTokens == 0, "Withdraw tokens is zero"); @@ -145,6 +145,7 @@ abstract contract Replace is VaultRegistry, Request { } function _acceptReplace( + IVaultRegistry vaultRegistry, address oldVaultId, address newVaultId, uint256 btcAmount, @@ -161,14 +162,14 @@ abstract contract Replace is VaultRegistry, Request { // TODO: The newVault’s free balance MUST be enough to lock collateral. // TODO: SECURITY CHECK (The oldVault, newVault MUST NOT be banned) - Vault storage oldVault = VaultRegistry.vaults[oldVaultId]; - Vault storage newVault = VaultRegistry.vaults[newVaultId]; + (uint256 oldVaultBtcPublicKeyX,,,,,,,,) = vaultRegistry.vaults(oldVaultId); + (uint256 newVaultBtcPublicKeyX,,,,,,,,) = vaultRegistry.vaults(newVaultId); - require(oldVault.btcPublicKeyX != 0, "Vault does not exist"); - require(newVault.btcPublicKeyX != 0, "Vault does not exist"); + require(oldVaultBtcPublicKeyX != 0, "Vault does not exist"); + require(newVaultBtcPublicKeyX != 0, "Vault does not exist"); // decrease old-vault's to-be-replaced tokens - (uint256 redeemableTokens, uint256 griefingCollateral) = VaultRegistry + (uint256 redeemableTokens, uint256 griefingCollateral) = vaultRegistry .decreaseToBeReplacedTokens(oldVaultId, btcAmount); // TODO: check amount_btc is above the minimum @@ -176,29 +177,29 @@ abstract contract Replace is VaultRegistry, Request { require(redeemableTokens >= dustValue, "Amount below dust amount"); // Calculate and lock the new-vault's additional collateral - uint256 actualNewVaultCollateral = VaultRegistry.calculateCollateral( + uint256 actualNewVaultCollateral = vaultRegistry.calculateCollateral( collateral, redeemableTokens, btcAmount ); - VaultRegistry.tryDepositCollateral( + vaultRegistry.tryDepositCollateral( newVaultId, actualNewVaultCollateral ); // increase old-vault's to-be-redeemed tokens - this should never fail - VaultRegistry.tryIncreaseToBeRedeemedTokens( + vaultRegistry.tryIncreaseToBeRedeemedTokens( oldVaultId, redeemableTokens ); // increase new-vault's to-be-issued tokens - this will fail if there is insufficient collateral - VaultRegistry.tryIncreaseToBeIssuedTokens(newVaultId, redeemableTokens); + vaultRegistry.tryIncreaseToBeIssuedTokens(newVaultId, redeemableTokens); uint256 replaceId = getReplaceId(oldVaultId); - address btcAddress = VaultRegistry.insertVaultDepositAddress( + address btcAddress = vaultRegistry.insertVaultDepositAddress( newVaultId, btcPublicKeyX, btcPublicKeyY, @@ -234,7 +235,7 @@ abstract contract Replace is VaultRegistry, Request { ); } - function _executeReplace(uint256 replaceId, bytes memory _vout) internal { + function _executeReplace(IVaultRegistry vaultRegistry, uint256 replaceId, bytes memory _vout) internal { // Retrieve the ReplaceRequest as per the replaceId parameter from Vaults in the VaultRegistry ReplaceRequest storage replace = replaceRequests[replaceId]; require( @@ -260,7 +261,7 @@ abstract contract Replace is VaultRegistry, Request { // decrease old-vault's issued & to-be-redeemed tokens, and // change new-vault's to-be-issued tokens to issued tokens - VaultRegistry.replaceTokens( + vaultRegistry.replaceTokens( oldVaultId, newVaultId, replace.amount, @@ -268,7 +269,7 @@ abstract contract Replace is VaultRegistry, Request { ); // if the old vault has not been liquidated, give it back its griefing collateral - ICollateral.releaseCollateral(oldVaultId, replace.griefingCollateral); + vaultRegistry.releaseCollateral(oldVaultId, replace.griefingCollateral); // Emit ExecuteReplace event. emit ExecuteReplace(replaceId, oldVaultId, newVaultId); diff --git a/contract/bridge/contracts/VaultRegistry.sol b/contract/bridge/contracts/VaultRegistry.sol index b849324..483eac6 100644 --- a/contract/bridge/contracts/VaultRegistry.sol +++ b/contract/bridge/contracts/VaultRegistry.sol @@ -2,31 +2,22 @@ pragma solidity 0.6.12; -import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/math/MathUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import {ICollateral} from "./Collateral.sol"; import {BitcoinKeyDerivation} from "./crypto/BitcoinKeyDerivation.sol"; import "./IExchangeRateOracle.sol"; +import "./IVaultRegistry.sol"; -abstract contract VaultRegistry is Initializable, ICollateral { +abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, ICollateral, IVaultRegistry { using SafeMathUpgradeable for uint256; - struct Vault { - uint256 btcPublicKeyX; - uint256 btcPublicKeyY; - uint256 collateral; - uint256 issued; - uint256 toBeIssued; - uint256 toBeRedeemed; - uint256 replaceCollateral; - uint256 toBeReplaced; - uint256 liquidatedCollateral; - mapping(address => bool) depositAddresses; - } - - mapping(address => Vault) public vaults; + mapping(address => Vault) public override vaults; + mapping(address => mapping(address => bool)) vaultDepositAddress; IExchangeRateOracle oracle; + address public oneBtcAddress; event RegisterVault( address indexed vaultId, @@ -51,6 +42,27 @@ abstract contract VaultRegistry is Initializable, ICollateral { ); event LiquidateVault(); + modifier onlyOneBtc() { + require(msg.sender == oneBtcAddress, "OnlyOneBtc"); + _; + } + + modifier onlyOneBtcOrSelf() { + require(msg.sender == oneBtcAddress || msg.sender == address(this), "OnlyOneBtc"); + _; + } + + function initialize(IExchangeRateOracle _oracle) external initializer { + __ReentrancyGuard_init(); + __Ownable_init(); + + oracle = _oracle; + } + + function setOneBtcAddress(address _oneBtcAddress) external onlyOwner { + oneBtcAddress = _oneBtcAddress; + } + function registerVault(uint256 btcPublicKeyX, uint256 btcPublicKeyY) external payable @@ -70,7 +82,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function registerDepositAddress(address vaultId, uint256 issueId) - internal + external + onlyOneBtc returns (address) { Vault storage vault = vaults[vaultId]; @@ -82,10 +95,10 @@ abstract contract VaultRegistry is Initializable, ICollateral { ); require( - !vault.depositAddresses[derivedKey], + !vaultDepositAddress[vaultId][derivedKey], "The btc address is already used" ); - vault.depositAddresses[derivedKey] = true; + vaultDepositAddress[vaultId][derivedKey] = true; return derivedKey; } @@ -95,7 +108,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { uint256 btcPublicKeyX, uint256 btcPublicKeyY, uint256 replaceId - ) internal returns (address) { + ) external onlyOneBtc returns (address) { Vault storage vault = vaults[vaultId]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); @@ -106,10 +119,10 @@ abstract contract VaultRegistry is Initializable, ICollateral { ); require( - !vault.depositAddresses[btcAddress], + !vaultDepositAddress[vaultId][btcAddress], "The btc address is already used" ); - vault.depositAddresses[btcAddress] = true; + vaultDepositAddress[vaultId][btcAddress] = true; return btcAddress; } @@ -133,7 +146,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { ICollateral.lockCollateral(vaultId, msg.value); } - function withdrawCollateral(uint256 amount) external { + function withdrawCollateral(uint256 amount) external nonReentrant { Vault storage vault = vaults[msg.sender]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); vault.collateral = vault.collateral.sub(amount); @@ -141,7 +154,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function decreaseToBeIssuedTokens(address vaultId, uint256 amount) - internal + external + onlyOneBtc { Vault storage vault = vaults[vaultId]; vault.toBeIssued = vault.toBeIssued.sub(amount); @@ -149,7 +163,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function tryIncreaseToBeIssuedTokens(address vaultId, uint256 amount) - internal + external + onlyOneBtc returns (bool) { uint256 issuableTokens = issuableTokens(vaultId); @@ -161,7 +176,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function tryIncreaseToBeRedeemedTokens(address vaultId, uint256 amount) - internal + external + onlyOneBtc returns (bool) { uint256 redeemable = redeemableTokens(vaultId); @@ -177,7 +193,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { return vault.issued.sub(vault.toBeRedeemed); } - function redeemTokens(address vaultId, uint256 amount) internal { + function redeemTokens(address vaultId, uint256 amount) external onlyOneBtc { Vault storage vault = vaults[vaultId]; vault.toBeRedeemed = vault.toBeRedeemed.sub(amount); vault.issued = vault.issued.sub(amount); @@ -192,7 +208,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { ); } - function issueTokens(address vaultId, uint256 amount) internal { + function issueTokens(address vaultId, uint256 amount) external onlyOneBtc { Vault storage vault = vaults[vaultId]; vault.issued = vault.issued.add(amount); vault.toBeIssued = vault.toBeIssued.sub(amount); @@ -203,7 +219,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { uint256 collateral, uint256 numerator, uint256 denominator - ) internal view returns (uint256 amount) { + ) public view returns (uint256 amount) { if (numerator == 0 && denominator == 0) { return collateral; } @@ -212,7 +228,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function requestableToBeReplacedTokens(address vaultId) - internal + public + onlyOneBtcOrSelf returns (uint256 amount) { Vault memory vault = vaults[vaultId]; @@ -229,7 +246,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { address vaultId, uint256 tokens, uint256 collateral - ) internal returns (uint256, uint256) { + ) external onlyOneBtc returns (uint256, uint256) { Vault storage vault = vaults[vaultId]; uint256 requestableIncrease = requestableToBeReplacedTokens(vaultId); @@ -248,7 +265,8 @@ abstract contract VaultRegistry is Initializable, ICollateral { } function decreaseToBeReplacedTokens(address vaultId, uint256 tokens) - internal + external + onlyOneBtc returns (uint256, uint256) { Vault storage vault = vaults[vaultId]; @@ -279,7 +297,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { address newVaultId, uint256 tokens, uint256 collateral - ) internal { + ) external onlyOneBtc { Vault storage oldVault = vaults[oldVaultId]; Vault storage newVault = vaults[newVaultId]; @@ -295,7 +313,7 @@ abstract contract VaultRegistry is Initializable, ICollateral { emit ReplaceTokens(oldVaultId, newVaultId, tokens, collateral); } - function tryDepositCollateral(address vaultId, uint256 amount) internal { + function tryDepositCollateral(address vaultId, uint256 amount) external onlyOneBtc { Vault storage vault = vaults[vaultId]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); @@ -398,9 +416,75 @@ abstract contract VaultRegistry is Initializable, ICollateral { /** * @dev Liquidate a vault by transferring all of its token balances to the liquidation vault. */ - function liquidateVault(address vaultId, address reporterId) internal { + function liquidateVault(address vaultId, address reporterId) external { liquidate(vaultId, reporterId); } + /// override functions for ICollateral + function lockCollateral(address sender, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { + ICollateral.lockCollateral(sender, amount); + } + + function releaseCollateral(address sender, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc nonReentrant { + ICollateral.releaseCollateral(sender, amount); + } + + function slashCollateral( + address from, + address to, + uint256 amount + ) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc nonReentrant { + ICollateral.slashCollateral(from, to, amount); + } + + function useCollateralInc(address vaultId, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { + ICollateral.useCollateralInc(vaultId, amount); + } + + function useCollateralDec(address vaultId, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { + ICollateral.useCollateralDec(vaultId, amount); + } + + // set functions for Vault + // function setBtcPublicKeyX(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].btcPublicKeyX = value; + // } + + // function setBtcPublicKeyY(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].btcPublicKeyY = value; + // } + + // function setCollateral(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].collateral = value; + // } + + // function setIssued(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].issued = value; + // } + + // function setToBeIssued(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].toBeIssued = value; + // } + + // function setToBeRedeemed(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].toBeRedeemed = value; + // } + + // function setReplaceCollateral(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].replaceCollateral = value; + // } + + // function setToBeReplaced(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].toBeReplaced = value; + // } + + // function setLiquidatedCollateral(address vaultAddress, uint256 value) external onlyOneBtc { + // vaults[vaultAddress].liquidatedCollateral = value; + // } + + // function setVaultDepositAddress(address vaultAddress, address derivedKey, bool value) external onlyOneBtc { + // vaults[vaultAddress][derivedKey] = value; + // } + uint256[45] private __gap; } diff --git a/contract/bridge/contracts/test/CollateralTestWrapper.sol b/contract/bridge/test_test/CollateralTestWrapper.sol similarity index 100% rename from contract/bridge/contracts/test/CollateralTestWrapper.sol rename to contract/bridge/test_test/CollateralTestWrapper.sol diff --git a/contract/bridge/contracts/test/ExchangeRateOracleWrapper.sol b/contract/bridge/test_test/ExchangeRateOracleWrapper.sol similarity index 100% rename from contract/bridge/contracts/test/ExchangeRateOracleWrapper.sol rename to contract/bridge/test_test/ExchangeRateOracleWrapper.sol diff --git a/contract/bridge/contracts/test/VaultRegistryTestWrapper.sol b/contract/bridge/test_test/VaultRegistryTestWrapper.sol similarity index 100% rename from contract/bridge/contracts/test/VaultRegistryTestWrapper.sol rename to contract/bridge/test_test/VaultRegistryTestWrapper.sol diff --git a/contract/bridge/yarn.lock b/contract/bridge/yarn.lock index a4cbbdf..ab3c926 100644 --- a/contract/bridge/yarn.lock +++ b/contract/bridge/yarn.lock @@ -3741,6 +3741,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -5835,6 +5840,14 @@ cli-spinners@^2.0.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.0.tgz#36c7dc98fb6a9a76bd6238ec3f77e2425627e939" integrity sha512-t+4/y50K/+4xcCRosKkA7W4gTr1MySvLV0q+PxmG7FJ5g+66ChKurYjxBCjHggHH3HA5Hh9cy+lcUGWDqVH+4Q== +cli-table@^0.3.1: + version "0.3.9" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.9.tgz#c0570b079296608a31761a67d9af9841dbee8300" + integrity sha512-7eA6hFtAZwVx3dWAGoaBqTrzWko5jRUFKpHT64ZHkJpaA3y5wf5NlLjguqTRmqycatJZiwftODYYyGNLbQ7MuA== + dependencies: + colors "1.0.3" + strip-ansi "^6.0.1" + cliui@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" @@ -5974,6 +5987,11 @@ colorette@^1.3.0: resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + colors@^1.1.2, colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -15424,6 +15442,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-1.0.0.tgz#e7144398577d51a6bed0fa1994fa05f43fd988ee" @@ -15911,6 +15936,14 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== +truffle-contract-size@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/truffle-contract-size/-/truffle-contract-size-2.0.1.tgz#9ec1b078b0b3221cc1612400bd6f5df83c7a3a09" + integrity sha512-AIKPwHPC/1pZwtVjgUcgcK23k6gWxKhn4ZnKLr339uieb94UgAUeIwGUkfc87T+0lqgC6ePY7YhsFeoZK2YEsA== + dependencies: + cli-table "^0.3.1" + yargs "^15.3.1" + truffle@^5.1.66: version "5.4.9" resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.4.9.tgz#67f3c82150d1a96905e03f2a91e369426acfb7ef" From 5bf5ecf02366fa6f3e76c2db86fc9fc5cc9d441f Mon Sep 17 00:00:00 2001 From: apptreeso Date: Mon, 24 Jan 2022 13:23:32 +1000 Subject: [PATCH 2/8] contract refactoring --- contract/bridge/contracts/Collateral.sol | 16 +-- .../bridge/contracts/ExchangeRateOracleV2.sol | 6 +- contract/bridge/contracts/IVaultRegistry.sol | 18 +--- contract/bridge/contracts/Issue.sol | 1 + contract/bridge/contracts/OneBtc.sol | 5 +- contract/bridge/contracts/Redeem.sol | 4 +- contract/bridge/contracts/Replace.sol | 3 +- contract/bridge/contracts/Request.sol | 2 +- contract/bridge/contracts/StakedRelayer.sol | 5 +- contract/bridge/contracts/VaultRegistry.sol | 99 +++++++++++-------- .../test}/CollateralTestWrapper.sol | 12 +-- .../test}/ExchangeRateOracleWrapper.sol | 12 +-- .../test/VaultRegistryWrapper.sol} | 6 +- 13 files changed, 97 insertions(+), 92 deletions(-) rename contract/bridge/{test_test => contracts/test}/CollateralTestWrapper.sol (71%) rename contract/bridge/{test_test => contracts/test}/ExchangeRateOracleWrapper.sol (82%) rename contract/bridge/{test_test/VaultRegistryTestWrapper.sol => contracts/test/VaultRegistryWrapper.sol} (91%) diff --git a/contract/bridge/contracts/Collateral.sol b/contract/bridge/contracts/Collateral.sol index 6e762c5..57ba7f2 100644 --- a/contract/bridge/contracts/Collateral.sol +++ b/contract/bridge/contracts/Collateral.sol @@ -4,7 +4,7 @@ pragma solidity 0.6.12; import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; -abstract contract ICollateral { +contract ICollateral { using SafeMathUpgradeable for uint256; event LockCollateral(address sender, uint256 amount); @@ -18,8 +18,8 @@ abstract contract ICollateral { return address(this).balance; } - function lockCollateral(address sender, uint256 amount) internal virtual { - require(msg.value >= amount, "Invalid collateral"); + function _lockCollateral(address sender, uint256 amount) internal virtual { + require(amount > 0, "Invalid collateral"); CollateralBalances[sender] = CollateralBalances[sender].add(amount); emit LockCollateral(sender, amount); } @@ -39,12 +39,12 @@ abstract contract ICollateral { require(sent, "Transfer failed."); } - function releaseCollateral(address sender, uint256 amount) internal virtual { + function _releaseCollateral(address sender, uint256 amount) internal virtual { release(sender, sender, amount); emit ReleaseCollateral(sender, amount); } - function slashCollateral( + function _slashCollateral( address from, address to, uint256 amount @@ -53,7 +53,7 @@ abstract contract ICollateral { emit SlashCollateral(from, to, amount); } - function getFreeCollateral(address vaultId) + function _getFreeCollateral(address vaultId) internal view returns (uint256) @@ -65,7 +65,7 @@ abstract contract ICollateral { return CollateralBalances[vaultId]; } - function useCollateralInc(address vaultId, uint256 amount) internal virtual { + function _useCollateralInc(address vaultId, uint256 amount) internal virtual { CollateralUsed[vaultId] = CollateralUsed[vaultId].add(amount); require( CollateralBalances[vaultId] >= CollateralUsed[vaultId], @@ -73,7 +73,7 @@ abstract contract ICollateral { ); } - function useCollateralDec(address vaultId, uint256 amount) internal virtual { + function _useCollateralDec(address vaultId, uint256 amount) internal virtual { require(CollateralUsed[vaultId] >= amount, "Insufficient collateral"); CollateralUsed[vaultId] = CollateralUsed[vaultId].sub(amount); } diff --git a/contract/bridge/contracts/ExchangeRateOracleV2.sol b/contract/bridge/contracts/ExchangeRateOracleV2.sol index 95e67f7..7641f20 100644 --- a/contract/bridge/contracts/ExchangeRateOracleV2.sol +++ b/contract/bridge/contracts/ExchangeRateOracleV2.sol @@ -50,9 +50,9 @@ contract ExchangeRateOracleV2 is Initializable { } /** - @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. - @return uint256 (aggregate) exchange rate value - */ + * @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. + * @return uint256 (aggregate) exchange rate value + */ function getExchangeRate() private view returns (uint256) { AggregatorV3Interface oneUSD = AggregatorV3Interface( 0xcEe686F89bc0dABAd95AEAAC980aE1d97A075FAD diff --git a/contract/bridge/contracts/IVaultRegistry.sol b/contract/bridge/contracts/IVaultRegistry.sol index 2fdd2fa..9837d85 100644 --- a/contract/bridge/contracts/IVaultRegistry.sol +++ b/contract/bridge/contracts/IVaultRegistry.sol @@ -3,19 +3,6 @@ pragma solidity 0.6.12; interface IVaultRegistry { - // VaultRegistry - struct Vault { - uint256 btcPublicKeyX; - uint256 btcPublicKeyY; - uint256 collateral; - uint256 issued; - uint256 toBeIssued; - uint256 toBeRedeemed; - uint256 replaceCollateral; - uint256 toBeReplaced; - uint256 liquidatedCollateral; - } - // // set functions for Vault // function setBtcPublicKeyX(address, uint256) external; // function setBtcPublicKeyY(address, uint256) external; @@ -30,7 +17,7 @@ interface IVaultRegistry { // // set function for vaultDepositAddress // function setVaultDepositAddress(address, address, bool) external; - // interfaces for VaultRegistry + // interfaces used by VaultRegistry contract function vaults(address) external returns(uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256); function vaultDepositAddress(address, address) external returns(bool); function registerVault(uint256, uint256) external; @@ -55,4 +42,7 @@ interface IVaultRegistry { function slashCollateral(address, address, uint256) external; function useCollateralInc(address, uint256) external; function useCollateralDec(address, uint256) external; + + // interface used by StakedRelayer contract + function liquidateTheftVault(address vaultId, address reporterId) external; } diff --git a/contract/bridge/contracts/Issue.sol b/contract/bridge/contracts/Issue.sol index c76dbb0..a6d0f18 100644 --- a/contract/bridge/contracts/Issue.sol +++ b/contract/bridge/contracts/Issue.sol @@ -70,6 +70,7 @@ abstract contract Issue is Request { function getIssueGriefingCollateral(uint256 amountBtc) private + pure returns (uint256) { return amountBtc; diff --git a/contract/bridge/contracts/OneBtc.sol b/contract/bridge/contracts/OneBtc.sol index 62379dc..fd0aeba 100644 --- a/contract/bridge/contracts/OneBtc.sol +++ b/contract/bridge/contracts/OneBtc.sol @@ -31,7 +31,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { bytes calldata rawTx, bytes calldata header, bytes calldata merkleProof - ) public returns (bytes memory) { + ) public view returns (bytes memory) { bytes32 txId = rawTx.hash256(); relay.verifyTx( height, @@ -107,7 +107,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { internal override(Redeem) { - ERC20Upgradeable._transfer(msg.sender, address(this), amount); + ERC20Upgradeable._transfer(from, address(this), amount); } function burnLockedOneBTC(uint256 amount) internal override(Redeem) { @@ -156,7 +156,6 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { } function executeReplace( - IVaultRegistry vaultRegistry, uint256 replaceId, bytes calldata merkleProof, bytes calldata rawTx, // avoid compiler error: stack too deep diff --git a/contract/bridge/contracts/Redeem.sol b/contract/bridge/contracts/Redeem.sol index 4608fee..6b75a01 100644 --- a/contract/bridge/contracts/Redeem.sol +++ b/contract/bridge/contracts/Redeem.sol @@ -63,11 +63,11 @@ abstract contract Redeem is Request { ); } - function getRedeemCollateral(uint256 amountBtc) private returns (uint256) { + function getRedeemCollateral(uint256 amountBtc) private pure returns (uint256) { return amountBtc; } - function getCurrentInclusionFee() private returns (uint256) { + function getCurrentInclusionFee() private pure returns (uint256) { return 0; } diff --git a/contract/bridge/contracts/Replace.sol b/contract/bridge/contracts/Replace.sol index 7fc0dcf..4e1dcfe 100644 --- a/contract/bridge/contracts/Replace.sol +++ b/contract/bridge/contracts/Replace.sol @@ -57,13 +57,14 @@ abstract contract Replace is Request { ); } - function getReplaceBtcDustValue() private returns (uint256) { + function getReplaceBtcDustValue() private pure returns (uint256) { // TODO return 0; } function getReplaceGriefingCollateral(uint256 amountBtc) private + pure returns (uint256) { // TODO diff --git a/contract/bridge/contracts/Request.sol b/contract/bridge/contracts/Request.sol index aebfe1b..3ff933f 100644 --- a/contract/bridge/contracts/Request.sol +++ b/contract/bridge/contracts/Request.sol @@ -2,7 +2,7 @@ pragma solidity 0.6.12; -abstract contract Request { +contract Request { enum RequestStatus { None, Pending, diff --git a/contract/bridge/contracts/StakedRelayer.sol b/contract/bridge/contracts/StakedRelayer.sol index 31b7cf4..2ddc552 100644 --- a/contract/bridge/contracts/StakedRelayer.sol +++ b/contract/bridge/contracts/StakedRelayer.sol @@ -4,10 +4,7 @@ import {IRelay} from "./IRelay.sol"; import "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@interlay/bitcoin-spv-sol/contracts/BTCUtils.sol"; - -interface IVaultRegistry { - function liquidateTheftVault(address vaultId, address reporterId) external; -} +import "./IVaultRegistry.sol"; contract StakedRelayer is Initializable, OwnableUpgradeable { using BTCUtils for bytes; diff --git a/contract/bridge/contracts/VaultRegistry.sol b/contract/bridge/contracts/VaultRegistry.sol index 483eac6..29dee56 100644 --- a/contract/bridge/contracts/VaultRegistry.sol +++ b/contract/bridge/contracts/VaultRegistry.sol @@ -11,10 +11,23 @@ import {BitcoinKeyDerivation} from "./crypto/BitcoinKeyDerivation.sol"; import "./IExchangeRateOracle.sol"; import "./IVaultRegistry.sol"; -abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, ICollateral, IVaultRegistry { +contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, ICollateral { using SafeMathUpgradeable for uint256; - mapping(address => Vault) public override vaults; + // VaultRegistry + struct Vault { + uint256 btcPublicKeyX; + uint256 btcPublicKeyY; + uint256 collateral; + uint256 issued; + uint256 toBeIssued; + uint256 toBeRedeemed; + uint256 replaceCollateral; + uint256 toBeReplaced; + uint256 liquidatedCollateral; + } + + mapping(address => Vault) public vaults; mapping(address => mapping(address => bool)) vaultDepositAddress; IExchangeRateOracle oracle; address public oneBtcAddress; @@ -47,11 +60,6 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl _; } - modifier onlyOneBtcOrSelf() { - require(msg.sender == oneBtcAddress || msg.sender == address(this), "OnlyOneBtc"); - _; - } - function initialize(IExchangeRateOracle _oracle) external initializer { __ReentrancyGuard_init(); __Ownable_init(); @@ -59,12 +67,12 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl oracle = _oracle; } - function setOneBtcAddress(address _oneBtcAddress) external onlyOwner { + function setOneBtcAddress(address _oneBtcAddress) public onlyOwner { oneBtcAddress = _oneBtcAddress; } function registerVault(uint256 btcPublicKeyX, uint256 btcPublicKeyY) - external + public payable { address vaultId = msg.sender; @@ -82,7 +90,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function registerDepositAddress(address vaultId, uint256 issueId) - external + public onlyOneBtc returns (address) { @@ -108,7 +116,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl uint256 btcPublicKeyX, uint256 btcPublicKeyY, uint256 replaceId - ) external onlyOneBtc returns (address) { + ) public onlyOneBtc returns (address) { Vault storage vault = vaults[vaultId]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); @@ -128,7 +136,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function updatePublicKey(uint256 btcPublicKeyX, uint256 btcPublicKeyY) - external + public { address vaultId = msg.sender; Vault storage vault = vaults[vaultId]; @@ -143,18 +151,18 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl Vault storage vault = vaults[vaultId]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); vault.collateral = vault.collateral.add(msg.value); - ICollateral.lockCollateral(vaultId, msg.value); + ICollateral._lockCollateral(vaultId, msg.value); } - function withdrawCollateral(uint256 amount) external nonReentrant { + function withdrawCollateral(uint256 amount) public nonReentrant { Vault storage vault = vaults[msg.sender]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); vault.collateral = vault.collateral.sub(amount); - ICollateral.releaseCollateral(msg.sender, amount); + ICollateral._releaseCollateral(msg.sender, amount); } function decreaseToBeIssuedTokens(address vaultId, uint256 amount) - external + public onlyOneBtc { Vault storage vault = vaults[vaultId]; @@ -163,7 +171,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function tryIncreaseToBeIssuedTokens(address vaultId, uint256 amount) - external + public onlyOneBtc returns (bool) { @@ -176,7 +184,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function tryIncreaseToBeRedeemedTokens(address vaultId, uint256 amount) - external + public onlyOneBtc returns (bool) { @@ -193,7 +201,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl return vault.issued.sub(vault.toBeRedeemed); } - function redeemTokens(address vaultId, uint256 amount) external onlyOneBtc { + function redeemTokens(address vaultId, uint256 amount) public onlyOneBtc { Vault storage vault = vaults[vaultId]; vault.toBeRedeemed = vault.toBeRedeemed.sub(amount); vault.issued = vault.issued.sub(amount); @@ -201,14 +209,14 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function issuableTokens(address vaultId) public view returns (uint256) { - uint256 freeCollateral = ICollateral.getFreeCollateral(vaultId); + uint256 freeCollateral = ICollateral._getFreeCollateral(vaultId); return oracle.collateralToWrapped( freeCollateral.mul(100).div(secureCollateralThreshold()) ); } - function issueTokens(address vaultId, uint256 amount) external onlyOneBtc { + function issueTokens(address vaultId, uint256 amount) public onlyOneBtc { Vault storage vault = vaults[vaultId]; vault.issued = vault.issued.add(amount); vault.toBeIssued = vault.toBeIssued.sub(amount); @@ -229,7 +237,16 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl function requestableToBeReplacedTokens(address vaultId) public - onlyOneBtcOrSelf + view + onlyOneBtc + returns (uint256 amount) + { + return requestableToBeReplacedTokensFromSelf(vaultId); + } + + function requestableToBeReplacedTokensFromSelf(address vaultId) + internal + view returns (uint256 amount) { Vault memory vault = vaults[vaultId]; @@ -246,10 +263,10 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl address vaultId, uint256 tokens, uint256 collateral - ) external onlyOneBtc returns (uint256, uint256) { + ) public onlyOneBtc returns (uint256, uint256) { Vault storage vault = vaults[vaultId]; - uint256 requestableIncrease = requestableToBeReplacedTokens(vaultId); + uint256 requestableIncrease = requestableToBeReplacedTokensFromSelf(vaultId); require( tokens <= requestableIncrease, @@ -265,7 +282,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl } function decreaseToBeReplacedTokens(address vaultId, uint256 tokens) - external + public onlyOneBtc returns (uint256, uint256) { @@ -297,7 +314,7 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl address newVaultId, uint256 tokens, uint256 collateral - ) external onlyOneBtc { + ) public onlyOneBtc { Vault storage oldVault = vaults[oldVaultId]; Vault storage newVault = vaults[newVaultId]; @@ -313,11 +330,11 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl emit ReplaceTokens(oldVaultId, newVaultId, tokens, collateral); } - function tryDepositCollateral(address vaultId, uint256 amount) external onlyOneBtc { + function tryDepositCollateral(address vaultId, uint256 amount) public onlyOneBtc { Vault storage vault = vaults[vaultId]; require(vault.btcPublicKeyX != 0, "Vault does not exist"); - ICollateral.lockCollateral(vaultId, amount); + ICollateral._lockCollateral(vaultId, amount); // Self::increase_total_backing_collateral(amount)?; @@ -339,8 +356,8 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl liquidateVault.collateral = liquidateVault.collateral.add(amount); vault.collateral = vault.collateral.sub(amount); // slash collateral - ICollateral.slashCollateral(vaultId, address(this), amount); - ICollateral.lockCollateral(address(this), amount); // TODO; double check + ICollateral._slashCollateral(vaultId, address(this), amount); + ICollateral._lockCollateral(address(this), amount); // TODO; double check } function backedTokens(address vaultId) private returns (uint256) { @@ -416,33 +433,33 @@ abstract contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeabl /** * @dev Liquidate a vault by transferring all of its token balances to the liquidation vault. */ - function liquidateVault(address vaultId, address reporterId) external { + function liquidateVault(address vaultId, address reporterId) public { liquidate(vaultId, reporterId); } /// override functions for ICollateral - function lockCollateral(address sender, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { - ICollateral.lockCollateral(sender, amount); + function lockCollateral(address sender, uint256 amount) public virtual onlyOneBtc { + ICollateral._lockCollateral(sender, amount); } - function releaseCollateral(address sender, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc nonReentrant { - ICollateral.releaseCollateral(sender, amount); + function releaseCollateral(address sender, uint256 amount) public virtual onlyOneBtc nonReentrant { + ICollateral._releaseCollateral(sender, amount); } function slashCollateral( address from, address to, uint256 amount - ) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc nonReentrant { - ICollateral.slashCollateral(from, to, amount); + ) public virtual onlyOneBtc nonReentrant { + ICollateral._slashCollateral(from, to, amount); } - function useCollateralInc(address vaultId, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { - ICollateral.useCollateralInc(vaultId, amount); + function useCollateralInc(address vaultId, uint256 amount) public virtual onlyOneBtc { + ICollateral._useCollateralInc(vaultId, amount); } - function useCollateralDec(address vaultId, uint256 amount) external virtual override(ICollateral, IVaultRegistry) onlyOneBtc { - ICollateral.useCollateralDec(vaultId, amount); + function useCollateralDec(address vaultId, uint256 amount) public virtual onlyOneBtc { + ICollateral._useCollateralDec(vaultId, amount); } // set functions for Vault diff --git a/contract/bridge/test_test/CollateralTestWrapper.sol b/contract/bridge/contracts/test/CollateralTestWrapper.sol similarity index 71% rename from contract/bridge/test_test/CollateralTestWrapper.sol rename to contract/bridge/contracts/test/CollateralTestWrapper.sol index 0b256b7..aa01e83 100644 --- a/contract/bridge/test_test/CollateralTestWrapper.sol +++ b/contract/bridge/contracts/test/CollateralTestWrapper.sol @@ -5,11 +5,11 @@ import {ICollateral} from "../Collateral.sol"; contract CollateralTestWrapper is ICollateral { function testLockCollateral(address sender, uint256 amount) public payable { - return lockCollateral(sender, amount); + return _lockCollateral(sender, amount); } function testReleaseCollateral(address sender, uint256 amount) public { - return releaseCollateral(sender, amount); + return _releaseCollateral(sender, amount); } function testSlashCollateral( @@ -17,7 +17,7 @@ contract CollateralTestWrapper is ICollateral { address to, uint256 amount ) public { - return slashCollateral(from, to, amount); + return _slashCollateral(from, to, amount); } function testGetFreeCollateral(address vaultId) @@ -25,14 +25,14 @@ contract CollateralTestWrapper is ICollateral { view returns (uint256) { - return getFreeCollateral(vaultId); + return _getFreeCollateral(vaultId); } function testUseCollateralInc(address vaultId, uint256 amount) public { - return useCollateralInc(vaultId, amount); + return _useCollateralInc(vaultId, amount); } function testUseCollateralDec(address vaultId, uint256 amount) public { - return useCollateralDec(vaultId, amount); + return _useCollateralDec(vaultId, amount); } } diff --git a/contract/bridge/test_test/ExchangeRateOracleWrapper.sol b/contract/bridge/contracts/test/ExchangeRateOracleWrapper.sol similarity index 82% rename from contract/bridge/test_test/ExchangeRateOracleWrapper.sol rename to contract/bridge/contracts/test/ExchangeRateOracleWrapper.sol index 6e70169..2edb679 100644 --- a/contract/bridge/test_test/ExchangeRateOracleWrapper.sol +++ b/contract/bridge/contracts/test/ExchangeRateOracleWrapper.sol @@ -25,9 +25,9 @@ contract ExchangeRateOracleWrapper is Initializable { } /** - @notice Set the latest (aggregate) BTC/ONE exchange rate. This function invokes a check of vault collateral rates in the Vault Registry component. - @param rate uint256 BTC/ONE exchange rate. - */ + * @notice Set the latest (aggregate) BTC/ONE exchange rate. This function invokes a check of vault collateral rates in the Vault Registry component. + * @param rate uint256 BTC/ONE exchange rate. + */ function setExchangeRate(uint256 rate) public { exchangeRate = rate; lastExchangeRateTime = now; @@ -36,9 +36,9 @@ contract ExchangeRateOracleWrapper is Initializable { } /** - @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. - @return uint256 (aggregate) exchange rate value - */ + * @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. + * @return uint256 (aggregate) exchange rate value + */ function getExchangeRate() public view returns (uint256) { require( now - lastExchangeRateTime > MAX_DELAY, diff --git a/contract/bridge/test_test/VaultRegistryTestWrapper.sol b/contract/bridge/contracts/test/VaultRegistryWrapper.sol similarity index 91% rename from contract/bridge/test_test/VaultRegistryTestWrapper.sol rename to contract/bridge/contracts/test/VaultRegistryWrapper.sol index 1cf467f..235104a 100644 --- a/contract/bridge/test_test/VaultRegistryTestWrapper.sol +++ b/contract/bridge/contracts/test/VaultRegistryWrapper.sol @@ -3,7 +3,7 @@ pragma solidity 0.6.12; import {VaultRegistry} from "../VaultRegistry.sol"; -contract VaultRegistryTestWrapper is VaultRegistry { +contract VaultRegistryWrapper is VaultRegistry { event RedeemableTokens(address vaultId, uint256 amount); event RequestableToBeReplacedTokens(address vaultId, uint256 amount); @@ -29,7 +29,7 @@ contract VaultRegistryTestWrapper is VaultRegistry { public returns (bool) { - uint256 issuableTokens = getFreeCollateral(vaultId).mul(100).div(150); // mock oracle + uint256 issuableTokens = _getFreeCollateral(vaultId).mul(100).div(150); // mock oracle if (issuableTokens < amount) return false; // ExceedingVaultLimit Vault storage vault = vaults[vaultId]; vault.toBeIssued = vault.toBeIssued.add(amount); @@ -62,7 +62,7 @@ contract VaultRegistryTestWrapper is VaultRegistry { view returns (uint256) { - return getFreeCollateral(vaultId); + return _getFreeCollateral(vaultId); } function testRequestableToBeReplacedTokens(address vaultId) public returns (uint256) { From f676c3d5ca62b0bbd9c23fdb53678d3cb07592dc Mon Sep 17 00:00:00 2001 From: apptreeso Date: Wed, 26 Jan 2022 03:17:54 +1000 Subject: [PATCH 3/8] finish the unit tests --- contract/bridge/migrations/3_deploy_onebtc.js | 21 +++- contract/bridge/scripts/deploy_for_test.js | 50 ++++++++ .../deploy_onebtc.js | 0 .../deploy_onebtc_proxy.js | 0 .../deploy_oracle.js | 0 .../oracle_interact.js | 0 .../oracle_upgrade.js | 0 contract/bridge/test/Collateral.js | 18 +-- contract/bridge/test/Issue.test.js | 109 ++++++++++-------- contract/bridge/test/OneBTC.test.js | 57 ++++++--- contract/bridge/test/Redeem.test.js | 88 +++++++++----- contract/bridge/test/Replace.js | 59 ++++++++-- contract/bridge/test/TxValidate.test.js | 8 +- contract/bridge/test/VaultRegistry.js | 12 +- 14 files changed, 293 insertions(+), 129 deletions(-) create mode 100644 contract/bridge/scripts/deploy_for_test.js rename contract/bridge/{scripts => scripts_scripts}/deploy_onebtc.js (100%) rename contract/bridge/{scripts => scripts_scripts}/deploy_onebtc_proxy.js (100%) rename contract/bridge/{scripts => scripts_scripts}/deploy_oracle.js (100%) rename contract/bridge/{scripts => scripts_scripts}/oracle_interact.js (100%) rename contract/bridge/{scripts => scripts_scripts}/oracle_upgrade.js (100%) diff --git a/contract/bridge/migrations/3_deploy_onebtc.js b/contract/bridge/migrations/3_deploy_onebtc.js index 8a77719..37ca454 100644 --- a/contract/bridge/migrations/3_deploy_onebtc.js +++ b/contract/bridge/migrations/3_deploy_onebtc.js @@ -1,12 +1,23 @@ const { deployProxy } = require('@openzeppelin/truffle-upgrades'); -const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); +const OneBtc = artifacts.require("OneBtc"); module.exports = async function(deployer) { - const IRelay = await RelayMock.deployed(); - const IExchangeRateOracleWrapper = await ExchangeRateOracleWrapper.deployed(); + // RelayMock + const relayMock = await RelayMock.deployed(); + console.log("RelayMock contract deployed to: ", relayMock.address); + + // ExchangeRateOracleWrapper + const exchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + console.log("ExchangeRateOracleWrapper contract deployed to: ", exchangeRateOracleWrapper.address); + + // VaultRegistry + const vaultRegistry = await deployProxy(VaultRegistry, [exchangeRateOracleWrapper.address]); + console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); - const c = await deployProxy(OneBtc, [IRelay.address, IExchangeRateOracleWrapper.address], { deployer } ); - console.log(c.address) + // OneBtc + const oneBtc = await deployProxy(OneBtc, [relayMock.address, exchangeRateOracleWrapper.address, vaultRegistry.address]); + console.log("OneBtc contract deployed to: ", oneBtc.address); }; diff --git a/contract/bridge/scripts/deploy_for_test.js b/contract/bridge/scripts/deploy_for_test.js new file mode 100644 index 0000000..b255fad --- /dev/null +++ b/contract/bridge/scripts/deploy_for_test.js @@ -0,0 +1,50 @@ +const { ethers, upgrades } = require("hardhat"); + +async function main() { + // deployer + [deployer] = await ethers.getSigners(); + console.log('deployer address is ', deployer.address); + + const RelayMock = await ethers.getContractFactory("RelayMock"); + const relayMock = await RelayMock.deploy(); + await relayMock.deployed(); + console.log("RelayMock contract deployed to: ", relayMock.address); + + const ExchangeRateOracle = await ethers.getContractFactory("ExchangeRateOracle"); + const exchangeRateOracle = await upgrades.deployProxy(ExchangeRateOracle, [process.env.EXCHANGE_RATE_ORACLE]); + await exchangeRateOracle.deployed(); + console.log("ExchangeRateOracle contract deployed to: ", exchangeRateOracle.address); + + const VaultRegistry = await ethers.getContractFactory("VaultRegistry"); + const vaultRegistry = await upgrades.deployProxy(VaultRegistry, [exchangeRateOracle.address]); + await vaultRegistry.deployed(); + console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); + + const OneBtc = await ethers.getContractFactory("OneBtc"); + const oneBtc = await upgrades.deployProxy(OneBtc, [relayMock.address, exchangeRateOracle.address, vaultRegistry.address]); + await oneBtc.deployed(); + console.log("OneBtc contract deployed to: ", oneBtc.address); + + + // const Relay = await ethers.getContractFactory("RelayMock"); + // const relay = await Relay.attach( + // process.env.HMY_RELAY_CONTRACT + // ); + + // const ExchangeRateOracle = await ethers.getContractFactory("ExchangeRateOracle"); + // const oracle = await ExchangeRateOracle.attach( + // process.env.EXCHANGE_RATE_ORACLE + // ); + + // const OneBtc = await ethers.getContractFactory("OneBtc"); + // const oneBtc = await upgrades.deployProxy(OneBtc, [relay.address, oracle.address], { initializer: "initialize" }); + + // console.log("OneBtc deployed to:", oneBtc.address); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); \ No newline at end of file diff --git a/contract/bridge/scripts/deploy_onebtc.js b/contract/bridge/scripts_scripts/deploy_onebtc.js similarity index 100% rename from contract/bridge/scripts/deploy_onebtc.js rename to contract/bridge/scripts_scripts/deploy_onebtc.js diff --git a/contract/bridge/scripts/deploy_onebtc_proxy.js b/contract/bridge/scripts_scripts/deploy_onebtc_proxy.js similarity index 100% rename from contract/bridge/scripts/deploy_onebtc_proxy.js rename to contract/bridge/scripts_scripts/deploy_onebtc_proxy.js diff --git a/contract/bridge/scripts/deploy_oracle.js b/contract/bridge/scripts_scripts/deploy_oracle.js similarity index 100% rename from contract/bridge/scripts/deploy_oracle.js rename to contract/bridge/scripts_scripts/deploy_oracle.js diff --git a/contract/bridge/scripts/oracle_interact.js b/contract/bridge/scripts_scripts/oracle_interact.js similarity index 100% rename from contract/bridge/scripts/oracle_interact.js rename to contract/bridge/scripts_scripts/oracle_interact.js diff --git a/contract/bridge/scripts/oracle_upgrade.js b/contract/bridge/scripts_scripts/oracle_upgrade.js similarity index 100% rename from contract/bridge/scripts/oracle_upgrade.js rename to contract/bridge/scripts_scripts/oracle_upgrade.js diff --git a/contract/bridge/test/Collateral.js b/contract/bridge/test/Collateral.js index 6c5603b..1d42fc9 100644 --- a/contract/bridge/test/Collateral.js +++ b/contract/bridge/test/Collateral.js @@ -1,4 +1,5 @@ const BN = require("bn.js"); +const { expectRevert } = require("@openzeppelin/test-helpers"); const CollateralTestWrapper = artifacts.require("CollateralTestWrapper"); web3.extend({ @@ -33,22 +34,15 @@ contract("Collateral unit test", (accounts) => { }); it("Error on lockCollateral with 0 amount", async function () { - let errorMessage = ""; - - try { - await this.CollateralTestWrapper.testLockCollateral( - this.vaultId, - this.lockAmount, + const lockAmount = 0; + await expectRevert(this.CollateralTestWrapper.testLockCollateral( + this.vaultId, + lockAmount, { from: accounts[0], value: 0, } - ); - } catch (e) { - errorMessage = e.message.split("Reason given: ")[1]; - } - - assert.equal(errorMessage, "Invalid collateral."); + ), 'Invalid collateral'); }); it("LockCollateral with 1 Gwei amount", async function () { diff --git a/contract/bridge/test/Issue.test.js b/contract/bridge/test/Issue.test.js index 944fbcf..4b4f18c 100644 --- a/contract/bridge/test/Issue.test.js +++ b/contract/bridge/test/Issue.test.js @@ -1,7 +1,10 @@ const BN = require("bn.js"); const { expectRevert } = require("@openzeppelin/test-helpers"); const { web3 } = require("@openzeppelin/test-helpers/src/setup"); +const { deployProxy } = require("@openzeppelin/truffle-upgrades"); +const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const { issueTxMock } = require("./mock/btcTxMock"); @@ -27,8 +30,21 @@ web3.extend({ contract("Issue unit test", (accounts) => { before(async function () { - const IRelay = await RelayMock.new(); - this.OneBtc = await OneBtc.new(IRelay.address); + // get contracts + this.RelayMock = await RelayMock.new(); + this.ExchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + this.VaultRegistry = await deployProxy(VaultRegistry, [this.ExchangeRateOracleWrapper.address]); + this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); + + // set OneBtc address to VaultRegistry + this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + + // set BTC/ONE exchange rate + await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE + + // increase time to be enable exchange rate + await web3.miner.incTime(Number(1001)); // MAX_DELAY = 1000 + await web3.miner.mine(); this.vaultId = accounts[1]; this.issueRequester = accounts[2]; @@ -37,48 +53,49 @@ contract("Issue unit test", (accounts) => { this.OneBtcBalanceVault = 0; }); - it("Error on requestIssue with the exceeding vault limit", async function () { - const IssueAmount = Math.floor(10 * 1e8 * 100 / 150) + 1; // threshold = 150 - - await expectRevert(this.OneBtc.requestIssue(IssueAmount, this.vaultId, { - from: this.issueRequester, - value: IssueAmount, - }), 'ExceedingVaultLimit'); - }); - - it("Register Vault with 10 Wei Collateral", async function () { + it("Register Vault with 100e18 Wei Collateral", async function () { const VaultEcPair = bitcoin.ECPair.makeRandom({ compressed: false }); const pubX = bn(VaultEcPair.publicKey.slice(1, 33)); const pubY = bn(VaultEcPair.publicKey.slice(33, 65)); - const collateral = 10 * 1e8; - await this.OneBtc.registerVault(pubX, pubY, { + const collateral = new BN('10000000000000000000'); // 10e18, 10 ONE + await this.VaultRegistry.registerVault(pubX, pubY, { from: this.vaultId, value: collateral, }); - const vault = await this.OneBtc.vaults(this.vaultId); + const vault = await this.VaultRegistry.vaults(this.vaultId); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); - assert.equal(collateral, vault.collateral.toString()); + assert.equal(collateral.toString(), vault.collateral.toString()); }); it("Error on requestIssue with the insufficient collateral", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('100000000'); // 1e8, 1 OneBtc await expectRevert(this.OneBtc.requestIssue(IssueAmount+1, this.vaultId, { from: this.issueRequester, value: IssueAmount, - }), 'InsufficientCollateral'); + }), 'Insufficient collateral'); + }); + + it("Error on requestIssue with the exceeding vault limit", async function () { + const collateral = new BN('10000000000000000000'); // 10e18, 10 ONE + const IssueAmount = Math.floor(collateral * 100 / 150) + 1; // threshold = 150 + + await expectRevert(this.OneBtc.requestIssue(IssueAmount.toString(), this.vaultId, { + from: this.issueRequester, + value: IssueAmount.toString(), + }), 'Amount requested exceeds vault limit'); }); it("Issue 1 BTC", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -86,10 +103,10 @@ contract("Issue unit test", (accounts) => { Buffer.from(btcAddress.slice(2), "hex"), 0 ); - const btcTx = issueTxMock(issueId, btcBase58, IssueAmount); + const btcTx = issueTxMock(issueId, btcBase58, IssueAmount.toNumber()); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); const ExecuteReq = await this.OneBtc.executeIssue( @@ -97,7 +114,8 @@ contract("Issue unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock ); @@ -107,7 +125,7 @@ contract("Issue unit test", (accounts) => { assert.equal(this.OneBtcBalanceVault.toString(), IssueEvent.args.fee.toString()); const ExecuteEvent = ExecuteReq.logs.filter( - (log) => log.event == "IssueComplete" + (log) => log.event == "IssueCompleted" )[0]; assert.equal(ExecuteEvent.args.issuedId.toString(), issueId.toString()); @@ -117,25 +135,26 @@ contract("Issue unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxIndexMock, + btcTxIndexMock, headerMock - ), 'request is completed'); + ), 'Request is already completed'); // should not cancel the request which has been already completed await expectRevert(this.OneBtc.cancelIssue( this.issueRequester, issueId - ), 'request is completed'); + ), 'Request is already completed'); }); it("Error on requester is not a executor of issue call", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -146,7 +165,7 @@ contract("Issue unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount / 4); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); @@ -155,19 +174,20 @@ contract("Issue unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock - ), 'InvalidExecutor'); + ), 'Invalid executor'); }); it("Slash in case the transferred BTC amount is smaller than the requested OneBTC amount", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -178,7 +198,7 @@ contract("Issue unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount / 4); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); const ExecuteReq = await this.OneBtc.executeIssue( @@ -186,16 +206,13 @@ contract("Issue unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock, { from: this.issueRequester } ); - const ExecuteEvent = ExecuteReq.logs.filter( - (log) => log.event == "SlashCollateral" - )[0]; - assert.equal(ExecuteEvent.args.amount, IssueAmount - (IssueAmount / 4)); const beforeOneBtcBalance = this.OneBtcBalance; const beforeOneBtcBalanceVault = this.OneBtcBalanceVault; @@ -206,13 +223,13 @@ contract("Issue unit test", (accounts) => { }); it("Error on cancelIssue with the invalid cancel period", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -221,17 +238,17 @@ contract("Issue unit test", (accounts) => { 0 ); - await expectRevert(this.OneBtc.cancelIssue(this.issueRequester, issueId), 'TimeNotExpired'); + await expectRevert(this.OneBtc.cancelIssue(this.issueRequester, issueId), 'Time not expired'); }); it("Cancel issue", async function () { - const IssueAmount = 1 * 1e8; + const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -246,7 +263,7 @@ contract("Issue unit test", (accounts) => { const CancelReq = await this.OneBtc.cancelIssue(this.issueRequester, issueId); const CancelEvent = CancelReq.logs.filter( - (log) => log.event == "IssueCancel" + (log) => log.event == "IssueCanceled" )[0]; assert.equal(CancelEvent.args.issuedId.toString(), issueId.toString()); }); diff --git a/contract/bridge/test/OneBTC.test.js b/contract/bridge/test/OneBTC.test.js index f31752d..46872a5 100644 --- a/contract/bridge/test/OneBTC.test.js +++ b/contract/bridge/test/OneBTC.test.js @@ -1,9 +1,15 @@ +const BN = require("bn.js"); +const { expectRevert } = require("@openzeppelin/test-helpers"); +const { web3 } = require("@openzeppelin/test-helpers/src/setup"); +const { deployProxy } = require("@openzeppelin/truffle-upgrades"); + +const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); +const { issueTxMock } = require("./mock/btcTxMock"); -const { expectRevert } = require("@openzeppelin/test-helpers"); const bitcoin = require('bitcoinjs-lib'); -const { issueTxMock } = require('./mock/btcTxMock'); const bn=b=>BigInt(`0x${b.toString('hex')}`); web3.extend({ @@ -21,9 +27,22 @@ web3.extend({ contract("issue/redeem test", accounts => { before(async function() { - this.name="name"; - const IRelay = await RelayMock.new(); - this.OneBtc = await OneBtc.new(IRelay.address); + // get contracts + this.RelayMock = await RelayMock.new(); + this.ExchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + this.VaultRegistry = await deployProxy(VaultRegistry, [this.ExchangeRateOracleWrapper.address]); + this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); + + // set OneBtc address to VaultRegistry + this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + + // set BTC/ONE exchange rate + await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE + + // increase time to be enable exchange rate + await web3.miner.incTime(Number(1001)); // MAX_DELAY = 1000 + await web3.miner.mine(); + this.VaultEcPair = bitcoin.ECPair.makeRandom({compressed:false}); this.vaultId = accounts[1]; this.issueRequester = accounts[2]; @@ -36,26 +55,26 @@ contract("issue/redeem test", accounts => { const pubX = bn(this.VaultEcPair.publicKey.slice(1, 33)); const pubY = bn(this.VaultEcPair.publicKey.slice(33, 65)); const collateral = web3.utils.toWei('10'); - await this.OneBtc.registerVault(pubX, pubY, {from:this.vaultId, value: collateral}); - const vault = await this.OneBtc.vaults(this.vaultId); + await this.VaultRegistry.registerVault(pubX, pubY, {from:this.vaultId, value: collateral}); + const vault = await this.VaultRegistry.vaults(this.vaultId); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(collateral, vault.collateral.toString()); }); it("issue test", async function() { - const IssueAmount = 0.01*1e8; + const IssueAmount = 0.05*1e8; const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, {from: this.issueRequester, value:IssueAmount}); - const IssueEvent = IssueReq.logs.filter(log=>log.event == 'IssueRequest')[0]; + const IssueEvent = IssueReq.logs.filter(log=>log.event == 'IssueRequested')[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; const btcBase58 = bitcoin.address.toBase58Check(Buffer.from(btcAddress.slice(2), 'hex'), 0); const btcTx = issueTxMock(issueId, btcBase58, IssueAmount); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = btcBlockNumberMock<<32|btcTxIndexMock; + const btcHeightMock = btcBlockNumberMock<<32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); - await this.OneBtc.executeIssue(this.issueRequester, issueId, proofMock, btcTx.toBuffer(), heightAndIndex, headerMock); + await this.OneBtc.executeIssue(this.issueRequester, issueId, proofMock, btcTx.toBuffer(), btcHeightMock, btcTxIndexMock, headerMock); const OneBtcBalance = await this.OneBtc.balanceOf(this.issueRequester); const OneBtcBalanceVault = await this.OneBtc.balanceOf(this.vaultId); assert.equal(OneBtcBalance.toString(), IssueEvent.args.amount.toString()); @@ -65,7 +84,7 @@ contract("issue/redeem test", accounts => { const RedeemAmount = 0.001*1e8; await this.OneBtc.transfer(this.redeemRequester, RedeemAmount, {from: this.issueRequester}); const RedeemReq = await this.OneBtc.requestRedeem(RedeemAmount, this.redeemBtcAddress, this.vaultId, {from: this.redeemRequester}); - const RedeemEvent = RedeemReq.logs.filter(log=>log.event == 'RedeemRequest')[0]; + const RedeemEvent = RedeemReq.logs.filter(log=>log.event == 'RedeemRequested')[0]; const redeemId = RedeemEvent.args.redeemId; const btcAmount = RedeemEvent.args.amount; @@ -74,20 +93,20 @@ contract("issue/redeem test", accounts => { const btcTx = issueTxMock(redeemId, btcBase58, Number(btcAmount)); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = btcBlockNumberMock<<32|btcTxIndexMock; + const btcTxHeight = btcBlockNumberMock<<32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); - const execRedeem = await this.OneBtc.executeRedeem(this.redeemRequester, redeemId, proofMock, btcTx.toBuffer(), heightAndIndex, headerMock); - const redeemEvent = execRedeem.logs.filter(log=>log.event == 'RedeemComplete')[0]; + const execRedeem = await this.OneBtc.executeRedeem(this.redeemRequester, redeemId, proofMock, btcTx.toBuffer(), btcTxHeight, btcTxIndexMock, headerMock); + const redeemEvent = execRedeem.logs.filter(log=>log.event == 'RedeemCompleted')[0]; assert.equal(redeemEvent.args.requester, this.redeemRequester); }); it("cancle issue request test", async function() { const IssueAmount = 0.01*1e8; const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, {from: this.issueRequester, value:IssueAmount}); - const IssueEvent = IssueReq.logs.filter(log=>log.event == 'IssueRequest')[0]; + const IssueEvent = IssueReq.logs.filter(log=>log.event == 'IssueRequested')[0]; const issueId = IssueEvent.args.issueId; const request = await this.OneBtc.issueRequests(this.issueRequester, issueId); - await expectRevert(this.OneBtc.cancelIssue(this.issueRequester, issueId), 'TimeNotExpired'); + await expectRevert(this.OneBtc.cancelIssue(this.issueRequester, issueId), 'Time not expired'); await web3.miner.incTime(Number(request.period)+1); await this.OneBtc.cancelIssue(this.issueRequester, issueId); }); @@ -95,12 +114,12 @@ contract("issue/redeem test", accounts => { const RedeemAmount = 0.001*1e8; await this.OneBtc.transfer(this.redeemRequester, RedeemAmount, {from: this.issueRequester}); const RedeemReq = await this.OneBtc.requestRedeem(RedeemAmount, this.redeemBtcAddress, this.vaultId, {from: this.redeemRequester}); - const RedeemEvent = RedeemReq.logs.filter(log=>log.event == 'RedeemRequest')[0]; + const RedeemEvent = RedeemReq.logs.filter(log=>log.event == 'RedeemRequested')[0]; const redeemId = RedeemEvent.args.redeemId; const request = await this.OneBtc.redeemRequests(this.redeemRequester, redeemId); - await expectRevert(this.OneBtc.cancelRedeem(this.redeemRequester, redeemId), 'TimeNotExpired'); + await expectRevert(this.OneBtc.cancelRedeem(this.redeemRequester, redeemId), 'Time not expired'); await web3.miner.incTime(Number(request.period)+1); await this.OneBtc.cancelRedeem(this.redeemRequester, redeemId); }); diff --git a/contract/bridge/test/Redeem.test.js b/contract/bridge/test/Redeem.test.js index 7834d27..cec57b3 100644 --- a/contract/bridge/test/Redeem.test.js +++ b/contract/bridge/test/Redeem.test.js @@ -1,7 +1,10 @@ const BN = require("bn.js"); const { expectRevert } = require("@openzeppelin/test-helpers"); const { web3 } = require("@openzeppelin/test-helpers/src/setup"); +const { deployProxy } = require("@openzeppelin/truffle-upgrades"); +const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const { issueTxMock } = require("./mock/btcTxMock"); @@ -27,8 +30,21 @@ web3.extend({ contract("Redeem unit test", (accounts) => { before(async function () { - const IRelay = await RelayMock.new(); - this.OneBtc = await OneBtc.new(IRelay.address); + // get contracts + this.RelayMock = await RelayMock.new(); + this.ExchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + this.VaultRegistry = await deployProxy(VaultRegistry, [this.ExchangeRateOracleWrapper.address]); + this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); + + // set OneBtc address to VaultRegistry + this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + + // set BTC/ONE exchange rate + await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE + + // increase time to be enable exchange rate + await web3.miner.incTime(Number(1001)); // MAX_DELAY = 1000 + await web3.miner.mine(); this.vaultId = accounts[1]; this.issueRequester = accounts[2]; @@ -42,30 +58,30 @@ contract("Redeem unit test", (accounts) => { this.redeemBtcAddress = '0x'+script.hash.toString('hex'); }); - it("Register Vault with 10 Wei Collateral", async function () { + it("Register Vault with 10e18 Wei Collateral", async function () { const VaultEcPair = bitcoin.ECPair.makeRandom({ compressed: false }); const pubX = bn(VaultEcPair.publicKey.slice(1, 33)); const pubY = bn(VaultEcPair.publicKey.slice(33, 65)); - const collateral = 10 * 1e8; - await this.OneBtc.registerVault(pubX, pubY, { + const collateral = new BN('10000000000000000000'); // 10e18, 10 ONE + await this.VaultRegistry.registerVault(pubX, pubY, { from: this.vaultId, value: collateral, }); - const vault = await this.OneBtc.vaults(this.vaultId); + const vault = await this.VaultRegistry.vaults(this.vaultId); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(collateral, vault.collateral.toString()); }); - it("Issue 5 BTC", async function () { - const IssueAmount = 5 * 1e8; + it("Issue 0.5 BTC", async function () { + const IssueAmount = new BN('50000000'); // 0.5e8, 0.5 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, value: IssueAmount, }); const IssueEvent = IssueReq.logs.filter( - (log) => log.event == "IssueRequest" + (log) => log.event == "IssueRequested" )[0]; const issueId = IssueEvent.args.issueId; const btcAddress = IssueEvent.args.btcAddress; @@ -76,7 +92,7 @@ contract("Redeem unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, Number(IssueAmount)); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); const ExecuteReq = await this.OneBtc.executeIssue( @@ -84,24 +100,34 @@ contract("Redeem unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock ); + const ExecuteReqEvent = ExecuteReq.logs.filter( + (log) => log.event == "IssueCompleted" + )[0]; + + const fee = IssueAmount / 1000 * 2; // 0.2% fee + const requesterAmount = await this.OneBtc.balanceOf(this.issueRequester); + assert.equal(this.issueRequester, ExecuteReqEvent.args.requester); + assert.equal(requesterAmount.toString(), ExecuteReqEvent.args.amount.toString()); + assert.equal(fee.toString(), ExecuteReqEvent.args.fee.toString()); }); - it("Redeem 1 BTC", async function () { - // Transfer 1 OneBTC - const RedeemAmount = 1 * 1e8; + it("Redeem 0.1 BTC", async function () { + // Transfer 0.1 OneBTC + const RedeemAmount = new BN('1000000'); // 0.1e8, 0.1 OneBtc await this.OneBtc.transfer(this.redeemRequester, RedeemAmount, { from: this.issueRequester }); - // Redeem 1 OneBTC + // Redeem 0.1 OneBTC const beforeOneBtcBalanceVault = await this.OneBtc.balanceOf(this.vaultId); const RedeemReq = await this.OneBtc.requestRedeem(RedeemAmount, this.redeemBtcAddress, this.vaultId, { from: this.redeemRequester }); const RedeemEvent = RedeemReq.logs.filter( - (log) => log.event == "RedeemRequest" + (log) => log.event == "RedeemRequested" )[0]; const redeemId = RedeemEvent.args.redeemId; const amountBtc = RedeemEvent.args.amount; @@ -113,7 +139,7 @@ contract("Redeem unit test", (accounts) => { btcTx = issueTxMock(redeemId, btcBase58, Number(amountBtc)); btcBlockNumberMock = 1000; btcTxIndexMock = 2; - heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + btcTxHeightMock = btcBlockNumberMock << 32; headerMock = Buffer.alloc(0); proofMock = Buffer.alloc(0); const ExecuteReq = await this.OneBtc.executeRedeem( @@ -121,12 +147,13 @@ contract("Redeem unit test", (accounts) => { redeemId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock ); const ExecuteEvent = ExecuteReq.logs.filter( - (log) => log.event == "RedeemComplete" + (log) => log.event == "RedeemCompleted" )[0]; this.OneBtcBalanceVault = await this.OneBtc.balanceOf(this.vaultId); assert.equal(this.OneBtcBalanceVault.toString(), (Number(beforeOneBtcBalanceVault) + Number(ExecuteEvent.args.fee)).toString()); @@ -138,40 +165,41 @@ contract("Redeem unit test", (accounts) => { redeemId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock - ), 'request is completed'); + ), 'Request is already completed'); // should not cancel the request which has been already completed await expectRevert(this.OneBtc.cancelRedeem( this.redeemRequester, redeemId - ), 'request is completed'); + ), 'Request is already completed'); }); it("Error on cancelRedeem with the invalid cancel period", async function () { - // Transfer 1 OneBTC - const RedeemAmount = 1 * 1e8; + // Transfer 0.1 OneBTC + const RedeemAmount = new BN('1000000'); // 0.1e8, 0.1 OneBtc await this.OneBtc.transfer(this.redeemRequester, RedeemAmount, { from: this.issueRequester }); - // Redeem 1 OneBTC + // Redeem 0.1 OneBTC const RedeemReq = await this.OneBtc.requestRedeem(RedeemAmount, this.redeemBtcAddress, this.vaultId, { from: this.redeemRequester }); const RedeemEvent = RedeemReq.logs.filter( - (log) => log.event == "RedeemRequest" + (log) => log.event == "RedeemRequested" )[0]; const redeemId = RedeemEvent.args.redeemId; await expectRevert(this.OneBtc.cancelRedeem( this.redeemRequester, redeemId, - ), 'TimeNotExpired'); + ), 'Time not expired'); }); it("Cancel Redeem", async function () { // Transfer 1 OneBTC - const RedeemAmount = 1 * 1e8; + const RedeemAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc await this.OneBtc.transfer(this.redeemRequester, RedeemAmount, { from: this.issueRequester }); // Redeem 1 OneBTC @@ -179,7 +207,7 @@ contract("Redeem unit test", (accounts) => { from: this.redeemRequester }); const RedeemEvent = RedeemReq.logs.filter( - (log) => log.event == "RedeemRequest" + (log) => log.event == "RedeemRequested" )[0]; const redeemId = RedeemEvent.args.redeemId; @@ -189,7 +217,7 @@ contract("Redeem unit test", (accounts) => { const CancelReq = await this.OneBtc.cancelRedeem(this.redeemRequester, redeemId); const CancelEvent = CancelReq.logs.filter( - (log) => log.event == "RedeemCancel" + (log) => log.event == "RedeemCanceled" )[0]; assert.equal(CancelEvent.args.redeemId.toString(), redeemId.toString()); }); diff --git a/contract/bridge/test/Replace.js b/contract/bridge/test/Replace.js index b1e23e5..41255af 100644 --- a/contract/bridge/test/Replace.js +++ b/contract/bridge/test/Replace.js @@ -1,3 +1,10 @@ +const BN = require("bn.js"); +const { expectRevert } = require("@openzeppelin/test-helpers"); +const { web3 } = require("@openzeppelin/test-helpers/src/setup"); +const { deployProxy } = require("@openzeppelin/truffle-upgrades"); + +const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const { issueTxMock } = require("./mock/btcTxMock"); @@ -5,11 +12,39 @@ const { issueTxMock } = require("./mock/btcTxMock"); const bitcoin = require("bitcoinjs-lib"); const bn = (b) => BigInt(`0x${b.toString("hex")}`); +web3.extend({ + property: "miner", + methods: [ + { + name: "incTime", + call: "evm_increaseTime", + params: 1, + }, + { + name: "mine", + call: "evm_mine", + params: 0, + }, + ], +}); + contract("Replace unit test", (accounts) => { before(async function () { - this.name = "name"; - const IRelay = await RelayMock.new(); - this.OneBtc = await OneBtc.new(IRelay.address); + // get contracts + this.RelayMock = await RelayMock.new(); + this.ExchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + this.VaultRegistry = await deployProxy(VaultRegistry, [this.ExchangeRateOracleWrapper.address]); + this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); + + // set OneBtc address to VaultRegistry + this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + + // set BTC/ONE exchange rate + await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE + + // increase time to be enable exchange rate + await web3.miner.incTime(Number(1001)); // MAX_DELAY = 1000 + await web3.miner.mine(); this.vaultId = accounts[1]; this.issueRequester = accounts[2]; @@ -22,11 +57,11 @@ contract("Replace unit test", (accounts) => { const pubY = bn(VaultEcPair.publicKey.slice(33, 65)); const collateral = web3.utils.toWei("10"); - await this.OneBtc.registerVault(pubX, pubY, { + await this.VaultRegistry.registerVault(pubX, pubY, { from: this.vaultId, value: collateral, }); - const vault = await this.OneBtc.vaults(this.vaultId); + const vault = await this.VaultRegistry.vaults(this.vaultId); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(collateral, vault.collateral.toString()); @@ -50,7 +85,7 @@ contract("Replace unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32 const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); await this.OneBtc.executeIssue( @@ -58,7 +93,8 @@ contract("Replace unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock ); const OneBtcBalance = await this.OneBtc.balanceOf(this.issueRequester); @@ -96,11 +132,11 @@ contract("Replace unit test", (accounts) => { const pubY = bn(VaultEcPair.publicKey.slice(33, 65)); const collateral = web3.utils.toWei("10"); - await this.OneBtc.registerVault(pubX, pubY, { + await this.VaultRegistry.registerVault(pubX, pubY, { from: this.newVaultId, value: collateral, }); - const vault = await this.OneBtc.vaults(this.newVaultId); + const vault = await this.VaultRegistry.vaults(this.newVaultId); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(pubX.toString(), vault.btcPublicKeyX.toString()); assert.equal(collateral, vault.collateral.toString()); @@ -148,7 +184,7 @@ contract("Replace unit test", (accounts) => { const btcTx = issueTxMock(replaceId, btcBase58, btcAmount); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; + const btcTxHeightMock = btcBlockNumberMock << 32; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); @@ -156,7 +192,8 @@ contract("Replace unit test", (accounts) => { replaceId, proofMock, btcTx.toBuffer(), - heightAndIndex, + btcTxHeightMock, + btcTxIndexMock, headerMock ); diff --git a/contract/bridge/test/TxValidate.test.js b/contract/bridge/test/TxValidate.test.js index e7b447f..bea0c31 100644 --- a/contract/bridge/test/TxValidate.test.js +++ b/contract/bridge/test/TxValidate.test.js @@ -27,7 +27,7 @@ contract("transaction parse test", accounts => { const txMock = issueTxMock(requestId, receiverAddress, requestAmount); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId+1), "InvalidOpReturn"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId+1), "Invalid OpReturn"); }); it("validateTxout with less amount", async () => { const txUtils = await TxUtils.new(); @@ -38,7 +38,7 @@ contract("transaction parse test", accounts => { const txMock = issueTxMock(requestId, receiverAddress, requestAmount-1); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "InsufficientValue"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Insufficient BTC value"); }); it("validateTxout with more amount", async () => { const txUtils = await TxUtils.new(); @@ -61,7 +61,7 @@ contract("transaction parse test", accounts => { const txMock = issueTxMock(requestId, receiverAddress, requestAmount+10); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x1111111111111111111111111111111111111111'; - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "InvalidRecipient"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Invalid recipient"); }); it("validateTxout without requestId", async () => { const txUtils = await TxUtils.new(); @@ -72,6 +72,6 @@ contract("transaction parse test", accounts => { const txMock = issueTxMock(undefined, receiverAddress, requestAmount+10); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "NoOpRetrun"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Invalid OpReturn"); }); }); \ No newline at end of file diff --git a/contract/bridge/test/VaultRegistry.js b/contract/bridge/test/VaultRegistry.js index f453847..c29ad5c 100644 --- a/contract/bridge/test/VaultRegistry.js +++ b/contract/bridge/test/VaultRegistry.js @@ -1,4 +1,8 @@ -const VaultRegistryTestWrapper = artifacts.require("VaultRegistryTestWrapper"); +const { deployProxy } = require("@openzeppelin/truffle-upgrades"); + +const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistryWrapper = artifacts.require("VaultRegistryWrapper"); + const bitcoin = require("bitcoinjs-lib"); const bn = (b) => BigInt(`0x${b.toString("hex")}`); @@ -20,7 +24,11 @@ web3.extend({ contract("VaultRegistry unit test", (accounts) => { before(async function () { - this.VaultRegistry = await VaultRegistryTestWrapper.new(); + this.ExchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + this.VaultRegistry = await deployProxy(VaultRegistryWrapper, [this.ExchangeRateOracleWrapper.address]); + + // set OneBtc address with accounts[0] + await this.VaultRegistry.setOneBtcAddress(accounts[0]); this.vaultId = accounts[1]; this.issueRequester = accounts[2]; From 6a2b239da51ca991b003973459cbff3ccd7813f9 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Wed, 26 Jan 2022 22:19:14 +1000 Subject: [PATCH 4/8] resolve conflicts --- .../bridge/contracts/ExchangeRateOracleV2.sol | 28 +----- contract/bridge/contracts/Issue.sol | 6 +- contract/bridge/contracts/OneBtc.sol | 19 ++-- contract/bridge/contracts/Redeem.sol | 3 +- contract/bridge/contracts/Replace.sol | 3 +- contract/bridge/contracts/TxValidate.sol | 98 +++++++------------ .../bridge/contracts/mock/TxValidateMock.sol | 3 +- contract/bridge/migrations/3_deploy_onebtc.js | 21 +--- contract/bridge/scripts/deploy_for_test.js | 50 ---------- .../deploy_onebtc.js | 0 .../deploy_onebtc_proxy.js | 0 .../deploy_oracle.js | 2 + .../oracle_interact.js | 0 .../oracle_upgrade.js | 0 14 files changed, 72 insertions(+), 161 deletions(-) delete mode 100644 contract/bridge/scripts/deploy_for_test.js rename contract/bridge/{scripts_scripts => scripts}/deploy_onebtc.js (100%) rename contract/bridge/{scripts_scripts => scripts}/deploy_onebtc_proxy.js (100%) rename contract/bridge/{scripts_scripts => scripts}/deploy_oracle.js (99%) rename contract/bridge/{scripts_scripts => scripts}/oracle_interact.js (100%) rename contract/bridge/{scripts_scripts => scripts}/oracle_upgrade.js (100%) diff --git a/contract/bridge/contracts/ExchangeRateOracleV2.sol b/contract/bridge/contracts/ExchangeRateOracleV2.sol index 7641f20..49aa034 100644 --- a/contract/bridge/contracts/ExchangeRateOracleV2.sol +++ b/contract/bridge/contracts/ExchangeRateOracleV2.sol @@ -1,9 +1,7 @@ /** SPDX-License-Identifier: MIT - Exchange Rate Oracle Module https://onebtc-dev.web.app/spec/oracle.html - The Exchange Rate Oracle receives a continuous data feed on the exchange rate between BTC and ONE. */ @@ -33,32 +31,16 @@ contract ExchangeRateOracleV2 is Initializable { authorizedOracles[provider] = true; } - function setExchangeRate(uint256 btcPrice, uint256 onePrice) public { - address oracle = msg.sender; - require(authorizedOracles[oracle], "Sender is not authorized"); - - uint256 rate = btcPrice.div(onePrice); - exchangeRate = rate; - - if (lastExchangeRateTime - now > MAX_DELAY) { - emit recoverFromORACLEOFFLINE(oracle, rate); - } - - lastExchangeRateTime = now; - - emit SetExchangeRate(oracle, rate); - } - /** - * @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. - * @return uint256 (aggregate) exchange rate value - */ + @notice Returns the latest BTC/ONE exchange rate, as received from the external data sources. + @return uint256 (aggregate) exchange rate value + */ function getExchangeRate() private view returns (uint256) { AggregatorV3Interface oneUSD = AggregatorV3Interface( - 0xcEe686F89bc0dABAd95AEAAC980aE1d97A075FAD + 0xdCD81FbbD6c4572A69a534D8b8152c562dA8AbEF ); AggregatorV3Interface btcUSD = AggregatorV3Interface( - 0xEF637736B220a58C661bfF4b71e03ca898DCC0Bd + 0x3C41439Eb1bF3BA3b2C3f8C921088b267f8d11f4 ); (, int256 onePrice, , uint256 oneTimeStamp, ) = oneUSD .latestRoundData(); diff --git a/contract/bridge/contracts/Issue.sol b/contract/bridge/contracts/Issue.sol index a6d0f18..8d6ad76 100644 --- a/contract/bridge/contracts/Issue.sol +++ b/contract/bridge/contracts/Issue.sol @@ -146,7 +146,8 @@ abstract contract Issue is Request { IVaultRegistry vaultRegistry, address requester, uint256 issueId, - bytes memory _vout + bytes memory _vout, + uint256 outputIndex ) internal { IssueRequest storage request = issueRequests[requester][issueId]; require( @@ -157,7 +158,8 @@ abstract contract Issue is Request { _vout, 0, request.btcAddress, - 0x0 + 0x0, + outputIndex ); uint256 expectedTotalAmount = request.amount.add(request.fee); if (amountTransferred < expectedTotalAmount) { diff --git a/contract/bridge/contracts/OneBtc.sol b/contract/bridge/contracts/OneBtc.sol index fd0aeba..9bc6791 100644 --- a/contract/bridge/contracts/OneBtc.sol +++ b/contract/bridge/contracts/OneBtc.sol @@ -61,15 +61,20 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { uint256 issueId, bytes calldata merkleProof, bytes calldata rawTx, // avoid compiler error: stack too deep - //bytes calldata _version, bytes calldata _vin, bytes calldata _vout, bytes calldata _locktime, - uint32 height, - uint256 index, - bytes calldata header + //bytes calldata _version, bytes calldata _vin, bytes calldata _vout, bytes calldata _locktime, + uint64 heightAndIndex, + bytes calldata header, + uint256 outputIndex ) external { - bytes memory _vout = - verifyTx(height, index, rawTx, header, merkleProof); + bytes memory _vout = verifyTx( + uint32(heightAndIndex >> 32), + heightAndIndex & type(uint32).max, + rawTx, + header, + merkleProof + ); - Issue._executeIssue(vaultRegistry, requester, issueId, _vout); + Issue._executeIssue(requester, issueId, _vout, outputIndex); } function cancelIssue(address requester, uint256 issueId) external { diff --git a/contract/bridge/contracts/Redeem.sol b/contract/bridge/contracts/Redeem.sol index 6b75a01..7fcc3f0 100644 --- a/contract/bridge/contracts/Redeem.sol +++ b/contract/bridge/contracts/Redeem.sol @@ -132,7 +132,8 @@ abstract contract Redeem is Request { _vout, request.amountBtc, request.btcAddress, - redeemId + redeemId, + 0 ); burnLockedOneBTC(request.amountBtc); releaseLockedOneBTC(request.vault, request.fee); diff --git a/contract/bridge/contracts/Replace.sol b/contract/bridge/contracts/Replace.sol index 4e1dcfe..2d8e755 100644 --- a/contract/bridge/contracts/Replace.sol +++ b/contract/bridge/contracts/Replace.sol @@ -252,7 +252,8 @@ abstract contract Replace is Request { _vout, 0, replace.btcAddress, - replaceId + replaceId, + 0 ); require( diff --git a/contract/bridge/contracts/TxValidate.sol b/contract/bridge/contracts/TxValidate.sol index b320b56..c71dded 100644 --- a/contract/bridge/contracts/TxValidate.sol +++ b/contract/bridge/contracts/TxValidate.sol @@ -10,74 +10,52 @@ library TxValidate { using BTCUtils for bytes; using BytesLib for bytes; - function extractPaymentValueAndOpReturn( - bytes memory txVout, - address recipientBtcAddress - ) private pure returns (uint256 btcAmount, uint256 opReturn) { - (, uint256 _nVouts) = txVout.parseVarInt(); - uint256 voutCount = _nVouts; - bytes memory OP_RETURN_DATA; - address btcAddress; - for (uint256 i = 0; i < voutCount; i++) { - bytes memory vout = txVout.extractOutputAtIndex(i); - if (OP_RETURN_DATA.length == 0) { - OP_RETURN_DATA = vout.extractOpReturnData(); - if (OP_RETURN_DATA.length > 0) continue; - } - if (btcAddress != recipientBtcAddress) { - bytes memory bytesAddress = vout.extractHash(); - if ( - bytesAddress.length == 20 && - bytesAddress.toAddress(0) == recipientBtcAddress - ) { - btcAmount = vout.extractValue(); - btcAddress = recipientBtcAddress; - } - } - } - require(btcAddress == recipientBtcAddress, "Invalid recipient"); - - if(OP_RETURN_DATA.length > 0) { - opReturn = OP_RETURN_DATA.bytesToUint(); - } - } - function validateTransaction( bytes memory txVout, uint256 minimumBtc, address recipientBtcAddress, - uint256 opReturnId + uint256 opReturnId, + uint256 outputIndex ) internal pure returns (uint256) { - ( - uint256 extrPaymentValue, - uint256 extrOpReturn - ) = extractPaymentValueAndOpReturn(txVout, recipientBtcAddress); + uint256 btcAmount; + address btcAddress; + + if (opReturnId != 0x0) { + (, uint256 _nVouts) = txVout.parseVarInt(); + uint256 voutCount = _nVouts; + bytes memory OP_RETURN_DATA; + for (uint256 i = 0; i < voutCount; i++) { + bytes memory vout = txVout.extractOutputAtIndex(i); + if (OP_RETURN_DATA.length == 0) { + OP_RETURN_DATA = vout.extractOpReturnData(); + if (OP_RETURN_DATA.length > 0) continue; + } + if (btcAddress != recipientBtcAddress) { + bytes memory bytesAddress = vout.extractHash(); + if ( + bytesAddress.length == 20 && + bytesAddress.toAddress(0) == recipientBtcAddress + ) { + btcAmount = vout.extractValue(); + btcAddress = recipientBtcAddress; + } + } + } - if(opReturnId != 0x0) { - require(extrOpReturn == opReturnId, "Invalid OpReturn"); + require( + OP_RETURN_DATA.bytesToUint() == opReturnId, + "Invalid OpReturn" + ); + } else { + bytes memory vout = txVout.extractOutputAtIndex(outputIndex); + bytes memory bytesAddress = vout.extractHash(); + btcAmount = vout.extractValue(); + btcAddress = bytesAddress.toAddress(0); } - require(extrPaymentValue >= minimumBtc, "Insufficient BTC value"); - return extrPaymentValue; - } - - function extractOpReturnOnly(bytes memory txVout) - internal - pure - returns (uint256 opReturn) - { - (, uint256 _nVouts) = txVout.parseVarInt(); - uint256 voutCount = MathUpgradeable.min(_nVouts, 3); - bytes memory OP_RETURN_DATA; - for (uint256 i = 0; i < voutCount; i++) { - bytes memory vout = txVout.extractOutputAtIndex(i); - if (OP_RETURN_DATA.length == 0) { - OP_RETURN_DATA = vout.extractOpReturnData(); - if (OP_RETURN_DATA.length > 0) continue; - } - } + require(btcAmount >= minimumBtc, "Insufficient BTC value"); + require(btcAddress == recipientBtcAddress, "Invalid recipient"); - require(OP_RETURN_DATA.length > 0, "No OpRetrun"); - opReturn = OP_RETURN_DATA.bytesToUint(); + return btcAmount; } } diff --git a/contract/bridge/contracts/mock/TxValidateMock.sol b/contract/bridge/contracts/mock/TxValidateMock.sol index e439ddb..c3a5b02 100644 --- a/contract/bridge/contracts/mock/TxValidateMock.sol +++ b/contract/bridge/contracts/mock/TxValidateMock.sol @@ -16,7 +16,8 @@ contract TxValidateMock { txVout, minimumBtc, recipientBtcAddress, - opReturnId + opReturnId, + 0 ); } } diff --git a/contract/bridge/migrations/3_deploy_onebtc.js b/contract/bridge/migrations/3_deploy_onebtc.js index 37ca454..c1e6d5b 100644 --- a/contract/bridge/migrations/3_deploy_onebtc.js +++ b/contract/bridge/migrations/3_deploy_onebtc.js @@ -1,23 +1,12 @@ const { deployProxy } = require('@openzeppelin/truffle-upgrades'); +const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); -const VaultRegistry = artifacts.require("VaultRegistry"); -const OneBtc = artifacts.require("OneBtc"); module.exports = async function(deployer) { - // RelayMock - const relayMock = await RelayMock.deployed(); - console.log("RelayMock contract deployed to: ", relayMock.address); - - // ExchangeRateOracleWrapper - const exchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); - console.log("ExchangeRateOracleWrapper contract deployed to: ", exchangeRateOracleWrapper.address); - - // VaultRegistry - const vaultRegistry = await deployProxy(VaultRegistry, [exchangeRateOracleWrapper.address]); - console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); + const IRelay = await RelayMock.deployed(); + const IExchangeRateOracleWrapper = await ExchangeRateOracleWrapper.deployed(); - // OneBtc - const oneBtc = await deployProxy(OneBtc, [relayMock.address, exchangeRateOracleWrapper.address, vaultRegistry.address]); - console.log("OneBtc contract deployed to: ", oneBtc.address); + const c = await deployProxy(OneBtc, [IRelay.address, IExchangeRateOracleWrapper.address], { deployer } ); + console.log(c.address) }; diff --git a/contract/bridge/scripts/deploy_for_test.js b/contract/bridge/scripts/deploy_for_test.js deleted file mode 100644 index b255fad..0000000 --- a/contract/bridge/scripts/deploy_for_test.js +++ /dev/null @@ -1,50 +0,0 @@ -const { ethers, upgrades } = require("hardhat"); - -async function main() { - // deployer - [deployer] = await ethers.getSigners(); - console.log('deployer address is ', deployer.address); - - const RelayMock = await ethers.getContractFactory("RelayMock"); - const relayMock = await RelayMock.deploy(); - await relayMock.deployed(); - console.log("RelayMock contract deployed to: ", relayMock.address); - - const ExchangeRateOracle = await ethers.getContractFactory("ExchangeRateOracle"); - const exchangeRateOracle = await upgrades.deployProxy(ExchangeRateOracle, [process.env.EXCHANGE_RATE_ORACLE]); - await exchangeRateOracle.deployed(); - console.log("ExchangeRateOracle contract deployed to: ", exchangeRateOracle.address); - - const VaultRegistry = await ethers.getContractFactory("VaultRegistry"); - const vaultRegistry = await upgrades.deployProxy(VaultRegistry, [exchangeRateOracle.address]); - await vaultRegistry.deployed(); - console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); - - const OneBtc = await ethers.getContractFactory("OneBtc"); - const oneBtc = await upgrades.deployProxy(OneBtc, [relayMock.address, exchangeRateOracle.address, vaultRegistry.address]); - await oneBtc.deployed(); - console.log("OneBtc contract deployed to: ", oneBtc.address); - - - // const Relay = await ethers.getContractFactory("RelayMock"); - // const relay = await Relay.attach( - // process.env.HMY_RELAY_CONTRACT - // ); - - // const ExchangeRateOracle = await ethers.getContractFactory("ExchangeRateOracle"); - // const oracle = await ExchangeRateOracle.attach( - // process.env.EXCHANGE_RATE_ORACLE - // ); - - // const OneBtc = await ethers.getContractFactory("OneBtc"); - // const oneBtc = await upgrades.deployProxy(OneBtc, [relay.address, oracle.address], { initializer: "initialize" }); - - // console.log("OneBtc deployed to:", oneBtc.address); -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); \ No newline at end of file diff --git a/contract/bridge/scripts_scripts/deploy_onebtc.js b/contract/bridge/scripts/deploy_onebtc.js similarity index 100% rename from contract/bridge/scripts_scripts/deploy_onebtc.js rename to contract/bridge/scripts/deploy_onebtc.js diff --git a/contract/bridge/scripts_scripts/deploy_onebtc_proxy.js b/contract/bridge/scripts/deploy_onebtc_proxy.js similarity index 100% rename from contract/bridge/scripts_scripts/deploy_onebtc_proxy.js rename to contract/bridge/scripts/deploy_onebtc_proxy.js diff --git a/contract/bridge/scripts_scripts/deploy_oracle.js b/contract/bridge/scripts/deploy_oracle.js similarity index 99% rename from contract/bridge/scripts_scripts/deploy_oracle.js rename to contract/bridge/scripts/deploy_oracle.js index aece703..61114f8 100644 --- a/contract/bridge/scripts_scripts/deploy_oracle.js +++ b/contract/bridge/scripts/deploy_oracle.js @@ -1,3 +1,5 @@ + + const { ethers } = require("hardhat"); const Web3 = require("web3"); const web3 = new Web3(); diff --git a/contract/bridge/scripts_scripts/oracle_interact.js b/contract/bridge/scripts/oracle_interact.js similarity index 100% rename from contract/bridge/scripts_scripts/oracle_interact.js rename to contract/bridge/scripts/oracle_interact.js diff --git a/contract/bridge/scripts_scripts/oracle_upgrade.js b/contract/bridge/scripts/oracle_upgrade.js similarity index 100% rename from contract/bridge/scripts_scripts/oracle_upgrade.js rename to contract/bridge/scripts/oracle_upgrade.js From 97ad1c38b635d15e836fb89ecb0db144b9a45f05 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Wed, 26 Jan 2022 23:19:12 +1000 Subject: [PATCH 5/8] update OneBtc and TxValidationMock contracts after resolving conflicts --- contract/bridge/contracts/OneBtc.sol | 2 +- contract/bridge/contracts/mock/TxValidateMock.sol | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/contract/bridge/contracts/OneBtc.sol b/contract/bridge/contracts/OneBtc.sol index 9bc6791..d961106 100644 --- a/contract/bridge/contracts/OneBtc.sol +++ b/contract/bridge/contracts/OneBtc.sol @@ -74,7 +74,7 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { merkleProof ); - Issue._executeIssue(requester, issueId, _vout, outputIndex); + Issue._executeIssue(vaultRegistry, requester, issueId, _vout, outputIndex); } function cancelIssue(address requester, uint256 issueId) external { diff --git a/contract/bridge/contracts/mock/TxValidateMock.sol b/contract/bridge/contracts/mock/TxValidateMock.sol index c3a5b02..c1e0772 100644 --- a/contract/bridge/contracts/mock/TxValidateMock.sol +++ b/contract/bridge/contracts/mock/TxValidateMock.sol @@ -9,7 +9,8 @@ contract TxValidateMock { bytes memory txVout, uint256 minimumBtc, address recipientBtcAddress, - uint256 opReturnId + uint256 opReturnId, + uint256 outputIndex ) public pure returns (uint256) { return TxValidate.validateTransaction( @@ -17,7 +18,7 @@ contract TxValidateMock { minimumBtc, recipientBtcAddress, opReturnId, - 0 + outputIndex ); } } From d6d390c14fc9702178e050fa964e43b801e5b042 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Wed, 26 Jan 2022 23:19:43 +1000 Subject: [PATCH 6/8] update deployment scripts with VaultRegistry address --- contract/bridge/migrations/3_deploy_onebtc.js | 21 ++++++++++++++----- .../bridge/scripts/deploy_onebtc_proxy.js | 11 +++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/contract/bridge/migrations/3_deploy_onebtc.js b/contract/bridge/migrations/3_deploy_onebtc.js index c1e6d5b..37ca454 100644 --- a/contract/bridge/migrations/3_deploy_onebtc.js +++ b/contract/bridge/migrations/3_deploy_onebtc.js @@ -1,12 +1,23 @@ const { deployProxy } = require('@openzeppelin/truffle-upgrades'); -const OneBtc = artifacts.require("OneBtc"); const RelayMock = artifacts.require("RelayMock"); const ExchangeRateOracleWrapper = artifacts.require("ExchangeRateOracleWrapper"); +const VaultRegistry = artifacts.require("VaultRegistry"); +const OneBtc = artifacts.require("OneBtc"); module.exports = async function(deployer) { - const IRelay = await RelayMock.deployed(); - const IExchangeRateOracleWrapper = await ExchangeRateOracleWrapper.deployed(); + // RelayMock + const relayMock = await RelayMock.deployed(); + console.log("RelayMock contract deployed to: ", relayMock.address); + + // ExchangeRateOracleWrapper + const exchangeRateOracleWrapper = await deployProxy(ExchangeRateOracleWrapper); + console.log("ExchangeRateOracleWrapper contract deployed to: ", exchangeRateOracleWrapper.address); + + // VaultRegistry + const vaultRegistry = await deployProxy(VaultRegistry, [exchangeRateOracleWrapper.address]); + console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); - const c = await deployProxy(OneBtc, [IRelay.address, IExchangeRateOracleWrapper.address], { deployer } ); - console.log(c.address) + // OneBtc + const oneBtc = await deployProxy(OneBtc, [relayMock.address, exchangeRateOracleWrapper.address, vaultRegistry.address]); + console.log("OneBtc contract deployed to: ", oneBtc.address); }; diff --git a/contract/bridge/scripts/deploy_onebtc_proxy.js b/contract/bridge/scripts/deploy_onebtc_proxy.js index ad962b5..9beb406 100644 --- a/contract/bridge/scripts/deploy_onebtc_proxy.js +++ b/contract/bridge/scripts/deploy_onebtc_proxy.js @@ -11,10 +11,15 @@ async function main() { process.env.EXCHANGE_RATE_ORACLE ); - const OneBtc = await ethers.getContractFactory("OneBtc"); - const oneBtc = await upgrades.deployProxy(OneBtc, [relay.address, oracle.address], { initializer: "initialize" }); + const VaultRegistry = await ethers.getContractFactory("VaultRegistry"); + const vaultRegistry = await upgrades.deployProxy(VaultRegistry, [exchangeRateOracle.address]); + await vaultRegistry.deployed(); + console.log("VaultRegistry contract deployed to: ", vaultRegistry.address); - console.log("OneBtc deployed to:", oneBtc.address); + const OneBtc = await ethers.getContractFactory("OneBtc"); + const oneBtc = await upgrades.deployProxy(OneBtc, [relayMock.address, exchangeRateOracle.address, vaultRegistry.address]); + await oneBtc.deployed(); + console.log("OneBtc contract deployed to: ", oneBtc.address); } main() From 16fbd6742df4ea68d2bb49b0b01c996c9b455b32 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Wed, 26 Jan 2022 23:20:05 +1000 Subject: [PATCH 7/8] update all tests after resolving conflicts --- contract/bridge/test/Issue.test.js | 34 +++++++++++++------------ contract/bridge/test/OneBTC.test.js | 5 ++-- contract/bridge/test/Redeem.test.js | 9 ++++--- contract/bridge/test/Replace.js | 9 ++++--- contract/bridge/test/TxValidate.test.js | 20 ++++++++++----- 5 files changed, 44 insertions(+), 33 deletions(-) diff --git a/contract/bridge/test/Issue.test.js b/contract/bridge/test/Issue.test.js index 4b4f18c..c049478 100644 --- a/contract/bridge/test/Issue.test.js +++ b/contract/bridge/test/Issue.test.js @@ -88,7 +88,7 @@ contract("Issue unit test", (accounts) => { }), 'Amount requested exceeds vault limit'); }); - it("Issue 1 BTC", async function () { + it("Issue 0.1 BTC", async function () { const IssueAmount = new BN('10000000'); // 0.1e8, 0.1 OneBtc const IssueReq = await this.OneBtc.requestIssue(IssueAmount, this.vaultId, { from: this.issueRequester, @@ -106,17 +106,18 @@ contract("Issue unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount.toNumber()); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcTxHeightMock = btcBlockNumberMock << 32; + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); + const ouputIndexMock = 0; const ExecuteReq = await this.OneBtc.executeIssue( this.issueRequester, issueId, proofMock, btcTx.toBuffer(), - btcTxHeightMock, - btcTxIndexMock, - headerMock + heightAndIndex, + headerMock, + ouputIndexMock ); this.OneBtcBalance = await this.OneBtc.balanceOf(this.issueRequester); @@ -135,9 +136,9 @@ contract("Issue unit test", (accounts) => { issueId, proofMock, btcTx.toBuffer(), - btcTxIndexMock, - btcTxIndexMock, - headerMock + heightAndIndex, + headerMock, + ouputIndexMock ), 'Request is already completed'); // should not cancel the request which has been already completed @@ -165,18 +166,18 @@ contract("Issue unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount / 4); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcTxHeightMock = btcBlockNumberMock << 32; + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); - + const ouputIndexMock = 0; await expectRevert(this.OneBtc.executeIssue( this.issueRequester, issueId, proofMock, btcTx.toBuffer(), - btcTxHeightMock, - btcTxIndexMock, - headerMock + heightAndIndex, + headerMock, + ouputIndexMock ), 'Invalid executor'); }); @@ -198,17 +199,18 @@ contract("Issue unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount / 4); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcTxHeightMock = btcBlockNumberMock << 32; + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); + const ouputIndexMock = 0; const ExecuteReq = await this.OneBtc.executeIssue( this.issueRequester, issueId, proofMock, btcTx.toBuffer(), - btcTxHeightMock, - btcTxIndexMock, + heightAndIndex, headerMock, + ouputIndexMock, { from: this.issueRequester } diff --git a/contract/bridge/test/OneBTC.test.js b/contract/bridge/test/OneBTC.test.js index 46872a5..09b422b 100644 --- a/contract/bridge/test/OneBTC.test.js +++ b/contract/bridge/test/OneBTC.test.js @@ -71,10 +71,11 @@ contract("issue/redeem test", accounts => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcHeightMock = btcBlockNumberMock<<32; + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); - await this.OneBtc.executeIssue(this.issueRequester, issueId, proofMock, btcTx.toBuffer(), btcHeightMock, btcTxIndexMock, headerMock); + const ouputIndexMock = 0; + await this.OneBtc.executeIssue(this.issueRequester, issueId, proofMock, btcTx.toBuffer(), heightAndIndex, headerMock, ouputIndexMock); const OneBtcBalance = await this.OneBtc.balanceOf(this.issueRequester); const OneBtcBalanceVault = await this.OneBtc.balanceOf(this.vaultId); assert.equal(OneBtcBalance.toString(), IssueEvent.args.amount.toString()); diff --git a/contract/bridge/test/Redeem.test.js b/contract/bridge/test/Redeem.test.js index cec57b3..e78fc65 100644 --- a/contract/bridge/test/Redeem.test.js +++ b/contract/bridge/test/Redeem.test.js @@ -92,17 +92,18 @@ contract("Redeem unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, Number(IssueAmount)); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcTxHeightMock = btcBlockNumberMock << 32; + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); + const ouputIndexMock = 0; const ExecuteReq = await this.OneBtc.executeIssue( this.issueRequester, issueId, proofMock, btcTx.toBuffer(), - btcTxHeightMock, - btcTxIndexMock, - headerMock + heightAndIndex, + headerMock, + ouputIndexMock ); const ExecuteReqEvent = ExecuteReq.logs.filter( (log) => log.event == "IssueCompleted" diff --git a/contract/bridge/test/Replace.js b/contract/bridge/test/Replace.js index 41255af..32be47c 100644 --- a/contract/bridge/test/Replace.js +++ b/contract/bridge/test/Replace.js @@ -85,17 +85,18 @@ contract("Replace unit test", (accounts) => { const btcTx = issueTxMock(issueId, btcBase58, IssueAmount); const btcBlockNumberMock = 1000; const btcTxIndexMock = 2; - const btcTxHeightMock = btcBlockNumberMock << 32 + const heightAndIndex = (btcBlockNumberMock << 32) | btcTxIndexMock; const headerMock = Buffer.alloc(0); const proofMock = Buffer.alloc(0); + const ouputIndexMock = 0; await this.OneBtc.executeIssue( this.issueRequester, issueId, proofMock, btcTx.toBuffer(), - btcTxHeightMock, - btcTxIndexMock, - headerMock + heightAndIndex, + headerMock, + ouputIndexMock ); const OneBtcBalance = await this.OneBtc.balanceOf(this.issueRequester); const OneBtcBalanceVault = await this.OneBtc.balanceOf(this.vaultId); diff --git a/contract/bridge/test/TxValidate.test.js b/contract/bridge/test/TxValidate.test.js index bea0c31..acae6da 100644 --- a/contract/bridge/test/TxValidate.test.js +++ b/contract/bridge/test/TxValidate.test.js @@ -11,11 +11,12 @@ contract("transaction parse test", accounts => { const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; const requestId = 123; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(requestId, receiverAddress, requestAmount); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - const amount = await txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId); + const amount = await txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId, outputIndex); assert.equal(Number(amount), requestAmount); }); it("validateTxout with wrong requestId", async () => { @@ -23,55 +24,60 @@ contract("transaction parse test", accounts => { const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; const requestId = 123; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(requestId, receiverAddress, requestAmount); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId+1), "Invalid OpReturn"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId+1, outputIndex), "Invalid OpReturn"); }); it("validateTxout with less amount", async () => { const txUtils = await TxUtils.new(); const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; const requestId = 123; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(requestId, receiverAddress, requestAmount-1); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Insufficient BTC value"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId, outputIndex), "Insufficient BTC value"); }); it("validateTxout with more amount", async () => { const txUtils = await TxUtils.new(); const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; const requestId = 123; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(requestId, receiverAddress, requestAmount+10); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - const amount = await txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId); + const amount = await txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId, outputIndex); assert.equal(Number(amount), requestAmount+10); }); it("validateTxout with wrong receiver", async () => { const txUtils = await TxUtils.new(); const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; - const requestId = 123; + const requestId = 0; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(requestId, receiverAddress, requestAmount+10); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x1111111111111111111111111111111111111111'; - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Invalid recipient"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId, outputIndex), "Invalid recipient"); }); it("validateTxout without requestId", async () => { const txUtils = await TxUtils.new(); const txValidate = await TxValidate.new(); const receiverAddress = '12L1QTVLowqsRUNht35RyBE3RZzmCZzYF3'; const requestId = 123; + const outputIndex = 0; const requestAmount = 2.5e8; const txMock = issueTxMock(undefined, receiverAddress, requestAmount+10); const parsedTx = await txUtils.extractTx(txMock.toBuffer()); const receiverAddressHex = '0x'+bitcoin.address.fromBase58Check(receiverAddress).hash.toString('hex'); - await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId), "Invalid OpReturn"); + await expectRevert(txValidate.validateTransaction(parsedTx.vouts, requestAmount, receiverAddressHex, requestId, outputIndex), "Invalid OpReturn"); }); }); \ No newline at end of file From c9f0b43b96b4c7193f2d805da722ead75659e096 Mon Sep 17 00:00:00 2001 From: apptreeso Date: Fri, 4 Feb 2022 12:19:41 +1000 Subject: [PATCH 8/8] add functions to update the contract address --- contract/bridge/contracts/OneBtc.sol | 29 ++++++++++++++++++++- contract/bridge/contracts/VaultRegistry.sol | 15 +++++++++-- contract/bridge/test/Issue.test.js | 2 +- contract/bridge/test/OneBTC.test.js | 2 +- contract/bridge/test/Redeem.test.js | 2 +- contract/bridge/test/Replace.js | 2 +- contract/bridge/test/VaultRegistry.js | 2 +- 7 files changed, 46 insertions(+), 8 deletions(-) diff --git a/contract/bridge/contracts/OneBtc.sol b/contract/bridge/contracts/OneBtc.sol index d961106..5e05ef9 100644 --- a/contract/bridge/contracts/OneBtc.sol +++ b/contract/bridge/contracts/OneBtc.sol @@ -2,6 +2,7 @@ pragma solidity 0.6.12; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import {ValidateSPV} from "@interlay/bitcoin-spv-sol/contracts/ValidateSPV.sol"; import {TransactionUtils} from "./TransactionUtils.sol"; @@ -12,12 +13,17 @@ import {IRelay} from "./IRelay.sol"; import "./IExchangeRateOracle.sol"; import "./IVaultRegistry.sol"; -contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { +contract OneBtc is OwnableUpgradeable, ERC20Upgradeable, Issue, Redeem, Replace { + event UpdateRelayAddress(address indexed by, address indexed oldRelay, address indexed newRelay); + event UpdateOracleAddress(address indexed by, address indexed oldOracle, address indexed newOracle); + event UpdateVaultRegistryAddress(address indexed by, address oldVaultRegistry, address indexed newVaultRegistry); + IRelay public relay; IVaultRegistry public vaultRegistry; IExchangeRateOracle oracle; function initialize(IRelay _relay, IExchangeRateOracle _oracle, IVaultRegistry _vaultRegistry) external initializer { + __Ownable_init(); __ERC20_init("Harmony Bitcoin", "1BTC"); _setupDecimals(8); relay = _relay; @@ -172,4 +178,25 @@ contract OneBtc is ERC20Upgradeable, Issue, Redeem, Replace { bytes memory _vout = verifyTx(height, index, rawTx, header, merkleProof); Replace._executeReplace(vaultRegistry, replaceId, _vout); } + + function updateRelayAddress(IRelay _relay) external onlyOwner { + UpdateRelayAddress(msg.sender, address(relay), address(_relay)); + + require(address(_relay) != address(0), "Zero address for Relay"); + relay = _relay; + } + + function updateOracleAddress(IExchangeRateOracle _oracle) external onlyOwner { + UpdateOracleAddress(msg.sender, address(oracle), address(_oracle)); + + require(address(_oracle) != address(0), "Zero address for Oracle"); + oracle = _oracle; + } + + function updateVaultRegistryAddress(IVaultRegistry _vaultRegistry) external onlyOwner { + UpdateVaultRegistryAddress(msg.sender, address(vaultRegistry), address(_vaultRegistry)); + + require(address(_vaultRegistry) != address(0), "Zero addess for VaultRegistry"); + vaultRegistry = _vaultRegistry; + } } diff --git a/contract/bridge/contracts/VaultRegistry.sol b/contract/bridge/contracts/VaultRegistry.sol index 29dee56..03600fb 100644 --- a/contract/bridge/contracts/VaultRegistry.sol +++ b/contract/bridge/contracts/VaultRegistry.sol @@ -38,7 +38,6 @@ contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, IColla uint256 btcPublicKeyX, uint256 btcPublicKeyY ); - event VaultPublicKeyUpdate(address indexed vaultId, uint256 x, uint256 y); event IncreaseToBeIssuedTokens(address indexed vaultId, uint256 amount); event IncreaseToBeRedeemedTokens(address indexed vaultId, uint256 amount); @@ -54,6 +53,8 @@ contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, IColla uint256 collateral ); event LiquidateVault(); + event UpdateOneBtcAddress(address indexed by, address indexed oldOneBtc, address indexed newOneBtc); + event UpdateOracleAddress(address indexed by, address indexed oldOracle, address indexed newOracle); modifier onlyOneBtc() { require(msg.sender == oneBtcAddress, "OnlyOneBtc"); @@ -67,10 +68,20 @@ contract VaultRegistry is ReentrancyGuardUpgradeable, OwnableUpgradeable, IColla oracle = _oracle; } - function setOneBtcAddress(address _oneBtcAddress) public onlyOwner { + function updateOneBtcAddress(address _oneBtcAddress) public onlyOwner { + UpdateOneBtcAddress(msg.sender, oneBtcAddress, _oneBtcAddress); + + require(_oneBtcAddress != address(0), "Zero address for OneBtc"); oneBtcAddress = _oneBtcAddress; } + function updateOracleAddress(IExchangeRateOracle _oracle) external onlyOwner { + UpdateOracleAddress(msg.sender, address(oracle), address(_oracle)); + + require(address(_oracle) != address(0), "Zero address for Oracle"); + oracle = _oracle; + } + function registerVault(uint256 btcPublicKeyX, uint256 btcPublicKeyY) public payable diff --git a/contract/bridge/test/Issue.test.js b/contract/bridge/test/Issue.test.js index c049478..609ae37 100644 --- a/contract/bridge/test/Issue.test.js +++ b/contract/bridge/test/Issue.test.js @@ -37,7 +37,7 @@ contract("Issue unit test", (accounts) => { this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); // set OneBtc address to VaultRegistry - this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + this.VaultRegistry.updateOneBtcAddress(this.OneBtc.address); // set BTC/ONE exchange rate await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE diff --git a/contract/bridge/test/OneBTC.test.js b/contract/bridge/test/OneBTC.test.js index 09b422b..7c4553a 100644 --- a/contract/bridge/test/OneBTC.test.js +++ b/contract/bridge/test/OneBTC.test.js @@ -34,7 +34,7 @@ contract("issue/redeem test", accounts => { this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); // set OneBtc address to VaultRegistry - this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + this.VaultRegistry.updateOneBtcAddress(this.OneBtc.address); // set BTC/ONE exchange rate await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE diff --git a/contract/bridge/test/Redeem.test.js b/contract/bridge/test/Redeem.test.js index e78fc65..9e1a352 100644 --- a/contract/bridge/test/Redeem.test.js +++ b/contract/bridge/test/Redeem.test.js @@ -37,7 +37,7 @@ contract("Redeem unit test", (accounts) => { this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); // set OneBtc address to VaultRegistry - this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + this.VaultRegistry.updateOneBtcAddress(this.OneBtc.address); // set BTC/ONE exchange rate await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE diff --git a/contract/bridge/test/Replace.js b/contract/bridge/test/Replace.js index 32be47c..ff843e1 100644 --- a/contract/bridge/test/Replace.js +++ b/contract/bridge/test/Replace.js @@ -37,7 +37,7 @@ contract("Replace unit test", (accounts) => { this.OneBtc = await deployProxy(OneBtc, [this.RelayMock.address, this.ExchangeRateOracleWrapper.address, this.VaultRegistry.address]); // set OneBtc address to VaultRegistry - this.VaultRegistry.setOneBtcAddress(this.OneBtc.address); + this.VaultRegistry.updateOneBtcAddress(this.OneBtc.address); // set BTC/ONE exchange rate await this.ExchangeRateOracleWrapper.setExchangeRate(10); // 1 OneBtc = 10 ONE diff --git a/contract/bridge/test/VaultRegistry.js b/contract/bridge/test/VaultRegistry.js index c29ad5c..6e199a7 100644 --- a/contract/bridge/test/VaultRegistry.js +++ b/contract/bridge/test/VaultRegistry.js @@ -28,7 +28,7 @@ contract("VaultRegistry unit test", (accounts) => { this.VaultRegistry = await deployProxy(VaultRegistryWrapper, [this.ExchangeRateOracleWrapper.address]); // set OneBtc address with accounts[0] - await this.VaultRegistry.setOneBtcAddress(accounts[0]); + await this.VaultRegistry.updateOneBtcAddress(accounts[0]); this.vaultId = accounts[1]; this.issueRequester = accounts[2];