From 79b4033bdc89a0e7f1ec4ba200521c204d3064be Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Wed, 29 Nov 2023 09:37:06 +0100 Subject: [PATCH 1/4] Testnet Deploy --- .openzeppelin/unknown-31.json | 361 +--------------------------------- config.json | 10 +- 2 files changed, 12 insertions(+), 359 deletions(-) diff --git a/.openzeppelin/unknown-31.json b/.openzeppelin/unknown-31.json index 0210ee2..3d74134 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -1,20 +1,20 @@ { "manifestVersion": "3.2", "admin": { - "address": "0xb77900eaC31c3E5CE26432807d5efFE9Ac06455f", - "txHash": "0x820ece21f6e73508041ac5c816d2d6cc65da196092680288f4464b5f0d13995d" + "address": "0x3e9552207f9aE1cC220BaCFf2BF5386d8842184E", + "txHash": "0xc29f17eaa51fb18ce9ac8426c93c9ee937e8db67d2e45f1cd03b7b4e7e96f504" }, "proxies": [ { - "address": "0x2A4C1373A52D65943b9043062052534F7724c356", - "txHash": "0x420730553de36e6fa322abe5d3dd73b108206d99a35869513eee48ad7612f77e", + "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", + "txHash": "0xcc06061b88c17b6bbaefdf338ea3d424cc2ac193570989f0236a659f278e5008", "kind": "transparent" } ], "impls": { - "da0a0546183d529a4b5c14527febccd2de6a0f30fcbe26cf815fb6b6ead1d595": { - "address": "0xbE1fa1653CB617A7Cad8DD3f0409D0801fEc5850", - "txHash": "0xecc3ec11f1afbf6c9ea26875fef6182e595c41df1bd2e7f762ebafebe6c0c685", + "8205e25ab44d03e2a4148f4cd4b0ad10fd6ccc6d71d0dc1b1f79ce25b1bce77a": { + "address": "0xe0712877a3AF76B0611a33872534e08c39c601d5", + "txHash": "0x804799515bae2faaec2bc7124a40f2ace1ba5c9103b339c272efaec9755e23b6", "layout": { "solcVersion": "0.8.18", "storage": [ @@ -358,353 +358,6 @@ } } } - }, - "90a13b12a1c79105378a05e67d9bb92fa14e6fa39baa8d1a8df8c8987ffc4c88": { - "address": "0xe09e975980C467b06B154Ec804FBe65022b58007", - "txHash": "0x0adac3c853f836ef2fc96a0c134d6e1efb366fbf8db3ef0c51f613ea6c64933d", - "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)7068_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)7068_storage)": { - "label": "mapping(bytes32 => struct Quotes.PegOutQuote)" - }, - "t_struct(PegOutQuote)7068_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/config.json b/config.json index 259ffc6..31fecf5 100644 --- a/config.json +++ b/config.json @@ -71,23 +71,23 @@ }, "rskTestnet": { "Migrations": { - "address": "0xD35900b447AA1f18B42d26372660b7FE7d9aEB9C", + "address": "0x96B4493B3390F6556979b002daC6D0A091973beE", "deployed": true }, "SignatureValidator": { - "address": "0x6905711e16AFBC51d8cf2a714479D14FB3e03281", + "address": "0xA66B2938b6cC837821cF4Be27F7016951047E03D", "deployed": true }, "Quotes": { - "address": "0x8384952Dc2A3e413800e9D4EE89d7383FA12Af10", + "address": "0xeC7147081b0d8f703c3178e514525cC14Ff01f99", "deployed": true }, "BtcUtils": { - "address": "0x52b0108b38Ae80305F92FC461026e4c200ed673E", + "address": "0xDE99557170A479c58b32C0cf92ee50ec2836cB5B", "deployed": true }, "LiquidityBridgeContract": { - "address": "0x2A4C1373A52D65943b9043062052534F7724c356", + "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", "deployed": true } } From 1c007125e0a6da4740487aaca64c272c7de91764 Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Mon, 4 Dec 2023 13:08:29 +0100 Subject: [PATCH 2/4] Added dao fee collector address --- .openzeppelin/BKP-rsk-testnet.json | 369 -------- .openzeppelin/unknown-31.json | 65 +- config.json | 22 + contracts/LiquidityBridgeContract.sol | 4 +- contracts/LiquidityBridgeContractV1.sol | 1025 +++++++++++++++++++++++ contracts/Quotes.sol | 2 +- contracts/QuotesV1.sol | 151 ++++ errorCodes.json | 3 +- migrations/2_deploy_contracts.js | 3 +- migrations/3_upgrade_contracts.js | 43 +- package.json | 2 + scripts/forceImport.js | 7 + scripts/testLBC.js | 46 + test/basic.tests.js | 39 +- test/miscellaneous.tests.js | 16 +- test/penalization.tests.js | 62 +- test/refund.tests.js | 42 +- test/utils/index.js | 4 + truffle-config.js | 6 + 19 files changed, 1418 insertions(+), 493 deletions(-) delete mode 100644 .openzeppelin/BKP-rsk-testnet.json create mode 100644 contracts/LiquidityBridgeContractV1.sol create mode 100644 contracts/QuotesV1.sol create mode 100644 scripts/forceImport.js create mode 100644 scripts/testLBC.js 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-31.json b/.openzeppelin/unknown-31.json index 3d74134..a60ccec 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -1,20 +1,17 @@ { "manifestVersion": "3.2", "admin": { - "address": "0x3e9552207f9aE1cC220BaCFf2BF5386d8842184E", - "txHash": "0xc29f17eaa51fb18ce9ac8426c93c9ee937e8db67d2e45f1cd03b7b4e7e96f504" + "address": "0x3e9552207f9aE1cC220BaCFf2BF5386d8842184E" }, "proxies": [ { "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", - "txHash": "0xcc06061b88c17b6bbaefdf338ea3d424cc2ac193570989f0236a659f278e5008", "kind": "transparent" } ], "impls": { "8205e25ab44d03e2a4148f4cd4b0ad10fd6ccc6d71d0dc1b1f79ce25b1bce77a": { "address": "0xe0712877a3AF76B0611a33872534e08c39c601d5", - "txHash": "0x804799515bae2faaec2bc7124a40f2ace1ba5c9103b339c272efaec9755e23b6", "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": [ { diff --git a/config.json b/config.json index 31fecf5..c3ac571 100644 --- a/config.json +++ b/config.json @@ -90,5 +90,27 @@ "address": "0xc2A630c053D12D63d32b025082f6Ba268db18300", "deployed": true } + }, + "ganache": { + "Migrations": { + "address": "0x21d86cA6fb5753d76ecBb313c9bc87f99306A57C", + "deployed": true + }, + "SignatureValidator": { + "address": "0x97522395D02899beB44C1c11cFD4F3B1ff2d6Ad9", + "deployed": true + }, + "Quotes": { + "address": "0x31fE2df3EccA461EaD8D1823e192E902dd3953eE", + "deployed": true + }, + "BtcUtils": { + "address": "0xbD171112775e70a44A1aBaabf3045aA8229Df7B5", + "deployed": true + }, + "LiquidityBridgeContract": { + "address": "0x11D50Bcaff24425FC0b8E60a6818C5c455082bE7", + "deployed": true + } } } \ No newline at end of file diff --git a/contracts/LiquidityBridgeContract.sol b/contracts/LiquidityBridgeContract.sol index 4e633bd..b431846 100644 --- a/contracts/LiquidityBridgeContract.sol +++ b/contracts/LiquidityBridgeContract.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; } diff --git a/contracts/LiquidityBridgeContractV1.sol b/contracts/LiquidityBridgeContractV1.sol new file mode 100644 index 0000000..038ae69 --- /dev/null +++ b/contracts/LiquidityBridgeContractV1.sol @@ -0,0 +1,1025 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; +pragma experimental ABIEncoderV2; + +import "./Bridge.sol"; +import "./QuotesV1.sol"; +import "./SignatureValidator.sol"; +import "./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 LiquidityBridgeContractV1 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 MAX_UINT = 2 ** 256 - 1; + 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 Deposit(address 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 PegOut( + address from, + uint256 amount, + bytes32 quotehash, + uint processed + ); + 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); + event DaoFeeSentError(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 => QuotesV1.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"); + _; + } + + modifier onlyEoa() { + require(tx.origin == msg.sender, "LBC003"); + _; + } + + function initializeV1( + 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 + ) public 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 (QuotesV1.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 onlyEoa returns (uint) { + //require(collateral[msg.sender] == 0, "Already registered"); + validateRegisterParameters( + _name, + _apiBaseUrl, + _providerType + ); + + 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); + } + + /** + @dev Validates input parameters for the register function + */ + function validateRegisterParameters( + string memory _name, + string memory _apiBaseUrl, + string memory _providerType + ) internal pure { + 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" + ); + } + + 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( + QuotesV1.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( + QuotesV1.PeginQuote memory quote, + bytes memory signature, + bytes memory btcRawTransaction, + bytes memory partialMerkleTree, + uint256 height + ) public 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); + + QuotesV1.checkAgreedAmount(quote, transferredAmount); + + if (callRegistry[quoteHash].timestamp > 0) { + uint refundAmount; + + if (callRegistry[quoteHash].success) { + refundAmount = min( + transferredAmount, + quote.value + quote.callFee + ); + } else { + refundAmount = min(transferredAmount, quote.callFee); + } + increaseBalance(quote.liquidityProviderRskAddress, refundAmount); + + uint remainingAmount = transferredAmount - refundAmount; + (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{ + gas: MAX_REFUND_GAS_LIMIT, + value: quote.productFeeAmount + }(""); + + if(daoSuccess) { + emit DaoFeeSent(quoteHash, quote.productFeeAmount); + } else { + emit DaoFeeSentError(quoteHash, quote.productFeeAmount); + } + + 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 + QuotesV1.PegOutQuote memory quote, + bytes memory signature + ) external payable { + require(isRegisteredForPegout(quote.lpRskAddress), "LBC037"); + require(quote.value + quote.callFee <= 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" + ); + + QuotesV1.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 + ) public nonReentrant { + QuotesV1.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; + 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 + ) public nonReentrant onlyRegisteredForPegout { + require(pegoutRegistry[quoteHash].completed == false, "LBC064"); + QuotesV1.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)); + 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.parsePayToAddressScript(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"); + + (bool sentDAO,) = payable(daoFeeCollectorAddress).call{ + value: quote.productFeeAmount + }(""); + require(sentDAO, "LBC074"); + + delete registeredPegoutQuotes[txQuoteHash]; + pegoutRegistry[txQuoteHash].completed = true; + emit PegOutRefunded(txQuoteHash); + } + + function validatePeginDepositAddress( + QuotesV1.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(QuotesV1.PeginQuote memory quote) public view returns (bytes32) { + return validateAndHashQuote(quote); + } + + function hashPegoutQuote( + QuotesV1.PegOutQuote memory quote + ) public view returns (bytes32) { + return validateAndHashPegOutQuote(quote); + } + + function validateAndHashQuote( + QuotesV1.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 >= minPegIn, + "LBC055" + ); + require( + type(uint32).max >= uint64(quote.agreementTimestamp) + uint64(quote.timeForDeposit), + "LBC071" + ); + + return keccak256(QuotesV1.encodeQuote(quote)); + } + + function validateAndHashPegOutQuote( + QuotesV1.PegOutQuote memory quote + ) private view returns (bytes32) { + require(address(this) == quote.lbcAddress, "LBC056"); + + return keccak256(QuotesV1.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( + QuotesV1.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( + QuotesV1.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) { + return false; + } + + bytes memory firstConfirmationHeader = bridge + .getBtcBlockchainBlockHeaderByHeight(height); + require(firstConfirmationHeader.length > 0, "Invalid block height"); + + uint256 firstConfirmationTimestamp = 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 = getBtcBlockTimestamp( + nConfirmationsHeader + ); + + // penalize if the call was not made on time + if (callTimestamp > nConfirmationsTimestamp + quote.callTime) { + return true; + } + return false; + } + + function shouldPenalizePegOutLP( + QuotesV1.PegOutQuote memory quote, + bytes32 quoteHash, + bytes32 blockHash + ) private view returns (bool) { + bytes memory firstConfirmationHeader = bridge.getBtcBlockchainBlockHeaderByHash(blockHash); + require(firstConfirmationHeader.length > 0, "LBC059"); + + uint256 firstConfirmationTimestamp = 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; + } + + /** + @dev Gets the timestamp of a Bitcoin block header + @param header The block header + @return The timestamp of the block header + */ + function getBtcBlockTimestamp( + bytes memory header + ) public pure returns (uint256) { + // bitcoin header is 80 bytes and timestamp is 4 bytes from byte 68 to byte 71 (both inclusive) + require(header.length == 80, "LBC061"); + + return sliceUint32FromLSB(header, 68); + } + + // bytes must have at least 28 bytes before the uint32 + function sliceUint32FromLSB( + bytes memory bs, + uint offset + ) internal pure returns (uint32) { + require(bs.length >= offset + 4, "LBC062"); + + return + uint32(uint8(bs[offset])) | + (uint32(uint8(bs[offset + 1])) << 8) | + (uint32(uint8(bs[offset + 2])) << 16) | + (uint32(uint8(bs[offset + 3])) << 24); + } +} 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/QuotesV1.sol b/contracts/QuotesV1.sol new file mode 100644 index 0000000..8723038 --- /dev/null +++ b/contracts/QuotesV1.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.18; + +library QuotesV1 { + 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; + } + + 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; + } + + 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 + ); + } + + 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 + ); + } + + function checkAgreedAmount( + PeginQuote memory quote, + uint transferredAmount + ) external pure { + uint agreedAmount = 0; + if(quote.productFeeAmount > 0) { + agreedAmount = quote.value + quote.callFee + quote.productFeeAmount; + } else { + agreedAmount = quote.value + quote.callFee; + } + + 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/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index 1282d82..aa0942c 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"); @@ -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..9355c72 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -1,40 +1,51 @@ const { upgradeProxy } = require("@openzeppelin/truffle-upgrades"); const SignatureValidator = artifacts.require("SignatureValidator"); -const Quotes = artifacts.require("Quotes"); -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const QuotesV1 = artifacts.require("QuotesV1"); +const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); const BtcUtils = artifacts.require("BtcUtils"); -const { deploy, read } = require("../config"); +const { read, deploy} = require("../config"); -module.exports = async function (deployer, network) { +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, LiquidityBridgeContractV1); - const quotesLib = await Quotes.at( - config[network]["Quotes"].address + await deployer.deploy(QuotesV1); + const quotesInstance = await QuotesV1.deployed(); + await LiquidityBridgeContractV1.link("QuotesV1", quotesInstance.address); + const quotesLib = await QuotesV1.at( + quotesInstance.address ); - await deployer.link(quotesLib, LiquidityBridgeContract); + await deployer.link(quotesLib, LiquidityBridgeContractV1); const btcUtilsLib = await BtcUtils.at( config[network]["BtcUtils"].address ); - await deployer.link(btcUtilsLib, LiquidityBridgeContract); + await deployer.link(btcUtilsLib, LiquidityBridgeContractV1); + + const existing = config[network]["LiquidityBridgeContract"]; - const existing = config[network]["LiquidityBridgeContract"] + console.log('Upgrading contract ', existing.address) const response = await upgradeProxy( existing.address, - LiquidityBridgeContract, + LiquidityBridgeContractV1, { deployer, unsafeAllowLinkedLibraries: true } ); + + let daoFeeCollectorAddress = ''; + + if(network === 'ganache' || network === 'rskRegtest') { + daoFeeCollectorAddress = accounts[9]; + } else if(network === '') { + daoFeeCollectorAddress = '0x438A3641d53552EFBaB487c5894a78A1434F5aC9'; + } + + await response.initializeV1(1, daoFeeCollectorAddress); + console.log("Upgraded", response.address); }; diff --git a/package.json b/package.json index ae678c7..c79fe67 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "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" }, 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/testLBC.js b/scripts/testLBC.js new file mode 100644 index 0000000..3d8fbad --- /dev/null +++ b/scripts/testLBC.js @@ -0,0 +1,46 @@ +const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); + +const quote = { + fedBTCAddr: "2N3JQb9erL1SnAr3NTMrZiPQQ8dcjJp4idV", + lbcAddr: "0xc2A630c053D12D63d32b025082f6Ba268db18300", + lpRSKAddr: "0x7C4890A0f1D4bBf2C669Ac2d1efFa185c505359b", + btcRefundAddr: "2N9sm43yNrTw7kcxFZEupXtYtgh7YTVQorK", + rskRefundAddr: "0xB4BF5fbe395298CEcC00e4d4aDDC62B7192E6f9F", + lpBTCAddr: "mhghaQCHedKZZQuFqSzg6Z3Rf1TqqDEPCc", + callFee: 101368444000000, + penaltyFee: 1000000, + contractAddr: "0xB4BF5fbe395298CEcC00e4d4aDDC62B7192E6f9F", + data: "", + gasLimit: 21000, + nonce: 2424835889795045890, + value: 5000000000000000, + agreementTimestamp: 1701165410, + timeForDeposit: 3600, + lpCallTime: 7200, + confirmations: 2, + callOnRegister: false, + callCost: 1368444000000 +} + +module.exports = async function (callback) { + let accounts = await web3.eth.getAccounts(); + + const proxyAddress = "0x11D50Bcaff24425FC0b8E60a6818C5c455082bE7"; + + const proxyInstance = await LiquidityBridgeContractV1.at(proxyAddress); + + console.log('Getting owner address'); + const ownerAddress = await proxyInstance.owner(); + console.log("Owner address: ", ownerAddress); + + console.log('Getting product fee percentage'); + const prodFee = await proxyInstance.productFeePercentage(); + console.log('Product fee percentage: ', prodFee.toString()); + + console.log('Getting DAO fee collector address'); + const daoFeeCollectorAddr = await proxyInstance.daoFeeCollectorAddress(); + console.log('DAO fee collector Address: ', daoFeeCollectorAddr); + + // invoke callback + callback(); +}; diff --git a/test/basic.tests.js b/test/basic.tests.js index 5132981..9bbebea 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract"); +const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require("Mock"); const SignatureValidatorMock = artifacts.require("SignatureValidatorMock"); @@ -14,7 +14,7 @@ const expect = chai.expect; const bs58check = require('bs58check') const bs58 = require('bs58'); -contract("LiquidityBridgeContract", async (accounts) => { +contract("LiquidityBridgeContractV1", async (accounts) => { let instance; let bridgeMockInstance; let mock; @@ -24,8 +24,8 @@ contract("LiquidityBridgeContract", async (accounts) => { 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 LiquidityBridgeContractV1.deployed(); + instance = await LiquidityBridgeContractV1.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); signatureValidatorInstance = await SignatureValidatorMock.deployed(); @@ -333,7 +333,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, @@ -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[9]); 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); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes( @@ -680,7 +681,7 @@ contract("LiquidityBridgeContract", async (accounts) => { height ); - await instance.registerPegIn( + const registerPegin = await instance.registerPegIn( utils.asArray(quote), signature, btcRawTransaction, @@ -704,6 +705,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 +717,14 @@ contract("LiquidityBridgeContract", async (accounts) => { success: true, quoteHash: quoteHash, }); + const daoFeeCollectorFinalBalance = await web3.eth.getBalance(accounts[9]); 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 resign", async () => { @@ -781,6 +789,7 @@ contract("LiquidityBridgeContract", async (accounts) => { value: web3.utils.toWei("30000", "wei"), from: liquidityProviderRskAddress, }); + const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[9]); const blockHeaderHash = "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; const partialMerkleTree = @@ -846,9 +855,12 @@ contract("LiquidityBridgeContract", async (accounts) => { ); const usedInGas = refund.receipt.gasUsed * refund.receipt.effectiveGasPrice; const refundedAmount = +quote.value + +quote.callFee; + const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[9]); + expect(+lpBalanceAfter).to.be.eq( +lpBalanceBefore + refundedAmount - usedInGas ); + expect(+daoFeeCollectorBefore + +quote.productFeeAmount.toString()).to.be.eq(+daoFeeCollectorAfter) truffleAssertions.eventEmitted(refund, "PegOutRefunded"); }); @@ -1756,7 +1768,8 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("6000000000000000") }, address: '2NB9Rp6DxS4WXefGoyNLa5rQWkcQtUM1FmF' }, @@ -1779,7 +1792,8 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("7000000000000000") }, address: '2Mvbn9JQWjoS3SCBuxf1KTTkLw49WYjrkLx' }, @@ -1802,7 +1816,8 @@ contract("LiquidityBridgeContract", async (accounts) => { timeForDeposit: 3600, callTime: 7200, depositConfirmations: 10, - callOnRegister: false + callOnRegister: false, + productFeeAmount: BigInt("8000000000000000") }, address: '2N2dEn75BJDgUA4mnfZyKG9qX99ofzKizeC' } diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index 5e95481..f41184e 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -1,3 +1,4 @@ +const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); 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("LiquidityBridgeContractV1", 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 LiquidityBridgeContractV1.deployed(); + instance = await LiquidityBridgeContractV1.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); }); @@ -88,6 +89,7 @@ contract("LiquidityBridgeContract", async (accounts) => { let depositConfirmations = 10; let penaltyFee = web3.utils.toBN(0); let callOnRegister = true; + let productFeeAmount = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -107,6 +109,7 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount ]; // Let's now register our quote in the bridge... note that the // value is only a hundred wei @@ -184,6 +187,8 @@ contract("LiquidityBridgeContract", async (accounts) => { let depositConfirmations = 10; let penaltyFee = 0; let callOnRegister = false; + let productFeeAmount = web3.utils.toBN(1); + peginAmount.add(productFeeAmount); let quote = [ fedBtcAddress, lbcAddress, @@ -203,6 +208,7 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount ]; let quoteHash = await instance.hashQuote(quote); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -232,7 +238,7 @@ contract("LiquidityBridgeContract", async (accounts) => { liquidityProviderRskAddress ); - await instance.callForUser(quote, { value: val }); + await instance.callForUser(quote, { value: val.add(productFeeAmount) }); let currentLPBalance = await instance.getBalance( liquidityProviderRskAddress @@ -313,6 +319,7 @@ contract("LiquidityBridgeContract", async (accounts) => { let depositConfirmations = 10; let penaltyFee = 0; let callOnRegister = false; + let productFeeAmount = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -332,6 +339,7 @@ contract("LiquidityBridgeContract", async (accounts) => { callTime, depositConfirmations, callOnRegister, + productFeeAmount ]; let quoteHash = await instance.hashQuote(quote); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); diff --git a/test/penalization.tests.js b/test/penalization.tests.js index 1560060..48e8afe 100644 --- a/test/penalization.tests.js +++ b/test/penalization.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); 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('LiquidityBridgeContractV1', 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 LiquidityBridgeContractV1.deployed(); + instance = await LiquidityBridgeContractV1.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); 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); 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 () => { diff --git a/test/refund.tests.js b/test/refund.tests.js index ac8fbd5..ca00302 100644 --- a/test/refund.tests.js +++ b/test/refund.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContract = artifacts.require('LiquidityBridgeContract'); +const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); 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('LiquidityBridgeContractV1', 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 LiquidityBridgeContractV1.deployed(); + instance = await LiquidityBridgeContractV1.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); @@ -51,7 +51,7 @@ contract('LiquidityBridgeContract', async accounts => { let initialRefundBalance = await web3.eth.getBalance(rskRefundAddress); let additionalFunds = web3.utils.toBN(1000000000000); let peginAmount = val.add(quote.callFee).add(additionalFunds); - + 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'; @@ -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); @@ -220,7 +220,7 @@ contract('LiquidityBridgeContract', async accounts => { let height = 10; let initialLPBalance = await instance.getBalance(liquidityProviderRskAddress); let peginAmount = quote.val.add(quote.callFee); - + 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)); @@ -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); @@ -292,7 +292,7 @@ contract('LiquidityBridgeContract', async accounts => { 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 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)); @@ -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; diff --git a/test/utils/index.js b/test/utils/index.js index 67f1d0c..de49c77 100644 --- a/test/utils/index.js +++ b/test/utils/index.js @@ -21,6 +21,7 @@ function getTestQuote( let depositConfirmations = 10; let penaltyFee = web3.utils.toBN(0); let callOnRegister = false; + let productFeeAmount = web3.utils.toBN(1); let quote = { fedBtcAddress, lbcAddress, @@ -40,6 +41,7 @@ function getTestQuote( callTime, depositConfirmations, callOnRegister, + productFeeAmount }; return quote; @@ -57,6 +59,7 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { let depositConfirmations = 10; let transferConfirmations = 10; let penaltyFee = web3.utils.toBN(0); + let productFeeAmount = web3.utils.toBN(1); let quote = { lbcAddress, @@ -76,6 +79,7 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { transferTime, expireDate, expireBlock, + productFeeAmount }; 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 17b9add9e74e4c3c0e4cd68e2fb97c1f2e6bc81c Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Mon, 4 Dec 2023 17:21:39 +0100 Subject: [PATCH 3/4] Fixed comments --- ...ctV1.sol => LiquidityBridgeContractV2.sol} | 47 +++++++++---------- contracts/{QuotesV1.sol => QuotesV2.sol} | 9 ++-- migrations/3_upgrade_contracts.js | 26 +++++----- scripts/testLBC.js | 46 ------------------ test/basic.tests.js | 14 +++--- test/miscellaneous.tests.js | 10 ++-- test/penalization.tests.js | 8 ++-- test/refund.tests.js | 8 ++-- 8 files changed, 58 insertions(+), 110 deletions(-) rename contracts/{LiquidityBridgeContractV1.sol => LiquidityBridgeContractV2.sol} (96%) rename contracts/{QuotesV1.sol => QuotesV2.sol} (95%) delete mode 100644 scripts/testLBC.js diff --git a/contracts/LiquidityBridgeContractV1.sol b/contracts/LiquidityBridgeContractV2.sol similarity index 96% rename from contracts/LiquidityBridgeContractV1.sol rename to contracts/LiquidityBridgeContractV2.sol index 038ae69..8c87078 100644 --- a/contracts/LiquidityBridgeContractV1.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.18; pragma experimental ABIEncoderV2; import "./Bridge.sol"; -import "./QuotesV1.sol"; +import "./QuotesV2.sol"; import "./SignatureValidator.sol"; import "./BtcUtils.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; @@ -13,7 +13,7 @@ import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable. @title Contract that assists with the Flyover protocol */ -contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { +contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, ReentrancyGuardUpgradeable { uint16 constant public MAX_CALL_GAS_COST = 35000; uint16 constant public MAX_REFUND_GAS_LIMIT = 2300; @@ -96,7 +96,6 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra address userAddress ); event DaoFeeSent(bytes32 indexed quoteHash, uint256 amount); - event DaoFeeSentError(bytes32 indexed quoteHash, uint256 amount); Bridge public bridge; mapping(address => uint256) private balances; @@ -118,7 +117,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra bool private mainnet; mapping(bytes32 => uint8) private processedQuotes; - mapping(bytes32 => QuotesV1.PegOutQuote) private registeredPegoutQuotes; + mapping(bytes32 => QuotesV2.PegOutQuote) private registeredPegoutQuotes; mapping(bytes32 => PegoutRecord) private pegoutRegistry; uint256 public productFeePercentage; @@ -139,7 +138,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra _; } - function initializeV1( + function initializeV2( uint256 _productFeePercentage, address _daoFeeCollectorAddress ) public { @@ -197,7 +196,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra function getRegisteredPegOutQuote( bytes32 quoteHash - ) external view returns (QuotesV1.PegOutQuote memory) { + ) external view returns (QuotesV2.PegOutQuote memory) { return registeredPegoutQuotes[quoteHash]; } @@ -421,7 +420,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra @return Boolean indicating whether the call was successful */ function callForUser( - QuotesV1.PeginQuote memory quote + QuotesV2.PeginQuote memory quote ) external payable onlyRegistered nonReentrant returns (bool) { require( msg.sender == quote.liquidityProviderRskAddress, @@ -481,7 +480,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra @return The total peg-in amount received from the bridge contract or an error code */ function registerPegIn( - QuotesV1.PeginQuote memory quote, + QuotesV2.PeginQuote memory quote, bytes memory signature, bytes memory btcRawTransaction, bytes memory partialMerkleTree, @@ -582,7 +581,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra // validated in lines 287/298 there's no (negative) error code being returned by the bridge. uint transferredAmount = uint(transferredAmountOrErrorCode); - QuotesV1.checkAgreedAmount(quote, transferredAmount); + QuotesV2.checkAgreedAmount(quote, transferredAmount); if (callRegistry[quoteHash].timestamp > 0) { uint refundAmount; @@ -605,8 +604,6 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra if(daoSuccess) { emit DaoFeeSent(quoteHash, quote.productFeeAmount); - } else { - emit DaoFeeSentError(quoteHash, quote.productFeeAmount); } if (remainingAmount > dust) { @@ -673,7 +670,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra } function depositPegout( // TODO convert to calldata when contract size issues are fixed - QuotesV1.PegOutQuote memory quote, + QuotesV2.PegOutQuote memory quote, bytes memory signature ) external payable { require(isRegisteredForPegout(quote.lpRskAddress), "LBC037"); @@ -687,7 +684,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra "LBC029" ); - QuotesV1.PegOutQuote storage registeredQuote = registeredPegoutQuotes[quoteHash]; + QuotesV2.PegOutQuote storage registeredQuote = registeredPegoutQuotes[quoteHash]; require(pegoutRegistry[quoteHash].completed == false, "LBC064"); require(registeredQuote.lbcAddress == address(0), "LBC028"); @@ -699,7 +696,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra function refundUserPegOut( bytes32 quoteHash ) public nonReentrant { - QuotesV1.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; + QuotesV2.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; require(quote.lbcAddress != address(0), "LBC042"); require( @@ -736,7 +733,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra bytes32[] memory merkleBranchHashes ) public nonReentrant onlyRegisteredForPegout { require(pegoutRegistry[quoteHash].completed == false, "LBC064"); - QuotesV1.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; + QuotesV2.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)); @@ -787,7 +784,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra } function validatePeginDepositAddress( - QuotesV1.PeginQuote memory quote, + QuotesV2.PeginQuote memory quote, bytes memory depositAddress ) external view returns (bool) { bytes32 derivationValue = keccak256( @@ -812,18 +809,18 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra @param quote The quote of the service @return The hash of a quote */ - function hashQuote(QuotesV1.PeginQuote memory quote) public view returns (bytes32) { + function hashQuote(QuotesV2.PeginQuote memory quote) public view returns (bytes32) { return validateAndHashQuote(quote); } function hashPegoutQuote( - QuotesV1.PegOutQuote memory quote + QuotesV2.PegOutQuote memory quote ) public view returns (bytes32) { return validateAndHashPegOutQuote(quote); } function validateAndHashQuote( - QuotesV1.PeginQuote memory quote + QuotesV2.PeginQuote memory quote ) private view returns (bytes32) { require(address(this) == quote.lbcAddress, "LBC051"); require( @@ -848,15 +845,15 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra "LBC071" ); - return keccak256(QuotesV1.encodeQuote(quote)); + return keccak256(QuotesV2.encodeQuote(quote)); } function validateAndHashPegOutQuote( - QuotesV1.PegOutQuote memory quote + QuotesV2.PegOutQuote memory quote ) private view returns (bytes32) { require(address(this) == quote.lbcAddress, "LBC056"); - return keccak256(QuotesV1.encodePegOutQuote(quote)); + return keccak256(QuotesV2.encodePegOutQuote(quote)); } function min(uint a, uint b) private pure returns (uint) { @@ -896,7 +893,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra @return The total peg-in amount received from the bridge contract or an error code */ function registerBridge( - QuotesV1.PeginQuote memory quote, + QuotesV2.PeginQuote memory quote, bytes memory btcRawTransaction, bytes memory partialMerkleTree, uint256 height, @@ -924,7 +921,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra @return Boolean indicating whether the penalty applies */ function shouldPenalizeLP( - QuotesV1.PeginQuote memory quote, + QuotesV2.PeginQuote memory quote, int256 amount, uint256 callTimestamp, uint256 height @@ -972,7 +969,7 @@ contract LiquidityBridgeContractV1 is Initializable, OwnableUpgradeable, Reentra } function shouldPenalizePegOutLP( - QuotesV1.PegOutQuote memory quote, + QuotesV2.PegOutQuote memory quote, bytes32 quoteHash, bytes32 blockHash ) private view returns (bool) { diff --git a/contracts/QuotesV1.sol b/contracts/QuotesV2.sol similarity index 95% rename from contracts/QuotesV1.sol rename to contracts/QuotesV2.sol index 8723038..f8943e0 100644 --- a/contracts/QuotesV1.sol +++ b/contracts/QuotesV2.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.18; -library QuotesV1 { +library QuotesV2 { struct PeginQuote { bytes20 fedBtcAddress; address lbcAddress; @@ -133,11 +133,8 @@ library QuotesV1 { uint transferredAmount ) external pure { uint agreedAmount = 0; - if(quote.productFeeAmount > 0) { - agreedAmount = quote.value + quote.callFee + quote.productFeeAmount; - } else { - agreedAmount = quote.value + quote.callFee; - } + agreedAmount = quote.value + quote.callFee + quote.productFeeAmount; + uint delta = agreedAmount / 10000; // transferred amount should not be lower than (agreed amount - delta), diff --git a/migrations/3_upgrade_contracts.js b/migrations/3_upgrade_contracts.js index 9355c72..7e705ce 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -1,8 +1,8 @@ const { upgradeProxy } = require("@openzeppelin/truffle-upgrades"); const SignatureValidator = artifacts.require("SignatureValidator"); -const QuotesV1 = artifacts.require("QuotesV1"); -const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); +const QuotesV2 = artifacts.require("QuotesV2"); +const LiquidityBridgeContractV2 = artifacts.require('LiquidityBridgeContractV2.sol'); const BtcUtils = artifacts.require("BtcUtils"); const { read, deploy} = require("../config"); @@ -13,39 +13,39 @@ module.exports = async function (deployer, network, accounts) { const signatureValidatorLib = await SignatureValidator.at( config[network]["SignatureValidator"].address ); - await deployer.link(signatureValidatorLib, LiquidityBridgeContractV1); + await deployer.link(signatureValidatorLib, LiquidityBridgeContractV2); - await deployer.deploy(QuotesV1); - const quotesInstance = await QuotesV1.deployed(); - await LiquidityBridgeContractV1.link("QuotesV1", quotesInstance.address); - const quotesLib = await QuotesV1.at( + 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, LiquidityBridgeContractV1); + await deployer.link(quotesLib, LiquidityBridgeContractV2); const btcUtilsLib = await BtcUtils.at( config[network]["BtcUtils"].address ); - await deployer.link(btcUtilsLib, LiquidityBridgeContractV1); + await deployer.link(btcUtilsLib, LiquidityBridgeContractV2); const existing = config[network]["LiquidityBridgeContract"]; console.log('Upgrading contract ', existing.address) const response = await upgradeProxy( existing.address, - LiquidityBridgeContractV1, + LiquidityBridgeContractV2, { deployer, unsafeAllowLinkedLibraries: true } ); let daoFeeCollectorAddress = ''; - if(network === 'ganache' || network === 'rskRegtest') { - daoFeeCollectorAddress = accounts[9]; + if(network === 'ganache' || network === 'rskRegtest' || network === 'test') { + daoFeeCollectorAddress = accounts[8]; } else if(network === '') { daoFeeCollectorAddress = '0x438A3641d53552EFBaB487c5894a78A1434F5aC9'; } - await response.initializeV1(1, daoFeeCollectorAddress); + await response.initializeV2(1, daoFeeCollectorAddress); console.log("Upgraded", response.address); }; diff --git a/scripts/testLBC.js b/scripts/testLBC.js deleted file mode 100644 index 3d8fbad..0000000 --- a/scripts/testLBC.js +++ /dev/null @@ -1,46 +0,0 @@ -const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); - -const quote = { - fedBTCAddr: "2N3JQb9erL1SnAr3NTMrZiPQQ8dcjJp4idV", - lbcAddr: "0xc2A630c053D12D63d32b025082f6Ba268db18300", - lpRSKAddr: "0x7C4890A0f1D4bBf2C669Ac2d1efFa185c505359b", - btcRefundAddr: "2N9sm43yNrTw7kcxFZEupXtYtgh7YTVQorK", - rskRefundAddr: "0xB4BF5fbe395298CEcC00e4d4aDDC62B7192E6f9F", - lpBTCAddr: "mhghaQCHedKZZQuFqSzg6Z3Rf1TqqDEPCc", - callFee: 101368444000000, - penaltyFee: 1000000, - contractAddr: "0xB4BF5fbe395298CEcC00e4d4aDDC62B7192E6f9F", - data: "", - gasLimit: 21000, - nonce: 2424835889795045890, - value: 5000000000000000, - agreementTimestamp: 1701165410, - timeForDeposit: 3600, - lpCallTime: 7200, - confirmations: 2, - callOnRegister: false, - callCost: 1368444000000 -} - -module.exports = async function (callback) { - let accounts = await web3.eth.getAccounts(); - - const proxyAddress = "0x11D50Bcaff24425FC0b8E60a6818C5c455082bE7"; - - const proxyInstance = await LiquidityBridgeContractV1.at(proxyAddress); - - console.log('Getting owner address'); - const ownerAddress = await proxyInstance.owner(); - console.log("Owner address: ", ownerAddress); - - console.log('Getting product fee percentage'); - const prodFee = await proxyInstance.productFeePercentage(); - console.log('Product fee percentage: ', prodFee.toString()); - - console.log('Getting DAO fee collector address'); - const daoFeeCollectorAddr = await proxyInstance.daoFeeCollectorAddress(); - console.log('DAO fee collector Address: ', daoFeeCollectorAddr); - - // invoke callback - callback(); -}; diff --git a/test/basic.tests.js b/test/basic.tests.js index 9bbebea..8f313f6 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); +const LiquidityBridgeContractV2 = artifacts.require("LiquidityBridgeContractV2.sol"); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require("Mock"); const SignatureValidatorMock = artifacts.require("SignatureValidatorMock"); @@ -14,7 +14,7 @@ const expect = chai.expect; const bs58check = require('bs58check') const bs58 = require('bs58'); -contract("LiquidityBridgeContractV1", async (accounts) => { +contract("LiquidityBridgeContractV2.sol", async (accounts) => { let instance; let bridgeMockInstance; let mock; @@ -24,8 +24,8 @@ contract("LiquidityBridgeContractV1", async (accounts) => { const MAX_UINT32 = Math.pow(2, 32) - 1; var providerList = []; before(async () => { - const proxy = await LiquidityBridgeContractV1.deployed(); - instance = await LiquidityBridgeContractV1.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 +41,7 @@ contract("LiquidityBridgeContractV1", 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( @@ -616,7 +616,7 @@ contract("LiquidityBridgeContractV1", async (accounts) => { it("should transfer value for user", async () => { let rskRefundAddress = accounts[2]; - const daoFeeCollectorInitialBalance = await web3.eth.getBalance(accounts[9]); + const daoFeeCollectorInitialBalance = await web3.eth.getBalance(accounts[8]); let destAddr = accounts[1]; let lbcAddress = instance.address; let quote = utils.getTestQuote( @@ -717,7 +717,7 @@ contract("LiquidityBridgeContractV1", async (accounts) => { success: true, quoteHash: quoteHash, }); - const daoFeeCollectorFinalBalance = await web3.eth.getBalance(accounts[9]); + 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.sub(quote.productFeeAmount)); diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index f41184e..5f1613b 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContractV1 = artifacts.require("LiquidityBridgeContractV1"); +const LiquidityBridgeContractV2 = artifacts.require("LiquidityBridgeContractV2.sol"); const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract"); const BridgeMock = artifacts.require("BridgeMock"); const Mock = artifacts.require("Mock"); @@ -12,15 +12,15 @@ const chaiBN = require("chai-bn")(BN); chai.use(chaiBN); const expect = chai.expect; -contract("LiquidityBridgeContractV1", async (accounts) => { +contract("LiquidityBridgeContractV2.sol", async (accounts) => { let instance; let bridgeMockInstance; let mock; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContractV1.deployed(); - instance = await LiquidityBridgeContractV1.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); }); @@ -49,7 +49,7 @@ contract("LiquidityBridgeContractV1", async (accounts) => { } ); - let goodLP = accounts[8]; + let goodLP = accounts[6]; let goodProviderCollateral = web3.utils.toWei("30"); await instance.register.call( "First contract", diff --git a/test/penalization.tests.js b/test/penalization.tests.js index 48e8afe..4c3a0bf 100644 --- a/test/penalization.tests.js +++ b/test/penalization.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); +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('LiquidityBridgeContractV1', async accounts => { +contract('LiquidityBridgeContractV2.sol', async accounts => { let instance; let bridgeMockInstance; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContractV1.deployed(); - instance = await LiquidityBridgeContractV1.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed() }); diff --git a/test/refund.tests.js b/test/refund.tests.js index ca00302..1a3198b 100644 --- a/test/refund.tests.js +++ b/test/refund.tests.js @@ -1,4 +1,4 @@ -const LiquidityBridgeContractV1 = artifacts.require('LiquidityBridgeContractV1'); +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('LiquidityBridgeContractV1', async accounts => { +contract('LiquidityBridgeContractV2.sol', async accounts => { let instance; let bridgeMockInstance; const liquidityProviderRskAddress = accounts[0]; before(async () => { - const proxy = await LiquidityBridgeContractV1.deployed(); - instance = await LiquidityBridgeContractV1.at(proxy.address); + const proxy = await LiquidityBridgeContractV2.deployed(); + instance = await LiquidityBridgeContractV2.at(proxy.address); bridgeMockInstance = await BridgeMock.deployed(); mock = await Mock.deployed(); }); From 8952ae24694662c255922498dae51fd62f8224c9 Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Thu, 14 Dec 2023 11:25:45 +0100 Subject: [PATCH 4/4] Upgraded LBC to testnet --- .openzeppelin/unknown-31.json | 726 ++++++++++++++++++++++++++++++ migrations/3_upgrade_contracts.js | 2 +- scripts/verifyV2Upgrade.js | 41 ++ 3 files changed, 768 insertions(+), 1 deletion(-) create mode 100644 scripts/verifyV2Upgrade.js diff --git a/.openzeppelin/unknown-31.json b/.openzeppelin/unknown-31.json index a60ccec..5f9ba1f 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -355,6 +355,732 @@ } } } + }, + "917d7c175a1885a117991616f0910bede6a05862080c26f877cb2de04b52bfb7": { + "address": "0x4491F58ccA0355bfa073B3dBE6a818f26Df9397a", + "txHash": "0xf5464d79261002494b22ad435d5ea156e573aa1ae38c07017b2c7762cfbe7ae0", + "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]" + } + } + } + }, + "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]" + } + } + } } } } diff --git a/migrations/3_upgrade_contracts.js b/migrations/3_upgrade_contracts.js index 7e705ce..1e0f90c 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -41,7 +41,7 @@ module.exports = async function (deployer, network, accounts) { if(network === 'ganache' || network === 'rskRegtest' || network === 'test') { daoFeeCollectorAddress = accounts[8]; - } else if(network === '') { + } else if(network === 'rskTestnet') { daoFeeCollectorAddress = '0x438A3641d53552EFBaB487c5894a78A1434F5aC9'; } diff --git a/scripts/verifyV2Upgrade.js b/scripts/verifyV2Upgrade.js new file mode 100644 index 0000000..0271a18 --- /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.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(); +};