From 6cb0965bdbe04d577516dcc991808e033cff3c80 Mon Sep 17 00:00:00 2001 From: Luisfc68 <60527258+Luisfc68@users.noreply.github.com> Date: Tue, 20 Feb 2024 10:53:37 -0300 Subject: [PATCH 1/2] Stable test (#211) * Deployed to mainnet * Testnet Deploy * Added dao fee collector address * Fixed comments * Upgraded LBC to testnet * fix update script * LBC upgrade * fix test broken when adding productFeeAmount * enforce test to run on each environment branch * fix time dependant test to run in pipeline * Migrate to BtcUtils published library * fix unsafe integer calculation in test * update to avoid tx to the fee collector if productFee is 0 * contract size optimizations * separate gasFee from callFee * update integration tests * remove addresses from regtest config * testnet upgrade information * ci: add npm config * fix: install lib from new registry * fix: add linebreak in .npmrc * feat: update BtcUtils library --------- Co-authored-by: Guilherme Soares Co-authored-by: MaximStanciu8 <118008926+MaximStanciu8@users.noreply.github.com> --- .github/workflows/ci.yml | 16 +- .npmrc | 1 + .openzeppelin/BKP-rsk-testnet.json | 369 ---- .openzeppelin/unknown-30.json | 347 ++++ .openzeppelin/unknown-31.json | 2028 ++++++++++++++++++++- config.json | 45 +- contracts/BtcUtils.sol | 179 -- contracts/LiquidityBridgeContract.sol | 10 +- contracts/LiquidityBridgeContractV2.sol | 969 ++++++++++ contracts/Quotes.sol | 2 +- contracts/QuotesV2.sol | 152 ++ errorCodes.json | 3 +- integration-test/common.js | 2 +- integration-test/pegin.test.js | 40 +- integration-test/pegout.test.js | 32 +- integration-test/test.config.example.json | 3 +- migrations/2_deploy_contracts.js | 5 +- migrations/3_upgrade_contracts.js | 52 +- package-lock.json | 16 +- package.json | 5 +- scripts/forceImport.js | 7 + scripts/verifyV2Upgrade.js | 41 + test/basic.tests.js | 204 ++- test/miscellaneous.tests.js | 59 +- test/penalization.tests.js | 64 +- test/refund.tests.js | 60 +- test/utils/index.js | 10 +- truffle-config.js | 6 + 28 files changed, 3879 insertions(+), 848 deletions(-) create mode 100644 .npmrc delete mode 100644 .openzeppelin/BKP-rsk-testnet.json delete mode 100644 contracts/BtcUtils.sol create mode 100644 contracts/LiquidityBridgeContractV2.sol create mode 100644 contracts/QuotesV2.sol create mode 100644 scripts/forceImport.js create mode 100644 scripts/verifyV2Upgrade.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 755206a..fafe853 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,10 @@ name: CI # Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master ] + branches: [ master, Stable-Test, QA-Test ] pull_request: - branches: [ master ] + branches: [ master, Stable-Test, QA-Test ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -23,12 +22,15 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Use Node.js 16.x - uses: actions/setup-node@v1 + - name: Use Node.js 19.6.0 + uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: '19.6.0' + + - name: NPM Login + run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc - name: Install truffle run: npm install -g truffle diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..4a3fad8 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@rsksmart:registry=https://npm.pkg.github.com diff --git a/.openzeppelin/BKP-rsk-testnet.json b/.openzeppelin/BKP-rsk-testnet.json deleted file mode 100644 index b877454..0000000 --- a/.openzeppelin/BKP-rsk-testnet.json +++ /dev/null @@ -1,369 +0,0 @@ -{ - "manifestVersion": "3.2", - "admin": { - "address": "0x12ae82B7648d6700B0bca818409d8d6E5F6Bb837", - "txHash": "0x3984cba914a96d1d179dab78e63d249ac1bb0243ee4ec1ae79d86d08c697d1b9" - }, - "proxies": [ - { - "address": "0x4243CD5eb064ce9ABD4e9e2BbDD329E731cACB4B", - "txHash": "0x7e62657983daf16f5d24e18e1aa2798db28275f6117e62ab358f078da5b74bf9", - "kind": "transparent" - } - ], - "impls": { - "3aae3e6115988aceb9d1d0f62d41068dead18ffeb55fbae1acfa0e2626c7c2bc": { - "address": "0x890Ba613295AC96268d7ef028ED1192e4F29C5AA", - "txHash": "0x43105eb5486b4803e394584d10a66d0e230ad76622e3053364ecc5c480636079", - "layout": { - "solcVersion": "0.8.18", - "storage": [ - { - "contract": "Initializable", - "label": "_initialized", - "type": "t_uint8", - "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "contract": "Initializable", - "label": "_initializing", - "type": "t_bool", - "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "contract": "ContextUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)50_storage", - "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "contract": "OwnableUpgradeable", - "label": "_owner", - "type": "t_address", - "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "contract": "OwnableUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "contract": "ReentrancyGuardUpgradeable", - "label": "_status", - "type": "t_uint256", - "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "contract": "ReentrancyGuardUpgradeable", - "label": "__gap", - "type": "t_array(t_uint256)49_storage", - "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" - }, - { - "contract": "LiquidityBridgeContract", - "label": "bridge", - "type": "t_contract(Bridge)2338", - "src": "../project:/contracts/LiquidityBridgeContract.sol:99" - }, - { - "contract": "LiquidityBridgeContract", - "label": "balances", - "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:100" - }, - { - "contract": "LiquidityBridgeContract", - "label": "collateral", - "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:101" - }, - { - "contract": "LiquidityBridgeContract", - "label": "pegoutCollateral", - "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:102" - }, - { - "contract": "LiquidityBridgeContract", - "label": "liquidityProviders", - "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:103" - }, - { - "contract": "LiquidityBridgeContract", - "label": "callRegistry", - "type": "t_mapping(t_bytes32,t_struct(Registry)4084_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:104" - }, - { - "contract": "LiquidityBridgeContract", - "label": "resignationBlockNum", - "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:105" - }, - { - "contract": "LiquidityBridgeContract", - "label": "minCollateral", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:107" - }, - { - "contract": "LiquidityBridgeContract", - "label": "minPegIn", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:108" - }, - { - "contract": "LiquidityBridgeContract", - "label": "rewardP", - "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:110" - }, - { - "contract": "LiquidityBridgeContract", - "label": "resignDelayInBlocks", - "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:111" - }, - { - "contract": "LiquidityBridgeContract", - "label": "dust", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:112" - }, - { - "contract": "LiquidityBridgeContract", - "label": "maxQuoteValue", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:113" - }, - { - "contract": "LiquidityBridgeContract", - "label": "providerId", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:114" - }, - { - "contract": "LiquidityBridgeContract", - "label": "btcBlockTime", - "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:116" - }, - { - "contract": "LiquidityBridgeContract", - "label": "mainnet", - "type": "t_bool", - "src": "../project:/contracts/LiquidityBridgeContract.sol:117" - }, - { - "contract": "LiquidityBridgeContract", - "label": "processedQuotes", - "type": "t_mapping(t_bytes32,t_uint8)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:119" - }, - { - "contract": "LiquidityBridgeContract", - "label": "registeredPegoutQuotes", - "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)7072_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:120" - }, - { - "contract": "LiquidityBridgeContract", - "label": "pegoutRegistry", - "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:121" - } - ], - "types": { - "t_contract(Bridge)2338": { - "label": "contract Bridge" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)" - }, - "t_address": { - "label": "address" - }, - "t_uint256": { - "label": "uint256" - }, - "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)": { - "label": "mapping(uint256 => struct LiquidityBridgeContract.LiquidityProvider)" - }, - "t_struct(LiquidityProvider)4102_storage": { - "label": "struct LiquidityBridgeContract.LiquidityProvider", - "members": [ - { - "label": "id", - "type": "t_uint256" - }, - { - "label": "provider", - "type": "t_address" - }, - { - "label": "name", - "type": "t_string_storage" - }, - { - "label": "apiBaseUrl", - "type": "t_string_storage" - }, - { - "label": "status", - "type": "t_bool" - }, - { - "label": "providerType", - "type": "t_string_storage" - } - ] - }, - "t_string_storage": { - "label": "string" - }, - "t_bool": { - "label": "bool" - }, - "t_mapping(t_bytes32,t_struct(Registry)4084_storage)": { - "label": "mapping(bytes32 => struct LiquidityBridgeContract.Registry)" - }, - "t_bytes32": { - "label": "bytes32" - }, - "t_struct(Registry)4084_storage": { - "label": "struct LiquidityBridgeContract.Registry", - "members": [ - { - "label": "timestamp", - "type": "t_uint32" - }, - { - "label": "success", - "type": "t_bool" - } - ] - }, - "t_uint32": { - "label": "uint32" - }, - "t_mapping(t_bytes32,t_uint8)": { - "label": "mapping(bytes32 => uint8)" - }, - "t_uint8": { - "label": "uint8" - }, - "t_mapping(t_bytes32,t_struct(PegOutQuote)7072_storage)": { - "label": "mapping(bytes32 => struct Quotes.PegOutQuote)" - }, - "t_struct(PegOutQuote)7072_storage": { - "label": "struct Quotes.PegOutQuote", - "members": [ - { - "label": "lbcAddress", - "type": "t_address" - }, - { - "label": "lpRskAddress", - "type": "t_address" - }, - { - "label": "btcRefundAddress", - "type": "t_bytes_storage" - }, - { - "label": "rskRefundAddress", - "type": "t_address" - }, - { - "label": "lpBtcAddress", - "type": "t_bytes_storage" - }, - { - "label": "callFee", - "type": "t_uint256" - }, - { - "label": "penaltyFee", - "type": "t_uint256" - }, - { - "label": "nonce", - "type": "t_int64" - }, - { - "label": "deposityAddress", - "type": "t_bytes_storage" - }, - { - "label": "value", - "type": "t_uint256" - }, - { - "label": "agreementTimestamp", - "type": "t_uint32" - }, - { - "label": "depositDateLimit", - "type": "t_uint32" - }, - { - "label": "depositConfirmations", - "type": "t_uint16" - }, - { - "label": "transferConfirmations", - "type": "t_uint16" - }, - { - "label": "transferTime", - "type": "t_uint32" - }, - { - "label": "expireDate", - "type": "t_uint32" - }, - { - "label": "expireBlock", - "type": "t_uint32" - } - ] - }, - "t_bytes_storage": { - "label": "bytes" - }, - "t_int64": { - "label": "int64" - }, - "t_uint16": { - "label": "uint16" - }, - "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)": { - "label": "mapping(bytes32 => struct LiquidityBridgeContract.PegoutRecord)" - }, - "t_struct(PegoutRecord)4089_storage": { - "label": "struct LiquidityBridgeContract.PegoutRecord", - "members": [ - { - "label": "depositTimestamp", - "type": "t_uint256" - }, - { - "label": "completed", - "type": "t_bool" - } - ] - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]" - } - } - } - } - } -} diff --git a/.openzeppelin/unknown-30.json b/.openzeppelin/unknown-30.json index 5de7f1b..2bd4e24 100644 --- a/.openzeppelin/unknown-30.json +++ b/.openzeppelin/unknown-30.json @@ -358,6 +358,353 @@ } } } + }, + "d854f9996cc7e19e766f2bd137d0e2c65ed7519af1409188c5dda5c0900140c1": { + "address": "0xC0734F2d988cAdF5999dB0B460748617EAf2d3c8", + "txHash": "0xf35b7332356d686d567e474b7d729351ad99deb299914edf62e27120871c691e", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContract", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContract.sol:99" + }, + { + "contract": "LiquidityBridgeContract", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:100" + }, + { + "contract": "LiquidityBridgeContract", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:101" + }, + { + "contract": "LiquidityBridgeContract", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:102" + }, + { + "contract": "LiquidityBridgeContract", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:103" + }, + { + "contract": "LiquidityBridgeContract", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)4084_storage)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:104" + }, + { + "contract": "LiquidityBridgeContract", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:105" + }, + { + "contract": "LiquidityBridgeContract", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContract.sol:107" + }, + { + "contract": "LiquidityBridgeContract", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContract.sol:108" + }, + { + "contract": "LiquidityBridgeContract", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContract.sol:110" + }, + { + "contract": "LiquidityBridgeContract", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContract.sol:111" + }, + { + "contract": "LiquidityBridgeContract", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContract.sol:112" + }, + { + "contract": "LiquidityBridgeContract", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContract.sol:113" + }, + { + "contract": "LiquidityBridgeContract", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContract.sol:115" + }, + { + "contract": "LiquidityBridgeContract", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContract.sol:116" + }, + { + "contract": "LiquidityBridgeContract", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:118" + }, + { + "contract": "LiquidityBridgeContract", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)7060_storage)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:119" + }, + { + "contract": "LiquidityBridgeContract", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)", + "src": "../project:/contracts/LiquidityBridgeContract.sol:120" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContract.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)4102_storage": { + "label": "struct LiquidityBridgeContract.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)4084_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContract.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)4084_storage": { + "label": "struct LiquidityBridgeContract.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)7060_storage)": { + "label": "mapping(bytes32 => struct Quotes.PegOutQuote)" + }, + "t_struct(PegOutQuote)7060_storage": { + "label": "struct Quotes.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContract.PegoutRecord)" + }, + "t_struct(PegoutRecord)4089_storage": { + "label": "struct LiquidityBridgeContract.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } } } } diff --git a/.openzeppelin/unknown-31.json b/.openzeppelin/unknown-31.json index 0210ee2..dc8d66d 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -1,20 +1,17 @@ { "manifestVersion": "3.2", "admin": { - "address": "0xb77900eaC31c3E5CE26432807d5efFE9Ac06455f", - "txHash": "0x820ece21f6e73508041ac5c816d2d6cc65da196092680288f4464b5f0d13995d" + "address": "0x3e9552207f9aE1cC220BaCFf2BF5386d8842184E" }, "proxies": [ { - "address": "0x2A4C1373A52D65943b9043062052534F7724c356", - "txHash": "0x420730553de36e6fa322abe5d3dd73b108206d99a35869513eee48ad7612f77e", + "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", "kind": "transparent" } ], "impls": { - "da0a0546183d529a4b5c14527febccd2de6a0f30fcbe26cf815fb6b6ead1d595": { - "address": "0xbE1fa1653CB617A7Cad8DD3f0409D0801fEc5850", - "txHash": "0xecc3ec11f1afbf6c9ea26875fef6182e595c41df1bd2e7f762ebafebe6c0c685", + "8205e25ab44d03e2a4148f4cd4b0ad10fd6ccc6d71d0dc1b1f79ce25b1bce77a": { + "address": "0xe0712877a3AF76B0611a33872534e08c39c601d5", "layout": { "solcVersion": "0.8.18", "storage": [ @@ -65,109 +62,109 @@ "contract": "LiquidityBridgeContract", "label": "bridge", "type": "t_contract(Bridge)2338", - "src": "../project:/contracts/LiquidityBridgeContract.sol:99" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:99" }, { "contract": "LiquidityBridgeContract", "label": "balances", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:100" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:100" }, { "contract": "LiquidityBridgeContract", "label": "collateral", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:101" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:101" }, { "contract": "LiquidityBridgeContract", "label": "pegoutCollateral", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:102" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:102" }, { "contract": "LiquidityBridgeContract", "label": "liquidityProviders", - "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:103" + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:103" }, { "contract": "LiquidityBridgeContract", "label": "callRegistry", - "type": "t_mapping(t_bytes32,t_struct(Registry)4084_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:104" + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:104" }, { "contract": "LiquidityBridgeContract", "label": "resignationBlockNum", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:105" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:105" }, { "contract": "LiquidityBridgeContract", "label": "minCollateral", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:107" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:107" }, { "contract": "LiquidityBridgeContract", "label": "minPegIn", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:108" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:108" }, { "contract": "LiquidityBridgeContract", "label": "rewardP", "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:110" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:110" }, { "contract": "LiquidityBridgeContract", "label": "resignDelayInBlocks", "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:111" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:111" }, { "contract": "LiquidityBridgeContract", "label": "dust", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:112" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:112" }, { "contract": "LiquidityBridgeContract", "label": "providerId", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:113" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:113" }, { "contract": "LiquidityBridgeContract", "label": "btcBlockTime", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:115" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:115" }, { "contract": "LiquidityBridgeContract", "label": "mainnet", "type": "t_bool", - "src": "../project:/contracts/LiquidityBridgeContract.sol:116" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:116" }, { "contract": "LiquidityBridgeContract", "label": "processedQuotes", "type": "t_mapping(t_bytes32,t_uint8)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:118" + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:118" }, { "contract": "LiquidityBridgeContract", "label": "registeredPegoutQuotes", - "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)7060_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:119" + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)9927_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:119" }, { "contract": "LiquidityBridgeContract", "label": "pegoutRegistry", - "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:120" + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV1.sol:120" } ], "types": { @@ -183,10 +180,10 @@ "t_uint256": { "label": "uint256" }, - "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)": { + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { "label": "mapping(uint256 => struct LiquidityBridgeContract.LiquidityProvider)" }, - "t_struct(LiquidityProvider)4102_storage": { + "t_struct(LiquidityProvider)6996_storage": { "label": "struct LiquidityBridgeContract.LiquidityProvider", "members": [ { @@ -221,13 +218,13 @@ "t_bool": { "label": "bool" }, - "t_mapping(t_bytes32,t_struct(Registry)4084_storage)": { + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { "label": "mapping(bytes32 => struct LiquidityBridgeContract.Registry)" }, "t_bytes32": { "label": "bytes32" }, - "t_struct(Registry)4084_storage": { + "t_struct(Registry)6978_storage": { "label": "struct LiquidityBridgeContract.Registry", "members": [ { @@ -249,10 +246,10 @@ "t_uint8": { "label": "uint8" }, - "t_mapping(t_bytes32,t_struct(PegOutQuote)7060_storage)": { + "t_mapping(t_bytes32,t_struct(PegOutQuote)9927_storage)": { "label": "mapping(bytes32 => struct Quotes.PegOutQuote)" }, - "t_struct(PegOutQuote)7060_storage": { + "t_struct(PegOutQuote)9927_storage": { "label": "struct Quotes.PegOutQuote", "members": [ { @@ -334,10 +331,10 @@ "t_uint16": { "label": "uint16" }, - "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)": { + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { "label": "mapping(bytes32 => struct LiquidityBridgeContract.PegoutRecord)" }, - "t_struct(PegoutRecord)4089_storage": { + "t_struct(PegoutRecord)6983_storage": { "label": "struct LiquidityBridgeContract.PegoutRecord", "members": [ { @@ -359,9 +356,9 @@ } } }, - "90a13b12a1c79105378a05e67d9bb92fa14e6fa39baa8d1a8df8c8987ffc4c88": { - "address": "0xe09e975980C467b06B154Ec804FBe65022b58007", - "txHash": "0x0adac3c853f836ef2fc96a0c134d6e1efb366fbf8db3ef0c51f613ea6c64933d", + "917d7c175a1885a117991616f0910bede6a05862080c26f877cb2de04b52bfb7": { + "address": "0x4491F58ccA0355bfa073B3dBE6a818f26Df9397a", + "txHash": "0xf5464d79261002494b22ad435d5ea156e573aa1ae38c07017b2c7762cfbe7ae0", "layout": { "solcVersion": "0.8.18", "storage": [ @@ -409,112 +406,124 @@ "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "bridge", "type": "t_contract(Bridge)2338", - "src": "../project:/contracts/LiquidityBridgeContract.sol:99" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "balances", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:100" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "collateral", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:101" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "pegoutCollateral", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:102" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "liquidityProviders", - "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:103" + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "callRegistry", - "type": "t_mapping(t_bytes32,t_struct(Registry)4084_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:104" + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "resignationBlockNum", "type": "t_mapping(t_address,t_uint256)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:105" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "minCollateral", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:107" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "minPegIn", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:108" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "rewardP", "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:110" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "resignDelayInBlocks", "type": "t_uint32", - "src": "../project:/contracts/LiquidityBridgeContract.sol:111" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "dust", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:112" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "providerId", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:113" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "btcBlockTime", "type": "t_uint256", - "src": "../project:/contracts/LiquidityBridgeContract.sol:115" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "mainnet", "type": "t_bool", - "src": "../project:/contracts/LiquidityBridgeContract.sol:116" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "processedQuotes", "type": "t_mapping(t_bytes32,t_uint8)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:118" + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "registeredPegoutQuotes", - "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)7068_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:119" + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" }, { - "contract": "LiquidityBridgeContract", + "contract": "LiquidityBridgeContractV2", "label": "pegoutRegistry", - "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)", - "src": "../project:/contracts/LiquidityBridgeContract.sol:120" + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" } ], "types": { @@ -530,11 +539,11 @@ "t_uint256": { "label": "uint256" }, - "t_mapping(t_uint256,t_struct(LiquidityProvider)4102_storage)": { - "label": "mapping(uint256 => struct LiquidityBridgeContract.LiquidityProvider)" + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" }, - "t_struct(LiquidityProvider)4102_storage": { - "label": "struct LiquidityBridgeContract.LiquidityProvider", + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", "members": [ { "label": "id", @@ -568,14 +577,14 @@ "t_bool": { "label": "bool" }, - "t_mapping(t_bytes32,t_struct(Registry)4084_storage)": { - "label": "mapping(bytes32 => struct LiquidityBridgeContract.Registry)" + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" }, "t_bytes32": { "label": "bytes32" }, - "t_struct(Registry)4084_storage": { - "label": "struct LiquidityBridgeContract.Registry", + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", "members": [ { "label": "timestamp", @@ -596,11 +605,11 @@ "t_uint8": { "label": "uint8" }, - "t_mapping(t_bytes32,t_struct(PegOutQuote)7068_storage)": { - "label": "mapping(bytes32 => struct Quotes.PegOutQuote)" + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" }, - "t_struct(PegOutQuote)7068_storage": { - "label": "struct Quotes.PegOutQuote", + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", "members": [ { "label": "lbcAddress", @@ -669,6 +678,10 @@ { "label": "expireBlock", "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" } ] }, @@ -681,11 +694,1834 @@ "t_uint16": { "label": "uint16" }, - "t_mapping(t_bytes32,t_struct(PegoutRecord)4089_storage)": { - "label": "mapping(bytes32 => struct LiquidityBridgeContract.PegoutRecord)" + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" }, - "t_struct(PegoutRecord)4089_storage": { - "label": "struct LiquidityBridgeContract.PegoutRecord", + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "69cb605f452f7317cba55d1f5ca965e201610b091ff506817230044b030565e7": { + "address": "0xEdDedD91Dc6338245037674168326102D3387FdC", + "txHash": "0x394f042ca86de441c0a2db90c48ed9dd016637d40baf39773fbbff6de43d7829", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "667749700d32e89368f54bae55198f7807a194b576dc98eb820235cac1aecd4f": { + "address": "0x5388Cc8A11c37D49b58eeC2AA680bBAc0fb4610a", + "txHash": "0x6006f61872f5f8f90606ff7596ebea4fc61118111f3186602aec078a33f811c6", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "00d5830f416690d00e4ac9da12989c5cb8f36a02dffbc779592a7fe979ad5c7e": { + "address": "0x26cc222A1b857fac709CCe873CA144146B3D9e32", + "txHash": "0x6ab555891939fcfd13cd282d6a5f58098aa05d7583ab0de5bd1e63fdd874db17", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10186_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10186_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10186_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "6bc6c18b463e3742156f8eabaac29d351569a724fdf21c4b31eff492061b57dc": { + "address": "0xdd343Ef491B9520BB5efFC2A7154B133893840Ea", + "txHash": "0x95e933ab67993ce3a81e915b9f8337f64653fcc09fd9e806e409a34d90c2852c", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)3244", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:92" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:93" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:94" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:95" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)7092_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:96" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)7074_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:97" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:98" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)7079_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:115" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + } + ], + "types": { + "t_contract(Bridge)3244": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)7092_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)7092_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)7074_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)7074_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + }, + { + "label": "gasFee", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)7079_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)7079_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "f8f62f1ec9efc7ba291bd6f0ee0ef844d78d0b9b6dba0dd53427c7ca88562284": { + "address": "0x0085d1e64d7255cd3d8ae4F2aDe10b46bf8c6327", + "txHash": "0xf79faeb490165de708085c81c4d9df6db43e17af475a756bae7927f8e02f4c51", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)3244", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:92" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:93" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:94" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:95" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)7092_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:96" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)7074_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:97" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:98" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)7079_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:115" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + } + ], + "types": { + "t_contract(Bridge)3244": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)7092_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)7092_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)7074_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)7074_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + }, + { + "label": "gasFee", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)7079_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)7079_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", "members": [ { "label": "depositTimestamp", diff --git a/config.json b/config.json index 259ffc6..ed3ffd5 100644 --- a/config.json +++ b/config.json @@ -1,23 +1,18 @@ { "rskRegtest": { "Migrations": { - "address": "0x1Af2844A588759D0DE58abD568ADD96BB8B3B6D8", "deployed": false }, "SignatureValidator": { - "address": "0xCd5805d60Bbf9Afe69a394c2BDa10F6Dae2c39AF", "deployed": false }, "Quotes": { - "address": "0xdac5481925A298B95Bf5b54c35b68FC6fc2eF423", "deployed": false }, "BtcUtils": { - "address": "0xfD1dda8C3BC734Bc1C8e71F69F25BFBEe9cE9535", "deployed": false }, "LiquidityBridgeContract": { - "address": "0x987c1f13d417F7E04d852B44badc883E4E9782e1", "deployed": false } }, @@ -61,33 +56,59 @@ "deployed": true }, "BtcUtils": { - "address": "0x912c0CbF5C7CE892B9f165BeE288C4380FAa16C7", - "deployed": true + "address": "", + "deployed": false }, "LiquidityBridgeContract": { "address": "0xAA9cAf1e3967600578727F975F283446A3Da6612", "deployed": true + }, + "Migrations": { + "address": "0x22183DB087d1Fed621435d00aa9cac4B5CdC9aE1", + "deployed": true } }, "rskTestnet": { "Migrations": { - "address": "0xD35900b447AA1f18B42d26372660b7FE7d9aEB9C", + "address": "0x96B4493B3390F6556979b002daC6D0A091973beE", + "deployed": true + }, + "SignatureValidator": { + "address": "0xA66B2938b6cC837821cF4Be27F7016951047E03D", + "deployed": true + }, + "Quotes": { + "address": "0xeC7147081b0d8f703c3178e514525cC14Ff01f99", + "deployed": true + }, + "BtcUtils": { + "deployed": true, + "address": "0xF2C9FD07aEee0b422D5DB714ef793b1F7De48E25" + }, + "LiquidityBridgeContract": { + "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", + "deployed": true + } + }, + "ganache": { + "Migrations": { + "address": "0x21d86cA6fb5753d76ecBb313c9bc87f99306A57C", "deployed": true }, "SignatureValidator": { - "address": "0x6905711e16AFBC51d8cf2a714479D14FB3e03281", + "address": "0x97522395D02899beB44C1c11cFD4F3B1ff2d6Ad9", "deployed": true }, "Quotes": { - "address": "0x8384952Dc2A3e413800e9D4EE89d7383FA12Af10", + "address": "0x31fE2df3EccA461EaD8D1823e192E902dd3953eE", "deployed": true }, "BtcUtils": { - "address": "0x52b0108b38Ae80305F92FC461026e4c200ed673E", + "address": "0xbD171112775e70a44A1aBaabf3045aA8229Df7B5", "deployed": true }, "LiquidityBridgeContract": { - "address": "0x2A4C1373A52D65943b9043062052534F7724c356", + "address": "0x11D50Bcaff24425FC0b8E60a6818C5c455082bE7", "deployed": true } } diff --git a/contracts/BtcUtils.sol b/contracts/BtcUtils.sol deleted file mode 100644 index 044b87f..0000000 --- a/contracts/BtcUtils.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -/** - * @title BtcUtils - * @notice This library is based in this document - * https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format - */ -library BtcUtils { - uint8 private constant MAX_COMPACT_SIZE_LENGTH = 252; - uint8 private constant MAX_BYTES_USED_FOR_COMPACT_SIZE = 8; - uint8 private constant OUTPOINT_SIZE = 36; - uint8 private constant OUTPUT_VALUE_SIZE = 8; - uint8 private constant PUBKEY_HASH_SIZE = 20; - uint8 private constant PUBKEY_HASH_START = 3; - uint8 private constant CHECK_BYTES_FROM_HASH = 4; - - - struct TxRawOutput { - uint64 value; - bytes pkScript; - uint256 scriptSize; - uint256 totalSize; - } - - function parseCompactSizeInt(uint sizePosition, bytes memory array) private pure returns(uint64, uint16) { - require(array.length > sizePosition, "Size position can't be bigger than array"); - uint8 maxSize = uint8(array[sizePosition]); - if (maxSize == 0) { - return (0, 1); - } else if (maxSize <= MAX_COMPACT_SIZE_LENGTH) { - return (maxSize, 1); - } - - uint compactSizeBytes = 2 ** (maxSize - MAX_COMPACT_SIZE_LENGTH); - require(compactSizeBytes <= MAX_BYTES_USED_FOR_COMPACT_SIZE, "unsupported compact size length"); - - uint64 result = uint64(calculateLittleEndianFragment(sizePosition + 1, sizePosition + compactSizeBytes, array)); - return (result, uint16(compactSizeBytes) + 1); - } - - function getOutputs(bytes calldata rawTx) public pure returns (TxRawOutput[] memory) { - uint currentPosition = 4; - - if (rawTx[4] == 0x00 && rawTx[5] == 0x01) { // if its segwit, skip marker and flag - currentPosition = 6; - } - - (uint64 inputCount, uint16 inputCountSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += inputCountSize; - - uint64 scriptLarge; - uint16 scriptLargeSize; - for (uint64 i = 0; i < inputCount; i++) { - currentPosition += OUTPOINT_SIZE; - (scriptLarge, scriptLargeSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += scriptLarge + scriptLargeSize + 4; - } - - (uint64 outputCount, uint16 outputCountSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += outputCountSize; - - TxRawOutput[] memory result = new TxRawOutput[](outputCount); - for (uint i = 0; i < outputCount; i++) { - result[i] = extractRawOutput(currentPosition, rawTx); - currentPosition += result[i].totalSize; - } - return result; - } - - function calculateLittleEndianFragment(uint fragmentStart, uint fragmentEnd, bytes memory array) - private pure returns (uint) { - require( - fragmentStart < array.length && fragmentEnd < array.length, - "Range can't be bigger than array" - ); - uint result = 0; - for (uint i = fragmentStart; i <= fragmentEnd; i++) { - result += uint8(array[i]) * uint64(2 ** (8 * (i - (fragmentStart)))); - } - return result; - } - - function extractRawOutput(uint position, bytes memory rawTx) private pure returns (TxRawOutput memory) { - TxRawOutput memory result; - result.value = uint64(calculateLittleEndianFragment(position, position + OUTPUT_VALUE_SIZE, rawTx)); - position += OUTPUT_VALUE_SIZE; - - (uint64 scriptLength, uint16 scriptLengthSize) = parseCompactSizeInt(position, rawTx); - position += scriptLengthSize; - - bytes memory pkScript = new bytes(scriptLength); - for (uint64 i = 0; i < scriptLength; i++) { - pkScript[i] = rawTx[position + i]; - } - result.pkScript = pkScript; - result.scriptSize = scriptLength; - result.totalSize = OUTPUT_VALUE_SIZE + scriptLength + scriptLengthSize; - return result; - } - - function parsePayToAddressScript(bytes calldata outputScript, bool mainnet) public pure returns (bytes memory) { - require(outputScript.length == 25, "Script has not the required length"); - require( - outputScript[0] == 0x76 && // OP_DUP - outputScript[1] == 0xa9 && // OP_HASH160 - outputScript[2] == 0x14 && // pubKeyHashSize, should be always 14 (20B) - outputScript[23] == 0x88 && // OP_EQUALVERIFY - outputScript[24] == 0xac, // OP_CHECKSIG - "Script has not the required structure" - ); - - bytes memory destinationAddress = new bytes(PUBKEY_HASH_SIZE); - for(uint8 i = PUBKEY_HASH_START; i < PUBKEY_HASH_SIZE + PUBKEY_HASH_START; i++) { - destinationAddress[i - PUBKEY_HASH_START] = outputScript[i]; - } - - uint8 versionByte = mainnet? 0x00 : 0x6f; - bytes memory result = addVersionByte(bytes1(versionByte), destinationAddress); - - return result; - } - - function parseOpReturnOuput(bytes calldata outputScript) public pure returns (bytes memory) { - require(outputScript[0] == 0x6a, "Not OP_RETURN"); - require( - outputScript.length > 2 && outputScript.length < 85, - "Data out of bounds" - ); - - bytes memory message = new bytes(uint8(outputScript[1])); - for (uint8 i = 0; i < message.length; i++) { - // the addition of two is because the two first bytes correspond to - // the op_return opcode and the length of the message - message[i] = outputScript[i + 2]; - } - return message; - } - - function addVersionByte(bytes1 versionByte, bytes memory source) private pure returns (bytes memory) { - bytes memory dataWithVersion = new bytes(source.length + 1); - dataWithVersion[0] = versionByte; - - uint8 i; - for (i = 0; i < source.length; i++) { - dataWithVersion[i + 1] = source[i]; - } - - return dataWithVersion; - } - - function hashBtcTx(bytes calldata btcTx) public pure returns (bytes32) { - bytes memory doubleSha256 = abi.encodePacked(sha256(abi.encodePacked(sha256(btcTx)))); - bytes1 aux; - for (uint i = 0; i < 16; i++) { - aux = doubleSha256[i]; - doubleSha256[i] = doubleSha256[31 - i]; - doubleSha256[31 - i] = aux; - } - - bytes32 result; - assembly { - result := mload(add(doubleSha256, 32)) - } - return result; - } - - function validateP2SHAdress(bytes memory p2sh, bytes calldata script, bool mainnet) public pure returns (bool) { - return p2sh.length == 25 && keccak256(p2sh) == keccak256(getP2SHAddressFromScript(script, mainnet)); - } - - function getP2SHAddressFromScript(bytes calldata script, bool mainnet) public pure returns (bytes memory) { - bytes20 scriptHash = ripemd160(abi.encodePacked(sha256(script))); - uint8 versionByte = mainnet ? 0x5 : 0xc4; - bytes memory versionAndHash = bytes.concat(bytes1(versionByte), scriptHash); - bytes4 checksum = bytes4(sha256(abi.encodePacked(sha256(versionAndHash)))); - return bytes.concat(versionAndHash, checksum); - } -} \ No newline at end of file diff --git a/contracts/LiquidityBridgeContract.sol b/contracts/LiquidityBridgeContract.sol index 4e633bd..49bd660 100644 --- a/contracts/LiquidityBridgeContract.sol +++ b/contracts/LiquidityBridgeContract.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; import "./Bridge.sol"; import "./Quotes.sol"; import "./SignatureValidator.sol"; -import "./BtcUtils.sol"; +import "@rsksmart/btc-transaction-solidity-helper/contracts/BtcUtils.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; @@ -153,7 +153,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc uint _dustThreshold, uint _btcBlockTime, bool _mainnet - ) external initializer { + ) public initializer { require(_rewardPercentage <= 100, "LBC004"); require(_minimumCollateral >= 0.03 ether, "LBC072"); require(_resignDelayBlocks >= 60, "LBC073"); @@ -196,7 +196,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc return address(bridge); } - function getMinCollateral() external view returns (uint) { + function getMinCollateral() public view returns (uint) { return minCollateral; } @@ -749,7 +749,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc Quotes.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; require(quote.lbcAddress != address(0), "LBC042"); BtcUtils.TxRawOutput[] memory outputs = BtcUtils.getOutputs(btcTx); - bytes32 txQuoteHash = abi.decode(BtcUtils.parseOpReturnOuput(outputs[QUOTE_HASH_OUTPUT].pkScript), (bytes32)); + bytes32 txQuoteHash = abi.decode(BtcUtils.parseNullDataScript(outputs[QUOTE_HASH_OUTPUT].pkScript), (bytes32)); require(quoteHash == txQuoteHash, "LBC069"); require(msg.sender == quote.lpRskAddress, "LBC048"); require( @@ -762,7 +762,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc "LBC049" ); require(quote.value <= outputs[PAY_TO_ADDRESS_OUTPUT].value * (10**10), "LBC067"); // satoshi to wei - bytes memory btcTxDestination = BtcUtils.parsePayToAddressScript(outputs[PAY_TO_ADDRESS_OUTPUT] + bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT] .pkScript, mainnet); require(keccak256(quote.deposityAddress) == keccak256(btcTxDestination), "LBC068"); diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol new file mode 100644 index 0000000..2b11b72 --- /dev/null +++ b/contracts/LiquidityBridgeContractV2.sol @@ -0,0 +1,969 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; +pragma experimental ABIEncoderV2; + +import "./Bridge.sol"; +import "./QuotesV2.sol"; +import "./SignatureValidator.sol"; +import "@rsksmart/btc-transaction-solidity-helper/contracts/BtcUtils.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; + +/** + @title Contract that assists with the Flyover protocol + */ + +contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { + uint16 constant public MAX_CALL_GAS_COST = 35000; + uint16 constant public MAX_REFUND_GAS_LIMIT = 2300; + + uint8 constant public UNPROCESSED_QUOTE_CODE = 0; + uint8 constant public CALL_DONE_CODE = 1; + uint8 constant public PROCESSED_QUOTE_CODE = 2; + + int16 constant public BRIDGE_REFUNDED_USER_ERROR_CODE = - 100; + int16 constant public BRIDGE_REFUNDED_LP_ERROR_CODE = - 200; + int16 constant public BRIDGE_UNPROCESSABLE_TX_NOT_CONTRACT_ERROR_CODE = - 300; + int16 constant public BRIDGE_UNPROCESSABLE_TX_INVALID_SENDER_ERROR_CODE = - 301; + int16 constant public BRIDGE_UNPROCESSABLE_TX_ALREADY_PROCESSED_ERROR_CODE = - 302; + int16 constant public BRIDGE_UNPROCESSABLE_TX_VALIDATIONS_ERROR = - 303; + int16 constant public BRIDGE_UNPROCESSABLE_TX_VALUE_ZERO_ERROR = - 304; + int16 constant public BRIDGE_UNPROCESSABLE_TX_UTXO_AMOUNT_SENT_BELOW_MINIMUM_ERROR = + - 305; + int16 constant public BRIDGE_GENERIC_ERROR = - 900; + uint constant public PAY_TO_ADDRESS_OUTPUT = 0; + uint constant public QUOTE_HASH_OUTPUT = 1; + + struct Registry { + uint32 timestamp; + bool success; + } + + struct PegoutRecord { + uint256 depositTimestamp; + bool completed; + } + + struct LiquidityProvider { + uint id; + address provider; + string name; + string apiBaseUrl; + bool status; + string providerType; + } + + event Register(uint id, address indexed from, uint256 amount); + event CollateralIncrease(address from, uint256 amount); + event PegoutCollateralIncrease(address from, uint256 amount); + event Withdrawal(address from, uint256 amount); + event WithdrawCollateral(address from, uint256 amount); + event PegoutWithdrawCollateral(address from, uint256 amount); + event Resigned(address from); + event CallForUser( + address indexed from, + address indexed dest, + uint gasLimit, + uint value, + bytes data, + bool success, + bytes32 quoteHash + ); + event PegInRegistered(bytes32 indexed quoteHash, int256 transferredAmount); + event Penalized(address liquidityProvider, uint penalty, bytes32 quoteHash); + event BridgeCapExceeded(bytes32 quoteHash, int256 errorCode); + event BalanceIncrease(address dest, uint amount); + event BalanceDecrease(address dest, uint amount); + event Refund(address dest, uint amount, bool success, bytes32 quoteHash); + event PegOutRefunded(bytes32 indexed quoteHash); + event PegOutDeposit( + bytes32 indexed quoteHash, + address indexed sender, + uint256 amount, + uint256 timestamp + ); + event PegOutUserRefunded( + bytes32 indexed quoteHash, + uint256 value, + address userAddress + ); + event DaoFeeSent(bytes32 indexed quoteHash, uint256 amount); + + Bridge public bridge; + mapping(address => uint256) private balances; + mapping(address => uint256) private collateral; + mapping(address => uint256) private pegoutCollateral; + mapping(uint => LiquidityProvider) private liquidityProviders; + mapping(bytes32 => Registry) private callRegistry; + mapping(address => uint256) private resignationBlockNum; + + uint256 private minCollateral; + uint256 private minPegIn; + + uint32 private rewardP; + uint32 private resignDelayInBlocks; + uint private dust; + uint public providerId; + + uint private btcBlockTime; + bool private mainnet; + + mapping(bytes32 => uint8) private processedQuotes; + mapping(bytes32 => QuotesV2.PegOutQuote) private registeredPegoutQuotes; + mapping(bytes32 => PegoutRecord) private pegoutRegistry; + + uint256 public productFeePercentage; + address public daoFeeCollectorAddress; + + modifier onlyRegistered() { + require(isRegistered(msg.sender), "LBC001"); + _; + } + + modifier onlyRegisteredForPegout() { + require(isRegisteredForPegout(msg.sender), "LBC001"); + _; + } + + function initializeV2( + uint256 _productFeePercentage, + address _daoFeeCollectorAddress + ) public { + productFeePercentage = _productFeePercentage; + daoFeeCollectorAddress = _daoFeeCollectorAddress; + } + + modifier onlyOwnerAndProvider(uint _providerId) { + require( + msg.sender == owner() || + msg.sender == liquidityProviders[_providerId].provider, + "LBC005" + ); + _; + } + + function setProviderStatus( + uint _providerId, + bool status + ) external onlyOwnerAndProvider(_providerId) { + liquidityProviders[_providerId].status = status; + } + + receive() external payable { + require(msg.sender == address(bridge), "LBC007"); + } + + function getProviderIds() external view returns (uint) { + return providerId; + } + + function getBridgeAddress() external view returns (address) { + return address(bridge); + } + + function getMinCollateral() public view returns (uint) { + return minCollateral; + } + + function getMinPegIn() external view returns (uint) { + return minPegIn; + } + + function getRewardPercentage() external view returns (uint) { + return rewardP; + } + + function getResignDelayBlocks() external view returns (uint) { + return resignDelayInBlocks; + } + + function getDustThreshold() external view returns (uint) { + return dust; + } + + function getRegisteredPegOutQuote( + bytes32 quoteHash + ) external view returns (QuotesV2.PegOutQuote memory) { + return registeredPegoutQuotes[quoteHash]; + } + + function isPegOutQuoteCompleted(bytes32 quoteHash) external view returns (bool) { + return pegoutRegistry[quoteHash].completed; + } + + /** + @dev Checks whether a liquidity provider can deliver a pegin service + @return Whether the liquidity provider is registered and has enough locked collateral + */ + function isOperational(address addr) external view returns (bool) { + return isRegistered(addr) && collateral[addr] >= minCollateral; + } + + /** + @dev Checks whether a liquidity provider can deliver a pegout service + @return Whether the liquidity provider is registered and has enough locked collateral + */ + function isOperationalForPegout(address addr) external view returns (bool) { + return + isRegisteredForPegout(addr) && + pegoutCollateral[addr] >= minCollateral; + } + + /** + @dev Registers msg.sender as a liquidity provider with msg.value as collateral + */ + function register( + string memory _name, + string memory _apiBaseUrl, + bool _status, + string memory _providerType + ) external payable returns (uint) { + require(tx.origin == msg.sender, "LBC003"); + //require(collateral[msg.sender] == 0, "Already registered"); + require(bytes(_name).length > 0, "LBC010"); + require( + bytes(_apiBaseUrl).length > 0, + "LBC017" + ); + + // Check if _providerType is one of the valid strings + require( + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("pegin")) || + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("pegout")) || + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("both")), + "LBC018" + ); + + require(collateral[msg.sender] == 0 && pegoutCollateral[msg.sender] == 0, "LBC070"); + require( + resignationBlockNum[msg.sender] == 0, + "LBC009" + ); + + if (keccak256(abi.encodePacked(_providerType)) == keccak256(abi.encodePacked("pegin"))) { + require(msg.value >= minCollateral, "LBC008"); + collateral[msg.sender] = msg.value; + } else if (keccak256(abi.encodePacked(_providerType)) == keccak256(abi.encodePacked("pegout"))) { + require(msg.value >= minCollateral, "LBC008"); + pegoutCollateral[msg.sender] = msg.value; + } else { + require(msg.value >= minCollateral * 2, "LBC008"); + uint halfMsgValue = msg.value / 2; + collateral[msg.sender] = msg.value % 2 == 0 ? halfMsgValue : halfMsgValue + 1; + pegoutCollateral[msg.sender] = halfMsgValue; + } + + providerId++; + liquidityProviders[providerId] = LiquidityProvider({ + id: providerId, + provider: msg.sender, + name: _name, + apiBaseUrl: _apiBaseUrl, + status: _status, + providerType: _providerType + }); + emit Register(providerId, msg.sender, msg.value); + return (providerId); + } + + function getProviders( + uint[] memory providerIds + ) external view returns (LiquidityProvider[] memory) { + LiquidityProvider[] memory providersToReturn = new LiquidityProvider[]( + providerIds.length + ); + uint count = 0; + + for (uint i = 0; i < providerIds.length; i++) { + uint id = providerIds[i]; + if ( + (isRegistered(liquidityProviders[id].provider) || + isRegisteredForPegout(liquidityProviders[id].provider)) && + liquidityProviders[id].status + ) { + providersToReturn[count] = liquidityProviders[id]; + count++; + } + } + return providersToReturn; + } + + /** + @dev Increases the amount of collateral of the sender + */ + function addCollateral() external payable onlyRegistered { + collateral[msg.sender] += msg.value; + emit CollateralIncrease(msg.sender, msg.value); + } + + function addPegoutCollateral() external payable onlyRegisteredForPegout { + pegoutCollateral[msg.sender] += msg.value; + emit PegoutCollateralIncrease(msg.sender, msg.value); + } + + /** + @dev Increases the balance of the sender + */ + function deposit() external payable onlyRegistered { + increaseBalance(msg.sender, msg.value); + } + + /** + @dev Used to withdraw funds + @param amount The amount to withdraw + */ + function withdraw(uint256 amount) external { + require(balances[msg.sender] >= amount, "LBC019"); + balances[msg.sender] -= amount; + (bool success,) = msg.sender.call{value: amount}(""); + require(success, "LBC020"); + emit Withdrawal(msg.sender, amount); + } + + /** + @dev Used to withdraw the locked collateral + */ + function withdrawCollateral() external { + require(resignationBlockNum[msg.sender] > 0, "LBC021"); + require( + block.number - resignationBlockNum[msg.sender] >= + resignDelayInBlocks, + "LBC022" + ); + uint amount = collateral[msg.sender]; + collateral[msg.sender] = 0; + resignationBlockNum[msg.sender] = 0; + (bool success,) = msg.sender.call{value: amount}(""); + require(success, "LBC020"); + emit WithdrawCollateral(msg.sender, amount); + } + + function withdrawPegoutCollateral() external { + require(resignationBlockNum[msg.sender] > 0, "LBC021"); + require( + block.number - resignationBlockNum[msg.sender] >= + resignDelayInBlocks, + "LBC022" + ); + uint amount = pegoutCollateral[msg.sender]; + pegoutCollateral[msg.sender] = 0; + resignationBlockNum[msg.sender] = 0; + (bool success,) = msg.sender.call{value: amount}(""); + require(success, "LBC020"); + emit PegoutWithdrawCollateral(msg.sender, amount); + } + + /** + @dev Used to resign as a liquidity provider + */ + function resign() external onlyRegistered { + require(resignationBlockNum[msg.sender] == 0, "LBC023"); + resignationBlockNum[msg.sender] = block.number; + emit Resigned(msg.sender); + } + + /** + @dev Returns the amount of collateral of a liquidity provider + @param addr The address of the liquidity provider + @return The amount of locked collateral + */ + function getCollateral(address addr) external view returns (uint256) { + return collateral[addr]; + } + + function getPegoutCollateral(address addr) external view returns (uint256) { + return pegoutCollateral[addr]; + } + + /** + @dev Returns the amount of funds of a liquidity provider + @param addr The address of the liquidity provider + @return The balance of the liquidity provider + */ + function getBalance(address addr) external view returns (uint256) { + return balances[addr]; + } + + /** + @dev Performs a call on behalf of a user + @param quote The quote that identifies the service + @return Boolean indicating whether the call was successful + */ + function callForUser( + QuotesV2.PeginQuote memory quote + ) external payable onlyRegistered nonReentrant returns (bool) { + require( + msg.sender == quote.liquidityProviderRskAddress, + "LBC024" + ); + require( + balances[quote.liquidityProviderRskAddress] + msg.value >= + quote.value, + "LBC019" + ); + + bytes32 quoteHash = validateAndHashQuote(quote); + require( + processedQuotes[quoteHash] == UNPROCESSED_QUOTE_CODE, + "LBC025" + ); + + increaseBalance(quote.liquidityProviderRskAddress, msg.value); + + // This check ensures that the call cannot be performed with less gas than the agreed amount + require( + gasleft() >= quote.gasLimit + MAX_CALL_GAS_COST, + "LBC026" + ); + (bool success,) = quote.contractAddress.call{ + gas: quote.gasLimit, + value: quote.value + }(quote.data); + + require(block.timestamp <= type(uint32).max, "LBC027"); + callRegistry[quoteHash].timestamp = uint32(block.timestamp); + + if (success) { + callRegistry[quoteHash].success = true; + decreaseBalance(quote.liquidityProviderRskAddress, quote.value); + } + emit CallForUser( + msg.sender, + quote.contractAddress, + quote.gasLimit, + quote.value, + quote.data, + success, + quoteHash + ); + processedQuotes[quoteHash] = CALL_DONE_CODE; + return success; + } + + /** + @dev Registers a peg-in transaction with the bridge and pays to the involved parties + @param quote The quote of the service + @param signature The signature of the quote + @param btcRawTransaction The peg-in transaction + @param partialMerkleTree The merkle tree path that proves transaction inclusion + @param height The block that contains the peg-in transaction + @return The total peg-in amount received from the bridge contract or an error code + */ + function registerPegIn( + QuotesV2.PeginQuote memory quote, + bytes memory signature, + bytes memory btcRawTransaction, + bytes memory partialMerkleTree, + uint256 height + ) external nonReentrant returns (int256) { + bytes32 quoteHash = validateAndHashQuote(quote); + + // TODO: allow multiple registerPegIns for the same quote with different transactions + require( + processedQuotes[quoteHash] <= CALL_DONE_CODE, + "LBC028" + ); + require( + SignatureValidator.verify( + quote.liquidityProviderRskAddress, + quoteHash, + signature + ), + "LBC029" + ); + require(height < uint256(int256(type(int32).max)), "LBC030"); + + int256 transferredAmountOrErrorCode = registerBridge( + quote, + btcRawTransaction, + partialMerkleTree, + height, + quoteHash + ); + + require( + transferredAmountOrErrorCode != + BRIDGE_UNPROCESSABLE_TX_VALIDATIONS_ERROR, + "LBC031" + ); + require( + transferredAmountOrErrorCode != + BRIDGE_UNPROCESSABLE_TX_ALREADY_PROCESSED_ERROR_CODE, + "LBC032" + ); + require( + transferredAmountOrErrorCode != + BRIDGE_UNPROCESSABLE_TX_VALUE_ZERO_ERROR, + "LBC033" + ); + require( + transferredAmountOrErrorCode != + BRIDGE_UNPROCESSABLE_TX_UTXO_AMOUNT_SENT_BELOW_MINIMUM_ERROR, + "LBC034" + ); + require( + transferredAmountOrErrorCode != BRIDGE_GENERIC_ERROR, + "LBC035" + ); + require( + transferredAmountOrErrorCode > 0 || + transferredAmountOrErrorCode == BRIDGE_REFUNDED_LP_ERROR_CODE || + transferredAmountOrErrorCode == BRIDGE_REFUNDED_USER_ERROR_CODE, + "LBC036" + ); + + if ( + shouldPenalizeLP( + quote, + transferredAmountOrErrorCode, + callRegistry[quoteHash].timestamp, + height + ) + ) { + uint penalizationAmount = min( + quote.penaltyFee, + collateral[quote.liquidityProviderRskAddress] + ); // prevent underflow when collateral is less than penalty fee. + collateral[quote.liquidityProviderRskAddress] -= penalizationAmount; + emit Penalized( + quote.liquidityProviderRskAddress, + penalizationAmount, + quoteHash + ); + + // pay reward to sender + uint256 punisherReward = (penalizationAmount * rewardP) / 100; + increaseBalance(msg.sender, punisherReward); + } + + if ( + transferredAmountOrErrorCode == BRIDGE_REFUNDED_LP_ERROR_CODE || + transferredAmountOrErrorCode == BRIDGE_REFUNDED_USER_ERROR_CODE + ) { + // Bridge cap exceeded + processedQuotes[quoteHash] = PROCESSED_QUOTE_CODE; + delete callRegistry[quoteHash]; + emit BridgeCapExceeded(quoteHash, transferredAmountOrErrorCode); + return transferredAmountOrErrorCode; + } + + // the amount is safely assumed positive because it's already been + // validated in lines 287/298 there's no (negative) error code being returned by the bridge. + uint transferredAmount = uint(transferredAmountOrErrorCode); + + QuotesV2.checkAgreedAmount(quote, transferredAmount); + + if (callRegistry[quoteHash].timestamp > 0) { + uint refundAmount; + + if (callRegistry[quoteHash].success) { + refundAmount = min( + transferredAmount, + quote.value + quote.callFee + quote.gasFee + ); + } else { + refundAmount = min(transferredAmount, quote.callFee + quote.gasFee); + } + increaseBalance(quote.liquidityProviderRskAddress, refundAmount); + + uint remainingAmount = transferredAmount - refundAmount; + payToFeeCollector(quote.productFeeAmount, quoteHash); + + if (remainingAmount > dust) { + // refund rskRefundAddress, if remaining amount greater than dust + (bool success,) = quote.rskRefundAddress.call{ + gas: MAX_REFUND_GAS_LIMIT, + value: remainingAmount + }(""); + emit Refund( + quote.rskRefundAddress, + remainingAmount, + success, + quoteHash + ); + + if (!success) { + // transfer funds to LP instead, if for some reason transfer to rskRefundAddress was unsuccessful + increaseBalance( + quote.liquidityProviderRskAddress, + remainingAmount + ); + } + } + } else { + uint refundAmount = transferredAmount; + + if (quote.callOnRegister && refundAmount >= quote.value) { + (bool callSuccess,) = quote.contractAddress.call{ + gas: quote.gasLimit, + value: quote.value + }(quote.data); + emit CallForUser( + msg.sender, + quote.contractAddress, + quote.gasLimit, + quote.value, + quote.data, + callSuccess, + quoteHash + ); + + if (callSuccess) { + refundAmount -= quote.value; + } + } + if (refundAmount > dust) { + // refund rskRefundAddress, if refund amount greater than dust + (bool success,) = quote.rskRefundAddress.call{ + gas: MAX_REFUND_GAS_LIMIT, + value: refundAmount + }(""); + emit Refund( + quote.rskRefundAddress, + refundAmount, + success, + quoteHash + ); + } + } + processedQuotes[quoteHash] = PROCESSED_QUOTE_CODE; + delete callRegistry[quoteHash]; + emit PegInRegistered(quoteHash, transferredAmountOrErrorCode); + return transferredAmountOrErrorCode; + } + + function depositPegout( // TODO convert to calldata when contract size issues are fixed + QuotesV2.PegOutQuote memory quote, + bytes memory signature + ) external payable { + require(isRegisteredForPegout(quote.lpRskAddress), "LBC037"); + require(quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee <= msg.value, "LBC063"); + require(block.timestamp <= quote.depositDateLimit, "LBC065"); + require(block.timestamp <= quote.expireDate, "LBC046"); + require(block.number <= quote.expireBlock, "LBC047"); + bytes32 quoteHash = hashPegoutQuote(quote); + require( + SignatureValidator.verify(quote.lpRskAddress, quoteHash, signature), + "LBC029" + ); + + QuotesV2.PegOutQuote storage registeredQuote = registeredPegoutQuotes[quoteHash]; + + require(pegoutRegistry[quoteHash].completed == false, "LBC064"); + require(registeredQuote.lbcAddress == address(0), "LBC028"); + registeredPegoutQuotes[quoteHash] = quote; + pegoutRegistry[quoteHash].depositTimestamp = block.timestamp; + emit PegOutDeposit(quoteHash, msg.sender, msg.value, block.timestamp); + } + + function refundUserPegOut( + bytes32 quoteHash + ) external nonReentrant { + QuotesV2.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; + + require(quote.lbcAddress != address(0), "LBC042"); + require( + block.timestamp > quote.expireDate && + block.number > quote.expireBlock, + "LBC041" + ); + + uint valueToTransfer = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee; + address addressToTransfer = quote.rskRefundAddress; + + uint penalty = min(quote.penaltyFee, pegoutCollateral[quote.lpRskAddress]); + pegoutCollateral[quote.lpRskAddress] -= penalty; + + emit Penalized(quote.lpRskAddress, penalty, quoteHash); + emit PegOutUserRefunded( + quoteHash, + valueToTransfer, + quote.rskRefundAddress + ); + + delete registeredPegoutQuotes[quoteHash]; + pegoutRegistry[quoteHash].completed = true; + + (bool sent,) = addressToTransfer.call{value: valueToTransfer}(""); + require(sent, "LBC044"); + } + + function refundPegOut( + bytes32 quoteHash, + bytes memory btcTx, + bytes32 btcBlockHeaderHash, + uint256 partialMerkleTree, + bytes32[] memory merkleBranchHashes + ) external nonReentrant onlyRegisteredForPegout { + require(pegoutRegistry[quoteHash].completed == false, "LBC064"); + QuotesV2.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; + require(quote.lbcAddress != address(0), "LBC042"); + BtcUtils.TxRawOutput[] memory outputs = BtcUtils.getOutputs(btcTx); + bytes memory scriptContent = BtcUtils.parseNullDataScript(outputs[QUOTE_HASH_OUTPUT].pkScript); + // shift the array to remove the first byte (the size) + for (uint8 i = 0 ; i < scriptContent.length - 1; i++) { + scriptContent[i] = scriptContent[i + 1]; + } + bytes32 txQuoteHash = abi.decode(scriptContent, (bytes32)); + require(quoteHash == txQuoteHash, "LBC069"); + require(msg.sender == quote.lpRskAddress, "LBC048"); + require( + bridge.getBtcTransactionConfirmations( + BtcUtils.hashBtcTx(btcTx), + btcBlockHeaderHash, + partialMerkleTree, + merkleBranchHashes + ) >= int(uint256(quote.transferConfirmations)), + "LBC049" + ); + require(quote.value <= outputs[PAY_TO_ADDRESS_OUTPUT].value * (10**10), "LBC067"); // satoshi to wei + bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT].pkScript, mainnet); + require(keccak256(quote.deposityAddress) == keccak256(btcTxDestination), "LBC068"); + + if ( + shouldPenalizePegOutLP( + quote, + txQuoteHash, + btcBlockHeaderHash + ) + ) { + uint penalty = min( + quote.penaltyFee, + pegoutCollateral[quote.lpRskAddress] + ); + pegoutCollateral[quote.lpRskAddress] -= penalty; + emit Penalized(quote.lpRskAddress, penalty, txQuoteHash); + } + + (bool sent,) = quote.lpRskAddress.call{ + value: quote.value + quote.callFee + }(""); + require(sent, "LBC050"); + + payToFeeCollector(quote.productFeeAmount, quoteHash); + + delete registeredPegoutQuotes[txQuoteHash]; + pegoutRegistry[txQuoteHash].completed = true; + emit PegOutRefunded(txQuoteHash); + } + + function validatePeginDepositAddress( + QuotesV2.PeginQuote memory quote, + bytes memory depositAddress + ) external view returns (bool) { + bytes32 derivationValue = keccak256( + bytes.concat( + hashQuote(quote), + quote.btcRefundAddress, + bytes20(quote.lbcAddress), + quote.liquidityProviderBtcAddress + ) + ); + bytes memory flyoverRedeemScript = bytes.concat( + hex"20", + derivationValue, + hex"75", + bridge.getActivePowpegRedeemScript() + ); + return BtcUtils.validateP2SHAdress(depositAddress, flyoverRedeemScript, mainnet); + } + + /** + @dev Calculates hash of a quote. Note: besides calculation this function also validates the quote. + @param quote The quote of the service + @return The hash of a quote + */ + function hashQuote(QuotesV2.PeginQuote memory quote) public view returns (bytes32) { + return validateAndHashQuote(quote); + } + + function hashPegoutQuote( + QuotesV2.PegOutQuote memory quote + ) public view returns (bytes32) { + return validateAndHashPegOutQuote(quote); + } + + function validateAndHashQuote( + QuotesV2.PeginQuote memory quote + ) private view returns (bytes32) { + require(address(this) == quote.lbcAddress, "LBC051"); + require( + address(bridge) != quote.contractAddress, + "LBC052" + ); + require( + quote.btcRefundAddress.length == 21 || + quote.btcRefundAddress.length == 33, + "LBC053" + ); + require( + quote.liquidityProviderBtcAddress.length == 21, + "LBC054" + ); + require( + quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee >= minPegIn, + "LBC055" + ); + require( + type(uint32).max >= uint64(quote.agreementTimestamp) + uint64(quote.timeForDeposit), + "LBC071" + ); + + return keccak256(QuotesV2.encodeQuote(quote)); + } + + function validateAndHashPegOutQuote( + QuotesV2.PegOutQuote memory quote + ) private view returns (bytes32) { + require(address(this) == quote.lbcAddress, "LBC056"); + + return keccak256(QuotesV2.encodePegOutQuote(quote)); + } + + function min(uint a, uint b) private pure returns (uint) { + return a < b ? a : b; + } + + // IMPORTANT: These methods should remain private at all costs + function increaseBalance(address dest, uint amount) private { + balances[dest] += amount; + emit BalanceIncrease(dest, amount); + } + + function decreaseBalance(address dest, uint amount) private { + balances[dest] -= amount; + emit BalanceDecrease(dest, amount); + } + + /** + @dev Checks if a liquidity provider is registered + @param addr The address of the liquidity provider + @return Boolean indicating whether the liquidity provider is registered + */ + function isRegistered(address addr) private view returns (bool) { + return collateral[addr] > 0 && resignationBlockNum[addr] == 0; + } + + function isRegisteredForPegout(address addr) private view returns (bool) { + return pegoutCollateral[addr] > 0 && resignationBlockNum[addr] == 0; + } + + /** + @dev Registers a transaction with the bridge contract + @param quote The quote of the service + @param btcRawTransaction The peg-in transaction + @param partialMerkleTree The merkle tree path that proves transaction inclusion + @param height The block that contains the transaction + @return The total peg-in amount received from the bridge contract or an error code + */ + function registerBridge( + QuotesV2.PeginQuote memory quote, + bytes memory btcRawTransaction, + bytes memory partialMerkleTree, + uint256 height, + bytes32 derivationHash + ) private returns (int256) { + return + bridge.registerFastBridgeBtcTransaction( + btcRawTransaction, + height, + partialMerkleTree, + derivationHash, + quote.btcRefundAddress, + payable(this), + quote.liquidityProviderBtcAddress, + callRegistry[derivationHash].timestamp > 0 && callRegistry[derivationHash].success + ); + } + + /** + @dev Checks if a liquidity provider should be penalized + @param quote The quote of the service + @param amount The transferred amount or an error code + @param callTimestamp The time that the liquidity provider called callForUser + @param height The block height where the peg-in transaction is included + @return Boolean indicating whether the penalty applies + */ + function shouldPenalizeLP( + QuotesV2.PeginQuote memory quote, + int256 amount, + uint256 callTimestamp, + uint256 height + ) private view returns (bool) { + // do not penalize if deposit amount is insufficient + if (amount > 0 && uint256(amount) < quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee) { + return false; + } + + bytes memory firstConfirmationHeader = bridge + .getBtcBlockchainBlockHeaderByHeight(height); + require(firstConfirmationHeader.length > 0, "Invalid block height"); + + uint256 firstConfirmationTimestamp = BtcUtils.getBtcBlockTimestamp( + firstConfirmationHeader + ); + + // do not penalize if deposit was not made on time + // prevent overflow when collateral is less than penalty fee. + uint timeLimit = quote.agreementTimestamp + quote.timeForDeposit; + if (firstConfirmationTimestamp > timeLimit) { + return false; + } + + // penalize if call was not made + if (callTimestamp == 0) { + return true; + } + + bytes memory nConfirmationsHeader = bridge + .getBtcBlockchainBlockHeaderByHeight( + height + quote.depositConfirmations - 1 + ); + require(nConfirmationsHeader.length > 0, "LBC058"); + + uint256 nConfirmationsTimestamp = BtcUtils.getBtcBlockTimestamp( + nConfirmationsHeader + ); + + // penalize if the call was not made on time + if (callTimestamp > nConfirmationsTimestamp + quote.callTime) { + return true; + } + return false; + } + + function shouldPenalizePegOutLP( + QuotesV2.PegOutQuote memory quote, + bytes32 quoteHash, + bytes32 blockHash + ) private view returns (bool) { + bytes memory firstConfirmationHeader = bridge.getBtcBlockchainBlockHeaderByHash(blockHash); + require(firstConfirmationHeader.length > 0, "LBC059"); + + uint256 firstConfirmationTimestamp = BtcUtils.getBtcBlockTimestamp(firstConfirmationHeader); + + // penalize if the transfer was not made on time + if (firstConfirmationTimestamp > pegoutRegistry[quoteHash].depositTimestamp + + quote.transferTime + btcBlockTime) { + return true; + } + + // penalize if LP is refunding after expiration + if (block.timestamp > quote.expireDate || block.number > quote.expireBlock) { + return true; + } + + return false; + } + + function payToFeeCollector(uint amount, bytes32 quoteHash) private { + if (amount > 0) { + (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{value: amount}(""); + require(daoSuccess, "LBC074"); + emit DaoFeeSent(quoteHash, amount); + } + } +} diff --git a/contracts/Quotes.sol b/contracts/Quotes.sol index 0d73f47..d34e9d7 100644 --- a/contracts/Quotes.sol +++ b/contracts/Quotes.sol @@ -137,5 +137,5 @@ library Quotes { "LBC057" ); } - + } \ No newline at end of file diff --git a/contracts/QuotesV2.sol b/contracts/QuotesV2.sol new file mode 100644 index 0000000..2ca6db5 --- /dev/null +++ b/contracts/QuotesV2.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +library QuotesV2 { + struct PeginQuote { + bytes20 fedBtcAddress; + address lbcAddress; + address liquidityProviderRskAddress; + bytes btcRefundAddress; + address payable rskRefundAddress; + bytes liquidityProviderBtcAddress; + uint256 callFee; + uint256 penaltyFee; + address contractAddress; + bytes data; + uint32 gasLimit; + int64 nonce; + uint256 value; + uint32 agreementTimestamp; + uint32 timeForDeposit; + uint32 callTime; + uint16 depositConfirmations; + bool callOnRegister; + uint256 productFeeAmount; + uint256 gasFee; + } + + struct PegOutQuote { + address lbcAddress; + address lpRskAddress; + bytes btcRefundAddress; + address rskRefundAddress; + bytes lpBtcAddress; + uint256 callFee; + uint256 penaltyFee; + int64 nonce; + bytes deposityAddress; + uint256 value; + uint32 agreementTimestamp; + uint32 depositDateLimit; + uint16 depositConfirmations; + uint16 transferConfirmations; + uint32 transferTime; + uint32 expireDate; + uint32 expireBlock; + uint256 productFeeAmount; + uint256 gasFee; + } + + function encodeQuote( + PeginQuote memory quote + ) external pure returns (bytes memory) { + // Encode in two parts because abi.encode cannot take more than 12 parameters due to stack depth limits. + return abi.encode(encodePart1(quote), encodePart2(quote)); + } + + function encodePegOutQuote( + PegOutQuote memory quote + ) external pure returns (bytes memory) { + // Encode in two parts because abi.encode cannot take more than 12 parameters due to stack depth limits. + return abi.encode(encodePegOutPart1(quote), encodePegOutPart2(quote)); + } + + function encodePart1( + PeginQuote memory quote + ) private pure returns (bytes memory) { + return + abi.encode( + quote.fedBtcAddress, + quote.lbcAddress, + quote.liquidityProviderRskAddress, + quote.btcRefundAddress, + quote.rskRefundAddress, + quote.liquidityProviderBtcAddress, + quote.callFee, + quote.penaltyFee, + quote.contractAddress + ); + } + + function encodePart2( + PeginQuote memory quote + ) private pure returns (bytes memory) { + return + abi.encode( + quote.data, + quote.gasLimit, + quote.nonce, + quote.value, + quote.agreementTimestamp, + quote.timeForDeposit, + quote.callTime, + quote.depositConfirmations, + quote.callOnRegister, + quote.productFeeAmount, + quote.gasFee + ); + } + + function encodePegOutPart1( + PegOutQuote memory quote + ) private pure returns (bytes memory) { + return + abi.encode( + quote.lbcAddress, + quote.lpRskAddress, + quote.btcRefundAddress, + quote.rskRefundAddress, + quote.lpBtcAddress, + quote.callFee, + quote.penaltyFee, + quote.nonce, + quote.deposityAddress + ); + } + + function encodePegOutPart2( + PegOutQuote memory quote + ) private pure returns (bytes memory) { + return + abi.encode( + quote.value, + quote.agreementTimestamp, + quote.depositDateLimit, + quote.depositConfirmations, + quote.transferConfirmations, + quote.transferTime, + quote.expireDate, + quote.expireBlock, + quote.productFeeAmount, + quote.gasFee + ); + } + + function checkAgreedAmount( + PeginQuote memory quote, + uint transferredAmount + ) external pure { + uint agreedAmount = 0; + agreedAmount = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee; + + + uint delta = agreedAmount / 10000; + // transferred amount should not be lower than (agreed amount - delta), + // where delta is intended to tackle rounding problems + require( + transferredAmount >= agreedAmount - delta, + "LBC057" + ); + } + +} \ No newline at end of file diff --git a/errorCodes.json b/errorCodes.json index 8caf909..8d94300 100644 --- a/errorCodes.json +++ b/errorCodes.json @@ -66,5 +66,6 @@ "LBC070": "Liquidity provider already registered", "LBC071": "Intentional overflow on quote values", "LBC072": "Minimum collateral for registration can't be lower than 0.6 RBTC", - "LBC073": "Resign delay blocks lower than minimal" + "LBC073": "Resign delay blocks lower than minimal", + "LBC074": "Error sending fee to DAO" } diff --git a/integration-test/common.js b/integration-test/common.js index 47d8ac7..742f3f2 100644 --- a/integration-test/common.js +++ b/integration-test/common.js @@ -67,7 +67,7 @@ async function loadConfig() { return JSON.parse(buffer.toString()) } async function sendBtc({ toAddress, amountInBtc, rpc, data }) { - const outputs = [ { [toAddress]: amountInBtc } ] + const outputs = [ { [toAddress]: amountInBtc.toString() } ] const fundOptions = { fee_rate: 25 } if (data) { outputs.push({ data }) diff --git a/integration-test/pegin.test.js b/integration-test/pegin.test.js index 034b775..5563189 100644 --- a/integration-test/pegin.test.js +++ b/integration-test/pegin.test.js @@ -1,6 +1,6 @@ const { expect } = require('chai') -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract") +const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContractV2") const BtcUtils = artifacts.require("BtcUtils") const bs58check = require('bs58check') const bs58 = require('bs58') @@ -29,8 +29,10 @@ describe('Flyover pegin process should', () => { let lpAccount let interval + let config + before(async () => { - const config = await loadConfig() + config = await loadConfig() interval = config.pollingIntervalInSeconds * 1000 lpAccount = web3.eth.accounts.privateKeyToAccount(config.lpPrivateKey) const userAccount = web3.eth.accounts.privateKeyToAccount(config.userPrivateKey) @@ -47,7 +49,8 @@ describe('Flyover pegin process should', () => { const timestamp = Math.floor(Date.now() / 1000) const transferTime = 1800 const gasLimit = await web3.eth.estimateGas({ to: userAddress, data: '0x' }) - const gasCost = await web3.eth.getGasPrice().then(price => price * gasLimit) + const gasPrice = await web3.eth.getGasPrice() + const cfuGasCost = gasPrice * gasLimit const fedAddress = await web3.eth.call({ to: '0x0000000000000000000000000000000001000006', data: web3.eth.abi.encodeFunctionSignature('getFederationAddress()') @@ -58,6 +61,13 @@ describe('Flyover pegin process should', () => { }) const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + const productFeePercentage = await lbc.methods.productFeePercentage().call() + const value = BigInt(600000000000000000) // 0.6 eth + const productFee = (BigInt(productFeePercentage) * value) / BigInt(100) + const daoAddress = await lbc.methods.daoFeeCollectorAddress().call() + const daoGas = await web3.eth.estimateGas({ to: daoAddress, value: productFee.toString() }) + const daoGasCost = gasPrice * daoGas + quote = { fedBtcAddress: fedAddress, lbcAddress: config.lbcAddress, @@ -65,18 +75,20 @@ describe('Flyover pegin process should', () => { btcRefundAddress: userBtcAddress, rskRefundAddress: userAddress, liquidityProviderBtcAddress: lpBtcAddress, - callFee: BigInt(100000000000000 + gasCost), // fee is 0.0001 eth + callFee: BigInt(10000000000000000), // fee is 0.01 eth penaltyFee: 1000000, contractAddress: userAddress, data: '0x', gasLimit: gasLimit, nonce: nonce, - value: BigInt(6000000000000000), // 0.006 eth + value: value, agreementTimestamp: timestamp, timeForDeposit: transferTime, callTime: transferTime * 2, depositConfirmations: 1, - callOnRegister: false + callOnRegister: false, + productFeeAmount: productFee, + gasFee: cfuGasCost + daoGasCost } @@ -98,10 +110,18 @@ describe('Flyover pegin process should', () => { it('execute registerPegIn', async () => { const derivationAddress = await getDervivationAddress({ quote, quoteHash, btcUtilsInstance: btcUtils, lbc}) - - const amountInSatoshi = (quote.value + quote.callFee) / BigInt(10**10) - const amountInBtc = Number(amountInSatoshi) / 10**8 - + const total = web3.utils.toBN(quote.value.toString()) + .add(web3.utils.toBN(quote.callFee.toString())) + .add(web3.utils.toBN(quote.gasFee.toString())) + .add(web3.utils.toBN(quote.productFeeAmount.toString())); + const amountInBtc = web3.utils.fromWei(total, 'ether') + + if (config.btc.walletPassphrase) { + await bitcoinRpc('walletpassphrase', { + passphrase: config.btc.walletPassphrase, + timeout: 60 + }) + } const txHash = await sendBtc({ rpc: bitcoinRpc, amountInBtc, toAddress: derivationAddress }) const tx = await waitForBtcTransaction({ rpc: bitcoinRpc, hash: txHash, confirmations: quote.depositConfirmations, interval }) diff --git a/integration-test/pegout.test.js b/integration-test/pegout.test.js index 54bc16c..5594f95 100644 --- a/integration-test/pegout.test.js +++ b/integration-test/pegout.test.js @@ -1,6 +1,6 @@ const { expect } = require('chai') -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract") +const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContractV2") const bs58check = require('bs58check') const pmtBuilder = require("@rsksmart/pmt-builder") @@ -29,8 +29,10 @@ describe('Flyover pegout process should', () => { let interval let userBtcEncodedAddress + let config + before(async () => { - const config = await loadConfig() + config = await loadConfig() interval = config.pollingIntervalInSeconds * 1000 lpAccount = web3.eth.accounts.privateKeyToAccount(config.lpPrivateKey) userAccount = web3.eth.accounts.privateKeyToAccount(config.userPrivateKey) @@ -46,22 +48,28 @@ describe('Flyover pegout process should', () => { const timestamp = Math.floor(Date.now() / 1000) const transferTime = 1800, expireDate = 3600 - const gasLimit = await web3.eth.estimateGas({ to: userAddress, data: '0x' }) - const gasCost = await web3.eth.getGasPrice().then(price => price * gasLimit) const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) const height = await web3.eth.getBlock("latest").then(block => block.number); + const productFeePercentage = await lbc.methods.productFeePercentage().call() + const value = BigInt(600000000000000000) // 0.6 eth + const productFee = (BigInt(productFeePercentage) * value) / BigInt(100) + const daoAddress = await lbc.methods.daoFeeCollectorAddress().call() + const daoGas = await web3.eth.estimateGas({ to: daoAddress, value: productFee.toString() }) + const gasPrice = await web3.eth.getGasPrice() + const daoGasCost = gasPrice * daoGas + const btcNetworkFee = 0.00006700 * 10**18 + quote = { lbcAddress: config.lbcAddress, lpRskAddress: lpAddress, btcRefundAddress: userBtcAddress, rskRefundAddress: userAddress, lpBtcAddress: lpBtcAddress, - callFee: BigInt(100000000000000 + gasCost), // fee is 0.0001 eth + callFee: BigInt(100000000000000), // fee is 0.0001 eth penaltyFee: 1000000, nonce: nonce, deposityAddress: userBtcAddress, - gasLimit: gasLimit, value: BigInt(6000000000000000), // 0.006 eth agreementTimestamp: timestamp, depositDateLimit: timestamp + transferTime, @@ -69,7 +77,9 @@ describe('Flyover pegout process should', () => { transferConfirmations: 1, transferTime: transferTime, expireDate: timestamp + expireDate, - expireBlock: height + 50 + expireBlock: height + 50, + productFeeAmount: productFee, + gasFee: daoGasCost + btcNetworkFee } quoteHash = await lbc.methods.hashPegoutQuote(quote).call() @@ -79,7 +89,7 @@ describe('Flyover pegout process should', () => { it('execute depositPegout', async () => { const receipt = await sendFromAccount({ account: userAccount, - value: quote.callFee + quote.value, + value: quote.callFee + quote.value + quote.productFeeAmount + BigInt(quote.gasFee), call: lbc.methods.depositPegout(quote, signedQuote.signature) }) const parsedReceipt = decodeLogs({ abi: LiquidityBridgeContract.abi, receipt }) @@ -89,6 +99,12 @@ describe('Flyover pegout process should', () => { it('execute refundPegOut', async () => { const amountInSatoshi = (quote.value + quote.callFee) / BigInt(10**10) const amountInBtc = Number(amountInSatoshi) / 10**8 + if (config.btc.walletPassphrase) { + await bitcoinRpc('walletpassphrase', { + passphrase: config.btc.walletPassphrase, + timeout: 60 + }) + } const txHash = await sendBtc({ rpc: bitcoinRpc, amountInBtc, diff --git a/integration-test/test.config.example.json b/integration-test/test.config.example.json index 2a9f476..4c73a05 100644 --- a/integration-test/test.config.example.json +++ b/integration-test/test.config.example.json @@ -9,6 +9,7 @@ "btc": { "url": "http://127.0.0.1:5555/", "user": "test", - "pass": "test" + "pass": "test", + "walletPassphrase": "pass" } } \ No newline at end of file diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 1282d82..d1deebd 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -1,5 +1,4 @@ const { deployProxy } = require("@openzeppelin/truffle-upgrades"); -const web3 = require("web3"); const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract"); @@ -99,7 +98,7 @@ module.exports = async function (deployer, network) { state.address = btcUtilsInstance.address; }); - minimumPegIn = 2; + minimumPegIn = 3; } let config = read(); @@ -123,7 +122,7 @@ module.exports = async function (deployer, network) { console.log('Already deployed, skipping LiquidityBridgeContract deploy...'); return; } - + const response = await deployProxy( LiquidityBridgeContract, [ diff --git a/migrations/3_upgrade_contracts.js b/migrations/3_upgrade_contracts.js index d46cf52..9679266 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -1,40 +1,60 @@ const { upgradeProxy } = require("@openzeppelin/truffle-upgrades"); const SignatureValidator = artifacts.require("SignatureValidator"); -const Quotes = artifacts.require("Quotes"); -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const QuotesV2 = artifacts.require("QuotesV2"); +const LiquidityBridgeContractV2 = artifacts.require('LiquidityBridgeContractV2.sol'); const BtcUtils = artifacts.require("BtcUtils"); -const { deploy, read } = require("../config"); +const { read, deploy} = require("../config"); -module.exports = async function (deployer, network) { +// using LP address as placeholder for now +const FEE_COLLECTOR_MAINNET_ADDRESS = '0x4202BAC9919C3412fc7C8BE4e678e26279386603' +const FEE_COLLECTOR_TESTNET_ADDRESS = '0x86B6534687A176A476C16083a373fB9Fe4FAb449' +const DAO_FEE_PERCENTAGE = 0 + +module.exports = async function (deployer, network, accounts) { let config = read(); - console.log(network); - if (network === 'test') { - console.log("Upgrade isn't executed during tests"); - return; - } const signatureValidatorLib = await SignatureValidator.at( config[network]["SignatureValidator"].address ); - await deployer.link(signatureValidatorLib, LiquidityBridgeContract); + await deployer.link(signatureValidatorLib, LiquidityBridgeContractV2); - const quotesLib = await Quotes.at( - config[network]["Quotes"].address + await deployer.deploy(QuotesV2); + const quotesInstance = await QuotesV2.deployed(); + await LiquidityBridgeContractV2.link("QuotesV2", quotesInstance.address); + const quotesLib = await QuotesV2.at( + quotesInstance.address ); - await deployer.link(quotesLib, LiquidityBridgeContract); + await deployer.link(quotesLib, LiquidityBridgeContractV2); const btcUtilsLib = await BtcUtils.at( config[network]["BtcUtils"].address ); - await deployer.link(btcUtilsLib, LiquidityBridgeContract); + await deployer.link(btcUtilsLib, LiquidityBridgeContractV2); - const existing = config[network]["LiquidityBridgeContract"] + const existing = config[network]["LiquidityBridgeContract"]; + + console.log('Upgrading contract ', existing.address) const response = await upgradeProxy( existing.address, - LiquidityBridgeContract, + LiquidityBridgeContractV2, { deployer, unsafeAllowLinkedLibraries: true } ); + + let daoFeeCollectorAddress = ''; + + if(network === 'ganache' || network === 'rskRegtest' || network === 'test') { + daoFeeCollectorAddress = accounts[8]; + } else if(network === 'rskTestnet') { + daoFeeCollectorAddress = FEE_COLLECTOR_TESTNET_ADDRESS; + } else if(network === 'rskMainnet'){ + daoFeeCollectorAddress = FEE_COLLECTOR_MAINNET_ADDRESS; + } else { + throw new Error('Unknown network'); + } + + await response.initializeV2(DAO_FEE_PERCENTAGE, daoFeeCollectorAddress); + console.log("Upgraded", response.address); }; diff --git a/package-lock.json b/package-lock.json index 438abc2..2c4dc48 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "liquidity-bridge-contract", - "version": "1.0.0", + "version": "1.1.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "liquidity-bridge-contract", - "version": "1.0.0", + "version": "1.1.1", "license": "ISC", "dependencies": { "@openzeppelin/contracts": "^4.8.0", "@openzeppelin/contracts-upgradeable": "^4.8.2", + "@rsksmart/btc-transaction-solidity-helper": "^0.0.3", "@truffle/hdwallet-provider": "^2.1.3", "chai": "^4.3.4", "chai-bn": "^0.3.0", @@ -3070,6 +3071,12 @@ "dev": true, "peer": true }, + "node_modules/@rsksmart/btc-transaction-solidity-helper": { + "version": "0.0.3", + "resolved": "https://npm.pkg.github.com/download/@rsksmart/btc-transaction-solidity-helper/0.0.3/3016563d1170bb6a4efbd8d56ee9994434527cff", + "integrity": "sha512-x4SzwxhyMWZGwj6ycUZvM/2uVcIQ4hhWZ3DJeJ1LHcza1nA9ghPH7dDGQYeuWAmJwgz9P4mW+f/9Wahe0KQ3Ew==", + "license": "ISC" + }, "node_modules/@rsksmart/pmt-builder": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@rsksmart/pmt-builder/-/pmt-builder-3.0.0.tgz", @@ -21751,6 +21758,11 @@ "dev": true, "peer": true }, + "@rsksmart/btc-transaction-solidity-helper": { + "version": "0.0.3", + "resolved": "https://npm.pkg.github.com/download/@rsksmart/btc-transaction-solidity-helper/0.0.3/3016563d1170bb6a4efbd8d56ee9994434527cff", + "integrity": "sha512-x4SzwxhyMWZGwj6ycUZvM/2uVcIQ4hhWZ3DJeJ1LHcza1nA9ghPH7dDGQYeuWAmJwgz9P4mW+f/9Wahe0KQ3Ew==" + }, "@rsksmart/pmt-builder": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@rsksmart/pmt-builder/-/pmt-builder-3.0.0.tgz", diff --git a/package.json b/package.json index ae678c7..c68dc37 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,13 @@ { "name": "liquidity-bridge-contract", - "version": "1.0.0", + "version": "1.1.1", "description": "## registerFastBridgeBtcTransaction", "main": "index.js", "scripts": { "compile": "npm run lint:sol && npx truffle compile --all", "test": "npm run compile && npx truffle test", "test-regtest": "npm run compile && npx truffle test --network testRegtest", + "test-ganache": "npm run compile && npx truffle test --network ganache", "coverage": "npx truffle run coverage", "lint": "npx prettier --list-different 'contracts/**/*.sol' 'test/**/*.js' 'migrations/**/*.js'", "lint:sol": "solhint 'contracts/**/*.sol'", @@ -14,6 +15,7 @@ "deploy-rskTestnet": "npm run lint:sol && npx truffle deploy --network rskTestnet", "deploy-rskMainet": "npm run lint:sol && npx truffle deploy --network rskMainnet", "deploy-alphanet": "npm run lint:sol && npx truffle deploy --network alphanet", + "deploy-localGanache": "npm run lint:sol && npx truffle deploy --network ganache", "contract-size": "npx truffle run contract-size", "test:integration": "npx truffle test integration-test/* --bail --migrate-none --network" }, @@ -41,6 +43,7 @@ "dependencies": { "@openzeppelin/contracts": "^4.8.0", "@openzeppelin/contracts-upgradeable": "^4.8.2", + "@rsksmart/btc-transaction-solidity-helper": "^0.0.3", "@truffle/hdwallet-provider": "^2.1.3", "chai": "^4.3.4", "chai-bn": "^0.3.0", diff --git a/scripts/forceImport.js b/scripts/forceImport.js new file mode 100644 index 0000000..bb6c6a2 --- /dev/null +++ b/scripts/forceImport.js @@ -0,0 +1,7 @@ +const { forceImport } = require('@openzeppelin/truffle-upgrades'); +const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); + +module.exports = async function (deployer) { + const proxyAddress = "0xc2A630c053D12D63d32b025082f6Ba268db18300"; + await forceImport(proxyAddress, LiquidityBridgeContract, { deployer }); +}; diff --git a/scripts/verifyV2Upgrade.js b/scripts/verifyV2Upgrade.js new file mode 100644 index 0000000..8eb4750 --- /dev/null +++ b/scripts/verifyV2Upgrade.js @@ -0,0 +1,41 @@ +const json = require("../build/contracts/LiquidityBridgeContractV2.json"); +const configJson = require("../config.json"); + +module.exports = async function (callback) { + const network = config.config.network + let LBCAddress = ''; + + if (network) { + LBCAddress = configJson[network].LiquidityBridgeContract.address; + console.log(LBCAddress); + const contract = new web3.eth.Contract( + json.abi, + LBCAddress + ); + + const productFeePercentage = await contract.methods.productFeePercentage().call(); + const daoFeeCollectorAddress = await contract.methods.daoFeeCollectorAddress().call(); + + console.log('DAO Fee Percentage: ', productFeePercentage); + console.log('DAO Fee Collector Address ', daoFeeCollectorAddress); + } + + // + // + // contract.methods + // .register( + // "First contract", + // "http://localhost/api", + // true, + // "both" + // ) + // .call({ + // // from: accounts[0], + // value: 1200000000000000000, + // }) + // .then((response) => console.log("Success: " + response)) + // .catch((err) => console.log("Error: " + err)); + // + // // invoke callback + callback(); +}; diff --git a/test/basic.tests.js b/test/basic.tests.js index 5132981..23f52c9 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract"); +const LiquidityBridgeContractV2 = artifacts.require("LiquidityBridgeContractV2.sol"); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require("Mock"); const SignatureValidatorMock = artifacts.require("SignatureValidatorMock"); @@ -14,18 +14,17 @@ const expect = chai.expect; const bs58check = require('bs58check') const bs58 = require('bs58'); -contract("LiquidityBridgeContract", async (accounts) => { +contract("LiquidityBridgeContractV2.sol", async (accounts) => { let instance; let bridgeMockInstance; let mock; let signatureValidatorInstance; let btcUtils; const liquidityProviderRskAddress = accounts[0]; - const MAX_UINT32 = Math.pow(2, 32) - 1; var providerList = []; before(async () => { - const proxy = await LiquidityBridgeContract.deployed(); - instance = await LiquidityBridgeContract.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); signatureValidatorInstance = await SignatureValidatorMock.deployed(); @@ -41,7 +40,7 @@ contract("LiquidityBridgeContract", async (accounts) => { }); it("should register liquidity provider", async () => { - let currAddr = accounts[8]; + let currAddr = accounts[9]; let existing = await instance.getCollateral(currAddr); let tx = await instance.register( @@ -333,7 +332,7 @@ contract("LiquidityBridgeContract", async (accounts) => { .sub(web3.utils.toBN(initialLBCBalance)); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(peginAmount).to.be.a.bignumber.eq(lpBal); - expect(peginAmount).to.be.a.bignumber.eq(lbcBal); + expect(peginAmount.sub(quote.productFeeAmount)).to.be.a.bignumber.eq(lbcBal); expect(initialLPDeposit).to.be.a.bignumber.eq(finalLPDeposit); truffleAssertions.eventEmitted(cfuTx, "CallForUser", { from: quote.liquidityProviderRskAddress, @@ -591,6 +590,7 @@ contract("LiquidityBridgeContract", async (accounts) => { rskRefundAddress, web3.utils.toBN(0) ); + quote.productFeeAmount = web3.utils.toBN(0) await truffleAssertions.reverts( instance.hashQuote.call(utils.asArray(quote)), @@ -616,6 +616,7 @@ contract("LiquidityBridgeContract", async (accounts) => { it("should transfer value for user", async () => { let rskRefundAddress = accounts[2]; + const daoFeeCollectorInitialBalance = await web3.eth.getBalance(accounts[8]); let destAddr = accounts[1]; let lbcAddress = instance.address; let quote = utils.getTestQuote( @@ -635,7 +636,7 @@ contract("LiquidityBridgeContract", async (accounts) => { liquidityProviderRskAddress ); let initialLBCBalance = await web3.eth.getBalance(instance.address); - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes( @@ -655,10 +656,7 @@ contract("LiquidityBridgeContract", async (accounts) => { await bridgeMockInstance.setPegin(quoteHash, { value: peginAmount }); await bridgeMockInstance.setHeader(height, firstHeader); - await bridgeMockInstance.setHeader( - height + quote.depositConfirmations - 1, - nHeader - ); + await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); let initialLPDeposit = await instance.getCollateral( liquidityProviderRskAddress ); @@ -680,7 +678,7 @@ contract("LiquidityBridgeContract", async (accounts) => { height ); - await instance.registerPegIn( + const registerPegin = await instance.registerPegIn( utils.asArray(quote), signature, btcRawTransaction, @@ -704,6 +702,10 @@ contract("LiquidityBridgeContract", async (accounts) => { let usrBal = web3.utils .toBN(finalUserBalance) .sub(web3.utils.toBN(initialUserBalance)); + truffleAssertions.eventEmitted(registerPegin, "DaoFeeSent", { + quoteHash: quoteHash, + amount: quote.productFeeAmount + }); truffleAssertions.eventEmitted(cfuTx, "CallForUser", { from: quote.liquidityProviderRskAddress, dest: quote.destAddr, @@ -712,11 +714,77 @@ contract("LiquidityBridgeContract", async (accounts) => { success: true, quoteHash: quoteHash, }); + const daoFeeCollectorFinalBalance = await web3.eth.getBalance(accounts[8]); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(usrBal).to.be.a.bignumber.eq(quote.val); - expect(lbcBal).to.be.a.bignumber.eq(peginAmount); - expect(lpBal).to.be.a.bignumber.eq(peginAmount); + expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(quote.productFeeAmount)); + expect(lpBal).to.be.a.bignumber.eq(peginAmount.sub(quote.productFeeAmount)); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); + expect(daoFeeCollectorFinalBalance).to.be.a.bignumber.eq( + web3.utils.toBN(daoFeeCollectorInitialBalance).add(quote.productFeeAmount)); + }); + + it("Should not generate transaction to DAO when product fee is 0 in registerPegIn", async () => { + const daoFeeCollectorInitialBalance = await web3.eth.getBalance(accounts[8]); + let quote = utils.getTestQuote( + instance.address, + accounts[1], + "0x00", + liquidityProviderRskAddress, + accounts[2], + web3.utils.toBN(100) + ); + quote.productFeeAmount = web3.utils.toBN(0) + + let btcRawTransaction = "0x101"; + let partialMerkleTree = "0x202"; + let height = 10; + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); + let quoteHash = await instance.hashQuote(utils.asArray(quote)); + let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); + let firstConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTime + 300).substring(2) + ); + let nConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTime + 600).substring(2) + ); + let firstHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + firstConfirmationTime + + "0000000000000000"; + let nHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + nConfirmationTime + + "0000000000000000"; + + await bridgeMockInstance.setPegin(quoteHash, { value: peginAmount }); + await bridgeMockInstance.setHeader(height, firstHeader); + await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); + + await instance.callForUser(utils.asArray(quote), { value: quote.val }); + + await instance.registerPegIn.call( + utils.asArray(quote), + signature, + btcRawTransaction, + partialMerkleTree, + height + ); + + const registerPegin = await instance.registerPegIn( + utils.asArray(quote), + signature, + btcRawTransaction, + partialMerkleTree, + height + ); + + const daoFeeCollectorFinalBalance = await web3.eth.getBalance(accounts[8]); + expect(daoFeeCollectorFinalBalance).to.be.a.bignumber.eq(web3.utils.toBN(daoFeeCollectorInitialBalance)); + truffleAssertions.eventNotEmitted(registerPegin, "DaoFeeSent", { + quoteHash: quoteHash, + amount: quote.productFeeAmount + }); }); it("should resign", async () => { @@ -781,6 +849,7 @@ contract("LiquidityBridgeContract", async (accounts) => { value: web3.utils.toWei("30000", "wei"), from: liquidityProviderRskAddress, }); + const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[8]); const blockHeaderHash = "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; const partialMerkleTree = @@ -807,7 +876,7 @@ contract("LiquidityBridgeContract", async (accounts) => { // configure mocked block on mockBridge const firstConfirmationTime = utils.reverseHexBytes( - web3.utils.toHex(quote.agreementTimestamp + 300).substring(2) + web3.utils.toHex(quote.agreementTimestamp + 100).substring(2) ); const firstHeader = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + @@ -817,7 +886,7 @@ contract("LiquidityBridgeContract", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -845,13 +914,70 @@ contract("LiquidityBridgeContract", async (accounts) => { liquidityProviderRskAddress ); const usedInGas = refund.receipt.gasUsed * refund.receipt.effectiveGasPrice; - const refundedAmount = +quote.value + +quote.callFee; - expect(+lpBalanceAfter).to.be.eq( - +lpBalanceBefore + refundedAmount - usedInGas + const refundedAmount = quote.value.add(quote.callFee); + const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[8]); + + expect(lpBalanceAfter).to.be.a.bignumber.eq( + web3.utils.toBN(lpBalanceBefore).add(refundedAmount).sub(web3.utils.toBN(usedInGas)) ); + expect(web3.utils.toBN(daoFeeCollectorBefore).add(quote.productFeeAmount)).to.be.a.bignumber.eq(web3.utils.toBN(daoFeeCollectorAfter)) truffleAssertions.eventEmitted(refund, "PegOutRefunded"); }); + it("Should not generate transaction to DAO when product fee is 0 in refundPegOut", async () => { + await instance.addPegoutCollateral({ + value: web3.utils.toWei("30000", "wei"), + from: liquidityProviderRskAddress, + }); + const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[8]); + const blockHeaderHash = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; + const partialMerkleTree = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb426"; + const merkleBranchHashes = [ + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326", + ]; + + let quote = utils.getTestPegOutQuote( + instance.address, //lbc address + liquidityProviderRskAddress, + accounts[2], + web3.utils.toBN(1) + ); + quote.transferConfirmations = 0; + quote.productFeeAmount = web3.utils.toBN(0); + + // configure mocked block on mockBridge + const firstConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTimestamp + 100).substring(2) + ); + const firstHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + firstConfirmationTime + + "0000000000000000"; + await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); + + const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); + const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); + const msgValue = quote.value.add(quote.callFee).add(quote.gasFee); + const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { + value: msgValue.toNumber() + }); + await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); + + const btcTx = await utils.generateRawTx(instance, quote); + const refund = await instance.refundPegOut( + quoteHash, + btcTx, + blockHeaderHash, + partialMerkleTree, + merkleBranchHashes + ); + const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[8]); + expect(daoFeeCollectorBefore).to.be.a.bignumber.eq(daoFeeCollectorAfter) + truffleAssertions.eventNotEmitted(refund, "DaoFeeSent"); + }); + it("Should not allow user to re deposit a refunded quote", async () => { await instance.addPegoutCollateral({ value: web3.utils.toWei("30000", "wei"), @@ -886,7 +1012,7 @@ contract("LiquidityBridgeContract", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); @@ -1022,7 +1148,7 @@ contract("LiquidityBridgeContract", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1340,25 +1466,25 @@ contract("LiquidityBridgeContract", async (accounts) => { const firstRawTX = "0x0100000001013503c427ba46058d2d8ac9221a2f6fd50734a69f19dae65420191e3ada2d40000000006a47304402205d047dbd8c49aea5bd0400b85a57b2da7e139cec632fb138b7bee1d382fd70ca02201aa529f59b4f66fdf86b0728937a91a40962aedd3f6e30bce5208fec0464d54901210255507b238c6f14735a7abe96a635058da47b05b61737a610bef757f009eea2a4ffffffff0200879303000000001976a9143c5f66fe733e0ad361805b3053f23212e5755c8d88ac0000000000000000426a403938343934346435383039323135366335613139643936356239613735383530326536646263326439353337333135656266343839373336333134656233343700000000"; const firstTxOutputs = await btcUtils.getOutputs(firstRawTX); - const firstQuoteHash = web3.utils.hexToAscii(await btcUtils.parseOpReturnOuput(firstTxOutputs[1].pkScript)); - const firstDestinationAddress = await btcUtils.parsePayToAddressScript(firstTxOutputs[0].pkScript, false); + const firstNullScript = await btcUtils.parseNullDataScript(firstTxOutputs[1].pkScript); + const firstDestinationAddress = await btcUtils.parsePayToPubKeyHash(firstTxOutputs[0].pkScript, false); const firstValue = firstTxOutputs[0].value; const firstHash = await btcUtils.hashBtcTx(firstRawTX); const secondRawTX = "0x01000000010178a1cf4f2f0cb1607da57dcb02835d6aa8ef9f06be3f74cafea54759a029dc000000006a473044022070a22d8b67050bee57564279328a2f7b6e7f80b2eb4ecb684b879ea51d7d7a31022057fb6ece52c23ecf792e7597448c7d480f89b77a8371dca4700a18088f529f6a012103ef81e9c4c38df173e719863177e57c539bdcf97289638ec6831f07813307974cffffffff02801d2c04000000001976a9143c5f66fe733e0ad361805b3053f23212e5755c8d88ac0000000000000000426a406539346138393731323632396262633966636364316630633034613237386330653130353265623736323666393365396137663130363762343036663035373600000000"; const secondTxOutputs = await btcUtils.getOutputs(secondRawTX); - const secondQuoteHash = web3.utils.hexToAscii(await btcUtils.parseOpReturnOuput(secondTxOutputs[1].pkScript)); - const secondDestinationAddress = await btcUtils.parsePayToAddressScript(secondTxOutputs[0].pkScript, true); + const secondNullScript = await btcUtils.parseNullDataScript(secondTxOutputs[1].pkScript); + const secondDestinationAddress = await btcUtils.parsePayToPubKeyHash(secondTxOutputs[0].pkScript, true); const secondValue = secondTxOutputs[0].value; const secondHash = await btcUtils.hashBtcTx(secondRawTX); - expect(firstQuoteHash).to.eq("984944d58092156c5a19d965b9a758502e6dbc2d9537315ebf489736314eb347"); + expect(firstNullScript).to.eq("0x4039383439343464353830393231353663356131396439363562396137353835303265366462633264393533373331356562663438393733363331346562333437"); expect(firstDestinationAddress).to.eq("0x6f3c5f66fe733e0ad361805b3053f23212e5755c8d"); expect(firstValue).to.eq("60000000"); expect(firstHash).to.eq("0x03c4522ef958f724a7d2ffef04bd534d9eca74ffc0b28308797d2853bc323ba6"); - expect(secondQuoteHash).to.eq("e94a89712629bbc9fccd1f0c04a278c0e1052eb7626f93e9a7f1067b406f0576"); + expect(secondNullScript).to.eq("0x4065393461383937313236323962626339666363643166306330346132373863306531303532656237363236663933653961376631303637623430366630353736"); expect(secondDestinationAddress).to.eq("0x003c5f66fe733e0ad361805b3053f23212e5755c8d"); expect(secondValue).to.eq("70000000"); expect(secondHash).to.eq("0xfd4251485dafe36aaa6766b38cf236b5925f23f12617daf286e0e92f73708aa3"); @@ -1440,7 +1566,7 @@ contract("LiquidityBridgeContract", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1482,7 +1608,7 @@ contract("LiquidityBridgeContract", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { @@ -1756,9 +1882,11 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("6000000000000000"), + gasFee: BigInt("3000000000000000") }, - address: '2NB9Rp6DxS4WXefGoyNLa5rQWkcQtUM1FmF' + address: '2N54HuutZf7Xkmv85wCtBkQg5nCC3k432rf' }, { quote: { @@ -1779,9 +1907,11 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("7000000000000000"), + gasFee: BigInt("4000000000000000") }, - address: '2Mvbn9JQWjoS3SCBuxf1KTTkLw49WYjrkLx' + address: '2NARRvPtz2ch1KozGfZg6FahLSVVaSG2fQr' }, { quote: { @@ -1802,9 +1932,11 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("8000000000000000"), + gasFee: BigInt("5000000000000000") }, - address: '2N2dEn75BJDgUA4mnfZyKG9qX99ofzKizeC' + address: '2Mwfc2XRxm64dDbNowHUFj2pZ4owePhNFyQ' } ] diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index 5e95481..1ea17fc 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -1,3 +1,4 @@ +const LiquidityBridgeContractV2 = artifacts.require("LiquidityBridgeContractV2.sol"); const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract"); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require("Mock"); @@ -11,15 +12,15 @@ const chaiBN = require("chai-bn")(BN); chai.use(chaiBN); const expect = chai.expect; -contract("LiquidityBridgeContract", async (accounts) => { +contract("LiquidityBridgeContractV2.sol", async (accounts) => { let instance; let bridgeMockInstance; let mock; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContract.deployed(); - instance = await LiquidityBridgeContract.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); }); @@ -48,7 +49,7 @@ contract("LiquidityBridgeContract", async (accounts) => { } ); - let goodLP = accounts[8]; + let goodLP = accounts[6]; let goodProviderCollateral = web3.utils.toWei("30"); await instance.register.call( "First contract", @@ -88,6 +89,8 @@ contract("LiquidityBridgeContract", async (accounts) => { let depositConfirmations = 10; let penaltyFee = web3.utils.toBN(0); let callOnRegister = true; + let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -107,6 +110,8 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount, + gasFee ]; // Let's now register our quote in the bridge... note that the // value is only a hundred wei @@ -167,15 +172,21 @@ contract("LiquidityBridgeContract", async (accounts) => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let data = "0x00"; let callFee = 1; + const productFeeAmount = 1; + const gasFee = 1; let gasLimit = 150000; let nonce = 0; let delta = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) + .add(web3.utils.toBN(productFeeAmount)) + .add(web3.utils.toBN(gasFee)) .div(web3.utils.toBN(10000)); let peginAmount = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) + .add(web3.utils.toBN(productFeeAmount)) + .add(web3.utils.toBN(gasFee)) .sub(delta); let lbcAddress = instance.address; let agreementTime = 1661788988; @@ -203,6 +214,8 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount, + gasFee ]; let quoteHash = await instance.hashQuote(quote); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -274,7 +287,7 @@ contract("LiquidityBridgeContract", async (accounts) => { .sub(web3.utils.toBN(initialLPBalance)); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(usrBal).to.be.a.bignumber.eq(val); - expect(lbcBal).to.be.a.bignumber.eq(peginAmount); + expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(web3.utils.toBN(productFeeAmount))); expect(lpBal).to.be.a.bignumber.eq(peginAmount); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); }); @@ -313,6 +326,8 @@ contract("LiquidityBridgeContract", async (accounts) => { let depositConfirmations = 10; let penaltyFee = 0; let callOnRegister = false; + let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -332,6 +347,8 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount, + gasFee ]; let quoteHash = await instance.hashQuote(quote); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -410,36 +427,4 @@ contract("LiquidityBridgeContract", async (accounts) => { instance.initialize(bridgeMockInstance.address, MINIMUM_COLLATERAL, 1, 101, RESIGN_DELAY_BLOCKS, 1, 1, false) ); }); - - it("should extract timestamp from btc block header", async () => { - const btcHeader = - "0x0080cf2a0857bdec9d66f5feb52d00d5061ff02a904112d9b0cd1ac401000000000000003d2d2b5733c820a1f07ce6e0acd2ea47f27016b49ccb405b1e3e5786f8ae962e3ce30c63bc292d1919856362"; - - let timestamp = await instance.getBtcBlockTimestamp(btcHeader); - expect(timestamp).to.be.a.bignumber.eq(web3.utils.toBN(1661788988)); - - const btcHeader2 = "0x" + "00".repeat(68) + "12345678" + "00".repeat(8); - - let timestamp2 = await instance.getBtcBlockTimestamp(btcHeader2); - expect(timestamp2).to.be.a.bignumber.eq(web3.utils.toBN("0x78563412")); - }); - - it("should fail to extract timestamp from btc block header with invalid length", async () => { - const btcHeaderEmpty = "0x"; - const btcHeader79 = "0x" + "00".repeat(79); - const btcHeader81 = "0x" + "00".repeat(81); - - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeaderEmpty), - "LBC061" - ); - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeader79), - "LBC061" - ); - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeader81), - "LBC061" - ); - }); }); diff --git a/test/penalization.tests.js b/test/penalization.tests.js index 1560060..5b7311f 100644 --- a/test/penalization.tests.js +++ b/test/penalization.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const LiquidityBridgeContractV2 = artifacts.require('LiquidityBridgeContractV2.sol'); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require('Mock') const truffleAssert = require('truffle-assertions'); @@ -11,14 +11,14 @@ chai.use(chaiBN); const expect = chai.expect; -contract('LiquidityBridgeContract', async accounts => { +contract('LiquidityBridgeContractV2.sol', async accounts => { let instance; let bridgeMockInstance; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContract.deployed(); - instance = await LiquidityBridgeContract.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed() }); @@ -34,10 +34,10 @@ contract('LiquidityBridgeContract', async accounts => { let data = web3.eth.abi.encodeFunctionCall(mock.abi[2], []); let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - data, - liquidityProviderRskAddress, + data, + liquidityProviderRskAddress, rskRefundAddress, val); quote.penaltyFee = web3.utils.toBN(10); @@ -84,10 +84,10 @@ contract('LiquidityBridgeContract', async accounts => { let data = web3.eth.abi.encodeFunctionCall(mock.abi[2], []); let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - data, - liquidityProviderRskAddress, + data, + liquidityProviderRskAddress, rskRefundAddress, val); quote.penaltyFee = web3.utils.toBN(10); @@ -127,21 +127,21 @@ contract('LiquidityBridgeContract', async accounts => { expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); }); - it ('should penalize on late call', async () => { + it('should penalize on late call', async () => { let val = web3.utils.toBN(10); let rskRefundAddress = accounts[2]; let destAddr = accounts[1]; let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - null, - liquidityProviderRskAddress, + null, + liquidityProviderRskAddress, rskRefundAddress, val); quote.penaltyFee = web3.utils.toBN(10); quote.callTime = 1; - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let btcRawTransaction = '0x101'; let partialMerkleTree = '0x202'; @@ -162,7 +162,7 @@ contract('LiquidityBridgeContract', async accounts => { await bridgeMockInstance.setPegin(quoteHash, {value : peginAmount}); await bridgeMockInstance.setHeader(height, firstHeader); await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); - + await utils.timeout(5000); await instance.callForUser( @@ -191,13 +191,13 @@ contract('LiquidityBridgeContract', async accounts => { liquidityProvider: liquidityProviderRskAddress, penalty: quote.penaltyFee, quoteHash: quoteHash - }); + }); expect(usrBal).to.be.a.bignumber.eq(quote.val); expect(lpCol).to.be.a.bignumber.eq(quote.penaltyFee); - expect(lpBal).to.eql(web3.utils.toBN(reward).add(peginAmount)); + expect(lpBal).to.eql(peginAmount); }); - it ('should not underflow when penalty is higher than collateral', async () => { + it('should not underflow when penalty is higher than collateral', async () => { const lpAddress = accounts[9]; await instance.register( "First contract", @@ -214,10 +214,10 @@ contract('LiquidityBridgeContract', async accounts => { let destAddr = accounts[1]; let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - null, - lpAddress, + null, + lpAddress, rskRefundAddress, val); quote.callTime = 1; @@ -227,7 +227,7 @@ contract('LiquidityBridgeContract', async accounts => { let height = 10; let initialLPBalance = await instance.getBalance(lpAddress, { from: lpAddress }); - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let initialLPDeposit = await instance.getCollateral(lpAddress, { from: lpAddress }); let rewardPercentage = await instance.getRewardPercentage(); let quoteHash = await instance.hashQuote(utils.asArray(quote)); @@ -236,22 +236,22 @@ contract('LiquidityBridgeContract', async accounts => { let nConfirmationTime = utils.reverseHexBytes(web3.utils.toHex(quote.agreementTime + 1).substring(2)); let firstHeader = '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + firstConfirmationTime + '0000000000000000'; let nHeader = '0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' + nConfirmationTime + '0000000000000000'; - + let reward = Math.floor(Math.min(initialLPDeposit, quote.penaltyFee) * rewardPercentage / 100); await bridgeMockInstance.setPegin(quoteHash, {value : peginAmount}); await bridgeMockInstance.setHeader(height, firstHeader); await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); - + await utils.timeout(5000); - + await instance.callForUser( utils.asArray(quote), { value: quote.val, from: lpAddress } ); - + currentLPBalance = await instance.getBalance(lpAddress); expect(currentLPBalance).to.be.a.bignumber.eq(initialLPBalance); - + let tx = await instance.registerPegIn( utils.asArray(quote), signature, @@ -260,11 +260,11 @@ contract('LiquidityBridgeContract', async accounts => { height, { from: lpAddress } ); - + finalUserBalance = await web3.eth.getBalance(destAddr); finalLPBalance = await instance.getBalance(lpAddress); finalLPDeposit = await instance.getCollateral(lpAddress); - + let lpBal = web3.utils.toBN(finalLPBalance).sub(web3.utils.toBN(initialLPBalance)); truffleAssert.eventEmitted(tx, "Penalized", { liquidityProvider: lpAddress, @@ -272,7 +272,7 @@ contract('LiquidityBridgeContract', async accounts => { quoteHash: quoteHash }); expect(web3.utils.toBN(0)).to.be.a.bignumber.eq(finalLPDeposit); - expect(lpBal).to.eql(web3.utils.toBN(reward).add(peginAmount)); + expect(lpBal).to.eql(peginAmount.add(web3.utils.toBN(reward)).sub(quote.productFeeAmount)); }); it("Should penalize LP on pegout if the transfer was not made on time", async () => { @@ -299,7 +299,7 @@ contract('LiquidityBridgeContract', async accounts => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); truffleAssert.eventEmitted(pegOut, "PegOutDeposit"); diff --git a/test/refund.tests.js b/test/refund.tests.js index ac8fbd5..280e656 100644 --- a/test/refund.tests.js +++ b/test/refund.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const LiquidityBridgeContractV2 = artifacts.require('LiquidityBridgeContractV2.sol'); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require('Mock'); const WalletMock = artifacts.require('WalletMock'); @@ -13,14 +13,14 @@ chai.use(chaiBN); const expect = chai.expect; -contract('LiquidityBridgeContract', async accounts => { +contract('LiquidityBridgeContractV2.sol', async accounts => { let instance; let bridgeMockInstance; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContract.deployed(); - instance = await LiquidityBridgeContract.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); }); @@ -29,16 +29,16 @@ contract('LiquidityBridgeContract', async accounts => { await utils.ensureLiquidityProviderAvailable(instance, liquidityProviderRskAddress, utils.LP_COLLATERAL); }); - it ('should transfer value and refund remaining', async () => { + it('should transfer value and refund remaining', async () => { let destAddr = accounts[1]; let rskRefundAddress = accounts[2]; let data = '0x00'; let val = web3.utils.toBN(10); let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - data, - liquidityProviderRskAddress, + data, + liquidityProviderRskAddress, rskRefundAddress, val); @@ -50,8 +50,8 @@ contract('LiquidityBridgeContract', async accounts => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let initialRefundBalance = await web3.eth.getBalance(rskRefundAddress); let additionalFunds = web3.utils.toBN(1000000000000); - let peginAmount = val.add(quote.callFee).add(additionalFunds); - + let peginAmount = val.add(quote.callFee).add(additionalFunds).add(quote.gasFee); + let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes(web3.utils.toHex(quote.agreementTime + 300).substring(2)); @@ -106,7 +106,7 @@ contract('LiquidityBridgeContract', async accounts => { }); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(usrBal).to.be.a.bignumber.eq(val); - expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(additionalFunds)); + expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(additionalFunds).sub(quote.productFeeAmount)); expect(lpBal).to.be.a.bignumber.eq(peginAmount.sub(additionalFunds)); expect(refBal).to.be.a.bignumber.eq(additionalFunds); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); @@ -115,7 +115,7 @@ contract('LiquidityBridgeContract', async accounts => { it ('should refund remaining amount to LP in case refunding to quote.rskRefundAddress fails', async () => { let walletMock = await WalletMock.new(); await walletMock.setRejectFunds(true); - + let destAddr = accounts[1]; let rskRefundAddress = walletMock.address; let data = '0x00'; @@ -136,7 +136,7 @@ contract('LiquidityBridgeContract', async accounts => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let initialRefundBalance = await web3.eth.getBalance(rskRefundAddress); let additionalFunds = web3.utils.toBN(1000000000000); - let peginAmount = val.add(quote.callFee).add(additionalFunds); + let peginAmount = val.add(quote.callFee).add(additionalFunds).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -196,7 +196,7 @@ contract('LiquidityBridgeContract', async accounts => { }); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(usrBal).to.be.a.bignumber.eq(val); - expect(lbcBal).to.be.a.bignumber.eq(peginAmount); + expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(quote.productFeeAmount)); expect(lpBal).to.be.a.bignumber.eq(peginAmount); expect(refBal).to.be.a.bignumber.eq(web3.utils.toBN(0)); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); @@ -208,10 +208,10 @@ contract('LiquidityBridgeContract', async accounts => { let destAddr = mock.address; let data = web3.eth.abi.encodeFunctionCall(mock.abi[2], []); let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - data, - liquidityProviderRskAddress, + data, + liquidityProviderRskAddress, rskRefundAddress, val); @@ -219,8 +219,8 @@ contract('LiquidityBridgeContract', async accounts => { let partialMerkleTree = '0x202'; let height = 10; let initialLPBalance = await instance.getBalance(liquidityProviderRskAddress); - let peginAmount = quote.val.add(quote.callFee); - + let peginAmount = quote.val.add(quote.callFee).add(quote.gasFee); + let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes(web3.utils.toHex(quote.agreementTime + 300).substring(2)); @@ -264,8 +264,8 @@ contract('LiquidityBridgeContract', async accounts => { amount: quote.val, success: true }); - expect(lpBal).to.be.a.bignumber.eq(quote.val.add(quote.callFee)); - expect(usrBal).to.be.a.bignumber.eq(peginAmount.sub(quote.callFee)); + expect(lpBal).to.be.a.bignumber.eq(quote.val.add(quote.callFee).add(quote.gasFee)); + expect(usrBal).to.be.a.bignumber.eq(peginAmount.sub(quote.callFee).sub(quote.gasFee)); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); }); @@ -275,10 +275,10 @@ contract('LiquidityBridgeContract', async accounts => { let destAddr = mock.address; let data = web3.eth.abi.encodeFunctionCall(mock.abi[2], []); let quote = utils.getTestQuote( - instance.address, + instance.address, destAddr, - data, - liquidityProviderRskAddress, + data, + liquidityProviderRskAddress, rskRefundAddress, val); quote.penaltyFee = web3.utils.toBN(10); @@ -291,8 +291,8 @@ contract('LiquidityBridgeContract', async accounts => { let initialLPDeposit = await instance.getCollateral(liquidityProviderRskAddress); let reward = Math.floor(quote.penaltyFee.div(web3.utils.toBN(10))); let initialLbcBalance = await web3.eth.getBalance(instance.address); - let peginAmount = quote.val.add(quote.callFee); - + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); + let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes(web3.utils.toHex(quote.agreementTime + 300).substring(2)); @@ -322,7 +322,7 @@ contract('LiquidityBridgeContract', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000001), + amount: web3.utils.toBN(1000000000003), success: true, quoteHash: quoteHash, }); @@ -335,7 +335,7 @@ contract('LiquidityBridgeContract', async accounts => { it ('should no one be refunded in registerPegIn on missed call in case refunding to quote.rskRefundAddress fails', async () => { let walletMock = await WalletMock.new(); await walletMock.setRejectFunds(true); - + let val = web3.utils.toBN(1000000000000); let registerPegInCaller = accounts[2]; let rskRefundAddress = walletMock.address; @@ -353,7 +353,7 @@ contract('LiquidityBridgeContract', async accounts => { let btcRawTransaction = '0x101'; let partialMerkleTree = '0x202'; let height = 10; - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -391,7 +391,7 @@ contract('LiquidityBridgeContract', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000001), + amount: web3.utils.toBN(1000000000003), success: false, quoteHash: quoteHash, }); diff --git a/test/utils/index.js b/test/utils/index.js index 67f1d0c..b01fa65 100644 --- a/test/utils/index.js +++ b/test/utils/index.js @@ -15,12 +15,14 @@ function getTestQuote( let gasLimit = 150000; let nonce = 0; let data = callData || "0x00"; - let agreementTime = 1661788988; + let agreementTime = Math.floor(Date.now() / 1000); let timeForDeposit = 600; let callTime = 600; let depositConfirmations = 10; let penaltyFee = web3.utils.toBN(0); let callOnRegister = false; + let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = { fedBtcAddress, lbcAddress, @@ -40,6 +42,8 @@ function getTestQuote( callTime, depositConfirmations, callOnRegister, + productFeeAmount, + gasFee }; return quote; @@ -57,6 +61,8 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { let depositConfirmations = 10; let transferConfirmations = 10; let penaltyFee = web3.utils.toBN(0); + let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = { lbcAddress, @@ -76,6 +82,8 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { transferTime, expireDate, expireBlock, + productFeeAmount, + gasFee }; return quote; diff --git a/truffle-config.js b/truffle-config.js index ed57124..8f445d3 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -16,6 +16,12 @@ module.exports = { }, plugins: ["truffle-contract-size"], networks: { + ganache: { + host: '127.0.0.1', + port: 7545, + network_id: 5777, + gas: 200000000 + }, rskRegtest: { host: '127.0.0.1', port: 4444, From feb895ba6fccac96f6cd09e8c13bbb0ff62b0deb Mon Sep 17 00:00:00 2001 From: Luisfc68 <60527258+Luisfc68@users.noreply.github.com> Date: Tue, 20 Feb 2024 11:41:20 -0300 Subject: [PATCH 2/2] Master sync (#214) * Deployed to mainnet * Testnet Deploy * Added dao fee collector address * Fixed comments * Upgraded LBC to testnet * fix update script * LBC upgrade * fix test broken when adding productFeeAmount * enforce test to run on each environment branch * fix time dependant test to run in pipeline * Migrate to BtcUtils published library * fix unsafe integer calculation in test * update to avoid tx to the fee collector if productFee is 0 * contract size optimizations * separate gasFee from callFee * update integration tests * remove addresses from regtest config * testnet upgrade information * ci: add npm config * fix: install lib from new registry * fix: add linebreak in .npmrc * feat: update BtcUtils library * testnet upgrade * 1.1.1 * Qa test (#212) * Feature/GBI-1652 - Null script size verification (#208) * chore: change method of setting github token in workflow * feat: add null script content validation * test: add test to null script content validation * 1.1.2 * Stable test (#211) (#213) * Added dao fee collector address * Fixed comments * Upgraded LBC to testnet * fix update script * fix test broken when adding productFeeAmount * enforce test to run on each environment branch * fix time dependant test to run in pipeline * Migrate to BtcUtils published library * fix unsafe integer calculation in test * update to avoid tx to the fee collector if productFee is 0 * contract size optimizations * separate gasFee from callFee * update integration tests * remove addresses from regtest config * testnet upgrade information * ci: add npm config * fix: install lib from new registry * fix: add linebreak in .npmrc * feat: update BtcUtils library --------- Co-authored-by: MaximStanciu8 <118008926+MaximStanciu8@users.noreply.github.com> Co-authored-by: Guilherme Soares --------- Co-authored-by: Guilherme Soares Co-authored-by: MaximStanciu8 <118008926+MaximStanciu8@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- .openzeppelin/unknown-31.json | 367 ++++++++++++++++++++++++ contracts/LiquidityBridgeContractV2.sol | 1 + errorCodes.json | 3 +- package-lock.json | 4 +- package.json | 2 +- test/basic.tests.js | 62 ++++ 7 files changed, 436 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fafe853..c5a262c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: node-version: '19.6.0' - name: NPM Login - run: echo "//npm.pkg.github.com/:_authToken=${{ secrets.GITHUB_TOKEN }}" >> .npmrc + run: npm config set //npm.pkg.github.com/:_authToken ${{ secrets.GITHUB_TOKEN }} - name: Install truffle run: npm install -g truffle diff --git a/.openzeppelin/unknown-31.json b/.openzeppelin/unknown-31.json index dc8d66d..d1b3351 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -2541,6 +2541,373 @@ } } } + }, + "5f8d16007d06cb460d5d9f35efd903dca17132c79941d9ebd70b99c3b405b092": { + "address": "0xf87F5dEEd6F527C62b718a509d65494dfFB8Dc6C", + "txHash": "0x17018de742d304ef560f7a606ceb6c5335e3b47c683d3adde3f7b5f313d25107", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)3208", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:92" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:93" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:94" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:95" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)7056_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:96" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)7038_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:97" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:98" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10194_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)7043_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:115" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + } + ], + "types": { + "t_contract(Bridge)3208": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)7056_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)7056_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)7038_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)7038_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10194_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10194_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + }, + { + "label": "gasFee", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)7043_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)7043_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } } } } diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index 2b11b72..32a8f86 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -703,6 +703,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra require(quote.lbcAddress != address(0), "LBC042"); BtcUtils.TxRawOutput[] memory outputs = BtcUtils.getOutputs(btcTx); bytes memory scriptContent = BtcUtils.parseNullDataScript(outputs[QUOTE_HASH_OUTPUT].pkScript); + require(scriptContent.length == 33 && scriptContent[0] == 0x20, "LBC075"); // shift the array to remove the first byte (the size) for (uint8 i = 0 ; i < scriptContent.length - 1; i++) { scriptContent[i] = scriptContent[i + 1]; diff --git a/errorCodes.json b/errorCodes.json index 8d94300..0eb1472 100644 --- a/errorCodes.json +++ b/errorCodes.json @@ -67,5 +67,6 @@ "LBC071": "Intentional overflow on quote values", "LBC072": "Minimum collateral for registration can't be lower than 0.6 RBTC", "LBC073": "Resign delay blocks lower than minimal", - "LBC074": "Error sending fee to DAO" + "LBC074": "Error sending fee to DAO", + "LBC075": "Malformed BTC transaction output" } diff --git a/package-lock.json b/package-lock.json index 2c4dc48..24a57b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "liquidity-bridge-contract", - "version": "1.1.1", + "version": "1.1.2", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "liquidity-bridge-contract", - "version": "1.1.1", + "version": "1.1.2", "license": "ISC", "dependencies": { "@openzeppelin/contracts": "^4.8.0", diff --git a/package.json b/package.json index c68dc37..ce5c3cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "liquidity-bridge-contract", - "version": "1.1.1", + "version": "1.1.2", "description": "## registerFastBridgeBtcTransaction", "main": "index.js", "scripts": { diff --git a/test/basic.tests.js b/test/basic.tests.js index 23f52c9..f872669 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1540,6 +1540,68 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { ); }); + it("Should fail on refundPegout if btc tx null data script has wrong format", async () => { + const blockHeaderHash = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; + const partialMerkleTree = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb426"; + const merkleBranchHashes = [ + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326", + ]; + let quote = utils.getTestPegOutQuote( + instance.address, //lbc address + liquidityProviderRskAddress, + accounts[2], + web3.utils.toBN(1) + ); + quote.transferConfirmations = 0; + + // configure mocked block on mockBridge + const firstConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTimestamp + 300).substring(2) + ); + const firstHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + firstConfirmationTime + + "0000000000000000"; + await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); + + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); + const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); + const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); + const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { + value: msgValue.toNumber() + }); + await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); + + let incorrectSizeByteTx = await utils.generateRawTx(instance, quote); + incorrectSizeByteTx = web3.utils.bytesToHex(incorrectSizeByteTx).replace("6a20", "6a40"); + await truffleAssertions.reverts( + instance.refundPegOut( + quoteHash, + incorrectSizeByteTx, + blockHeaderHash, + partialMerkleTree, + merkleBranchHashes + ), + "LBC075" + ); + + let incorrectHashSizeTx = await utils.generateRawTx(instance, quote); + incorrectHashSizeTx = web3.utils.bytesToHex(incorrectHashSizeTx) + .replace("226a20"+quoteHash.slice(2), "216a19"+quoteHash.slice(2, -2)); + await truffleAssertions.reverts( + instance.refundPegOut( + quoteHash, + incorrectHashSizeTx, + blockHeaderHash, + partialMerkleTree, + merkleBranchHashes + ), + "LBC075" + ); + }); + it("Should fail on refundPegout if btc tx doesn't have correct amount", async () => { const blockHeaderHash = "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; const partialMerkleTree = "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb426";