From a1adc68cda4641abea775fc4b75ca18de76ac040 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Sat, 16 Dec 2023 18:18:20 -0300 Subject: [PATCH 01/12] fix update script --- migrations/3_upgrade_contracts.js | 7 +++++-- scripts/verifyV2Upgrade.js | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/migrations/3_upgrade_contracts.js b/migrations/3_upgrade_contracts.js index 1e0f90c..a07b4fa 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -7,6 +7,9 @@ const BtcUtils = artifacts.require("BtcUtils"); const { read, deploy} = require("../config"); +const FEE_COLLECTOR_TESTNET_ADDRESS = '0x86B6534687A176A476C16083a373fB9Fe4FAb449' +const DAO_FEE_PERCENTAGE = 1 + module.exports = async function (deployer, network, accounts) { let config = read(); @@ -42,10 +45,10 @@ module.exports = async function (deployer, network, accounts) { if(network === 'ganache' || network === 'rskRegtest' || network === 'test') { daoFeeCollectorAddress = accounts[8]; } else if(network === 'rskTestnet') { - daoFeeCollectorAddress = '0x438A3641d53552EFBaB487c5894a78A1434F5aC9'; + daoFeeCollectorAddress = FEE_COLLECTOR_TESTNET_ADDRESS; } - await response.initializeV2(1, daoFeeCollectorAddress); + await response.initializeV2(DAO_FEE_PERCENTAGE, daoFeeCollectorAddress); console.log("Upgraded", response.address); }; diff --git a/scripts/verifyV2Upgrade.js b/scripts/verifyV2Upgrade.js index 0271a18..8eb4750 100644 --- a/scripts/verifyV2Upgrade.js +++ b/scripts/verifyV2Upgrade.js @@ -2,7 +2,7 @@ const json = require("../build/contracts/LiquidityBridgeContractV2.json"); const configJson = require("../config.json"); module.exports = async function (callback) { - const network = config.network; + const network = config.config.network let LBCAddress = ''; if (network) { From 8df6b5365e327d4705bdd7f3a5517590031e5799 Mon Sep 17 00:00:00 2001 From: Guilherme Soares Date: Tue, 19 Dec 2023 10:00:59 +0100 Subject: [PATCH 02/12] LBC upgrade --- .openzeppelin/unknown-31.json | 726 ++++++++++++++++++++++++ contracts/LiquidityBridgeContractV2.sol | 1 - migrations/3_upgrade_contracts.js | 2 +- 3 files changed, 727 insertions(+), 2 deletions(-) diff --git a/.openzeppelin/unknown-31.json b/.openzeppelin/unknown-31.json index 5f9ba1f..69e176c 100644 --- a/.openzeppelin/unknown-31.json +++ b/.openzeppelin/unknown-31.json @@ -1081,6 +1081,732 @@ } } } + }, + "667749700d32e89368f54bae55198f7807a194b576dc98eb820235cac1aecd4f": { + "address": "0x5388Cc8A11c37D49b58eeC2AA680bBAc0fb4610a", + "txHash": "0x6006f61872f5f8f90606ff7596ebea4fc61118111f3186602aec078a33f811c6", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10187_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10187_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } + }, + "00d5830f416690d00e4ac9da12989c5cb8f36a02dffbc779592a7fe979ad5c7e": { + "address": "0x26cc222A1b857fac709CCe873CA144146B3D9e32", + "txHash": "0x6ab555891939fcfd13cd282d6a5f58098aa05d7583ab0de5bd1e63fdd874db17", + "layout": { + "solcVersion": "0.8.18", + "storage": [ + { + "contract": "Initializable", + "label": "_initialized", + "type": "t_uint8", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "contract": "Initializable", + "label": "_initializing", + "type": "t_bool", + "src": "../@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "contract": "ContextUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)50_storage", + "src": "../@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "contract": "OwnableUpgradeable", + "label": "_owner", + "type": "t_address", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "contract": "OwnableUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "_status", + "type": "t_uint256", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "contract": "ReentrancyGuardUpgradeable", + "label": "__gap", + "type": "t_array(t_uint256)49_storage", + "src": "../@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:80" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "bridge", + "type": "t_contract(Bridge)2338", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:100" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "balances", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:101" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "collateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:102" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutCollateral", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:103" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "liquidityProviders", + "type": "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:104" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "callRegistry", + "type": "t_mapping(t_bytes32,t_struct(Registry)6978_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:105" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignationBlockNum", + "type": "t_mapping(t_address,t_uint256)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:106" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minCollateral", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:108" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "minPegIn", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:109" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "rewardP", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:111" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "resignDelayInBlocks", + "type": "t_uint32", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:112" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "dust", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:113" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "providerId", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:114" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "btcBlockTime", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:116" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "mainnet", + "type": "t_bool", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:117" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "processedQuotes", + "type": "t_mapping(t_bytes32,t_uint8)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:119" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "registeredPegoutQuotes", + "type": "t_mapping(t_bytes32,t_struct(PegOutQuote)10186_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:120" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "pegoutRegistry", + "type": "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:121" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "productFeePercentage", + "type": "t_uint256", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:123" + }, + { + "contract": "LiquidityBridgeContractV2", + "label": "daoFeeCollectorAddress", + "type": "t_address", + "src": "../project:/contracts/LiquidityBridgeContractV2.sol:124" + } + ], + "types": { + "t_contract(Bridge)2338": { + "label": "contract Bridge" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)" + }, + "t_address": { + "label": "address" + }, + "t_uint256": { + "label": "uint256" + }, + "t_mapping(t_uint256,t_struct(LiquidityProvider)6996_storage)": { + "label": "mapping(uint256 => struct LiquidityBridgeContractV2.LiquidityProvider)" + }, + "t_struct(LiquidityProvider)6996_storage": { + "label": "struct LiquidityBridgeContractV2.LiquidityProvider", + "members": [ + { + "label": "id", + "type": "t_uint256" + }, + { + "label": "provider", + "type": "t_address" + }, + { + "label": "name", + "type": "t_string_storage" + }, + { + "label": "apiBaseUrl", + "type": "t_string_storage" + }, + { + "label": "status", + "type": "t_bool" + }, + { + "label": "providerType", + "type": "t_string_storage" + } + ] + }, + "t_string_storage": { + "label": "string" + }, + "t_bool": { + "label": "bool" + }, + "t_mapping(t_bytes32,t_struct(Registry)6978_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.Registry)" + }, + "t_bytes32": { + "label": "bytes32" + }, + "t_struct(Registry)6978_storage": { + "label": "struct LiquidityBridgeContractV2.Registry", + "members": [ + { + "label": "timestamp", + "type": "t_uint32" + }, + { + "label": "success", + "type": "t_bool" + } + ] + }, + "t_uint32": { + "label": "uint32" + }, + "t_mapping(t_bytes32,t_uint8)": { + "label": "mapping(bytes32 => uint8)" + }, + "t_uint8": { + "label": "uint8" + }, + "t_mapping(t_bytes32,t_struct(PegOutQuote)10186_storage)": { + "label": "mapping(bytes32 => struct QuotesV2.PegOutQuote)" + }, + "t_struct(PegOutQuote)10186_storage": { + "label": "struct QuotesV2.PegOutQuote", + "members": [ + { + "label": "lbcAddress", + "type": "t_address" + }, + { + "label": "lpRskAddress", + "type": "t_address" + }, + { + "label": "btcRefundAddress", + "type": "t_bytes_storage" + }, + { + "label": "rskRefundAddress", + "type": "t_address" + }, + { + "label": "lpBtcAddress", + "type": "t_bytes_storage" + }, + { + "label": "callFee", + "type": "t_uint256" + }, + { + "label": "penaltyFee", + "type": "t_uint256" + }, + { + "label": "nonce", + "type": "t_int64" + }, + { + "label": "deposityAddress", + "type": "t_bytes_storage" + }, + { + "label": "value", + "type": "t_uint256" + }, + { + "label": "agreementTimestamp", + "type": "t_uint32" + }, + { + "label": "depositDateLimit", + "type": "t_uint32" + }, + { + "label": "depositConfirmations", + "type": "t_uint16" + }, + { + "label": "transferConfirmations", + "type": "t_uint16" + }, + { + "label": "transferTime", + "type": "t_uint32" + }, + { + "label": "expireDate", + "type": "t_uint32" + }, + { + "label": "expireBlock", + "type": "t_uint32" + }, + { + "label": "productFeeAmount", + "type": "t_uint256" + } + ] + }, + "t_bytes_storage": { + "label": "bytes" + }, + "t_int64": { + "label": "int64" + }, + "t_uint16": { + "label": "uint16" + }, + "t_mapping(t_bytes32,t_struct(PegoutRecord)6983_storage)": { + "label": "mapping(bytes32 => struct LiquidityBridgeContractV2.PegoutRecord)" + }, + "t_struct(PegoutRecord)6983_storage": { + "label": "struct LiquidityBridgeContractV2.PegoutRecord", + "members": [ + { + "label": "depositTimestamp", + "type": "t_uint256" + }, + { + "label": "completed", + "type": "t_bool" + } + ] + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]" + } + } + } } } } diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index 8c87078..a2de795 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -598,7 +598,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra uint remainingAmount = transferredAmount - refundAmount; (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{ - gas: MAX_REFUND_GAS_LIMIT, value: quote.productFeeAmount }(""); diff --git a/migrations/3_upgrade_contracts.js b/migrations/3_upgrade_contracts.js index 1e0f90c..ee69f41 100644 --- a/migrations/3_upgrade_contracts.js +++ b/migrations/3_upgrade_contracts.js @@ -42,7 +42,7 @@ module.exports = async function (deployer, network, accounts) { if(network === 'ganache' || network === 'rskRegtest' || network === 'test') { daoFeeCollectorAddress = accounts[8]; } else if(network === 'rskTestnet') { - daoFeeCollectorAddress = '0x438A3641d53552EFBaB487c5894a78A1434F5aC9'; + daoFeeCollectorAddress = '0x86B6534687A176A476C16083a373fB9Fe4FAb449'; } await response.initializeV2(1, daoFeeCollectorAddress); From 5ba9657e47a9c811f4b57b4804e34676770b4976 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 21 Dec 2023 15:24:45 -0300 Subject: [PATCH 03/12] fix test broken when adding productFeeAmount --- test/basic.tests.js | 6 +++--- test/miscellaneous.tests.js | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/test/basic.tests.js b/test/basic.tests.js index 8f313f6..199ba8f 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1771,7 +1771,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callOnRegister: false, productFeeAmount: BigInt("6000000000000000") }, - address: '2NB9Rp6DxS4WXefGoyNLa5rQWkcQtUM1FmF' + address: '2NFWum5hWgScV2QPPEfFGF1hWV6EcX5Gicg' }, { quote: { @@ -1795,7 +1795,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callOnRegister: false, productFeeAmount: BigInt("7000000000000000") }, - address: '2Mvbn9JQWjoS3SCBuxf1KTTkLw49WYjrkLx' + address: '2Mwk75Usb1GyQB2AtFLmr3iqrET3ByP9M9j' }, { quote: { @@ -1819,7 +1819,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callOnRegister: false, productFeeAmount: BigInt("8000000000000000") }, - address: '2N2dEn75BJDgUA4mnfZyKG9qX99ofzKizeC' + address: '2N56hAFac2aULZNw9SQfjdjLhT3qHtSrnFo' } ] diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index 5f1613b..9260d7f 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -170,15 +170,18 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let data = "0x00"; let callFee = 1; + const productFeeAmount = 1; let gasLimit = 150000; let nonce = 0; let delta = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) + .add(web3.utils.toBN(productFeeAmount)) .div(web3.utils.toBN(10000)); let peginAmount = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) + .add(web3.utils.toBN(productFeeAmount)) .sub(delta); let lbcAddress = instance.address; let agreementTime = 1661788988; @@ -187,8 +190,6 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let depositConfirmations = 10; let penaltyFee = 0; let callOnRegister = false; - let productFeeAmount = web3.utils.toBN(1); - peginAmount.add(productFeeAmount); let quote = [ fedBtcAddress, lbcAddress, @@ -238,7 +239,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { liquidityProviderRskAddress ); - await instance.callForUser(quote, { value: val.add(productFeeAmount) }); + await instance.callForUser(quote, { value: val }); let currentLPBalance = await instance.getBalance( liquidityProviderRskAddress @@ -280,7 +281,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { .sub(web3.utils.toBN(initialLPBalance)); expect(peginAmount).to.be.a.bignumber.eq(amount); expect(usrBal).to.be.a.bignumber.eq(val); - expect(lbcBal).to.be.a.bignumber.eq(peginAmount); + expect(lbcBal).to.be.a.bignumber.eq(peginAmount.sub(web3.utils.toBN(productFeeAmount))); expect(lpBal).to.be.a.bignumber.eq(peginAmount); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); }); From f6dd197f2e44f49b3411440a2efff97a20c18180 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 21 Dec 2023 15:27:34 -0300 Subject: [PATCH 04/12] enforce test to run on each environment branch --- .github/workflows/ci.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 755206a..dd636e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,10 @@ name: CI # Controls when the workflow will run on: - # Triggers the workflow on push or pull request events but only for the master branch push: - branches: [ master ] + branches: [ master, Stable-Test, QA-Test ] pull_request: - branches: [ master ] + branches: [ master, Stable-Test, QA-Test ] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -23,12 +22,12 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - name: Use Node.js 16.x - uses: actions/setup-node@v1 + - name: Use Node.js 19.6.0 + uses: actions/setup-node@v3 with: - node-version: 16.x + node-version: '19.6.0' - name: Install truffle run: npm install -g truffle From d2b5b2477189e2d26bcc6b1c576aa3265ff2c25c Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 21 Dec 2023 16:12:51 -0300 Subject: [PATCH 05/12] fix time dependant test to run in pipeline --- test/basic.tests.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/basic.tests.js b/test/basic.tests.js index 199ba8f..dbb3f59 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -816,7 +816,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { // configure mocked block on mockBridge const firstConfirmationTime = utils.reverseHexBytes( - web3.utils.toHex(quote.agreementTimestamp + 300).substring(2) + web3.utils.toHex(quote.agreementTimestamp + 100).substring(2) ); const firstHeader = "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + From 6bacc1277d8ba64be6217f72727029414fa75ba3 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 21 Dec 2023 16:40:25 -0300 Subject: [PATCH 06/12] Migrate to BtcUtils published library --- contracts/BtcUtils.sol | 179 ------------------------ contracts/LiquidityBridgeContract.sol | 6 +- contracts/LiquidityBridgeContractV2.sol | 40 +----- package-lock.json | 13 +- package.json | 1 + test/basic.tests.js | 8 +- test/miscellaneous.tests.js | 32 ----- 7 files changed, 25 insertions(+), 254 deletions(-) delete mode 100644 contracts/BtcUtils.sol diff --git a/contracts/BtcUtils.sol b/contracts/BtcUtils.sol deleted file mode 100644 index 044b87f..0000000 --- a/contracts/BtcUtils.sol +++ /dev/null @@ -1,179 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.18; - -/** - * @title BtcUtils - * @notice This library is based in this document - * https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format - */ -library BtcUtils { - uint8 private constant MAX_COMPACT_SIZE_LENGTH = 252; - uint8 private constant MAX_BYTES_USED_FOR_COMPACT_SIZE = 8; - uint8 private constant OUTPOINT_SIZE = 36; - uint8 private constant OUTPUT_VALUE_SIZE = 8; - uint8 private constant PUBKEY_HASH_SIZE = 20; - uint8 private constant PUBKEY_HASH_START = 3; - uint8 private constant CHECK_BYTES_FROM_HASH = 4; - - - struct TxRawOutput { - uint64 value; - bytes pkScript; - uint256 scriptSize; - uint256 totalSize; - } - - function parseCompactSizeInt(uint sizePosition, bytes memory array) private pure returns(uint64, uint16) { - require(array.length > sizePosition, "Size position can't be bigger than array"); - uint8 maxSize = uint8(array[sizePosition]); - if (maxSize == 0) { - return (0, 1); - } else if (maxSize <= MAX_COMPACT_SIZE_LENGTH) { - return (maxSize, 1); - } - - uint compactSizeBytes = 2 ** (maxSize - MAX_COMPACT_SIZE_LENGTH); - require(compactSizeBytes <= MAX_BYTES_USED_FOR_COMPACT_SIZE, "unsupported compact size length"); - - uint64 result = uint64(calculateLittleEndianFragment(sizePosition + 1, sizePosition + compactSizeBytes, array)); - return (result, uint16(compactSizeBytes) + 1); - } - - function getOutputs(bytes calldata rawTx) public pure returns (TxRawOutput[] memory) { - uint currentPosition = 4; - - if (rawTx[4] == 0x00 && rawTx[5] == 0x01) { // if its segwit, skip marker and flag - currentPosition = 6; - } - - (uint64 inputCount, uint16 inputCountSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += inputCountSize; - - uint64 scriptLarge; - uint16 scriptLargeSize; - for (uint64 i = 0; i < inputCount; i++) { - currentPosition += OUTPOINT_SIZE; - (scriptLarge, scriptLargeSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += scriptLarge + scriptLargeSize + 4; - } - - (uint64 outputCount, uint16 outputCountSize) = parseCompactSizeInt(currentPosition, rawTx); - currentPosition += outputCountSize; - - TxRawOutput[] memory result = new TxRawOutput[](outputCount); - for (uint i = 0; i < outputCount; i++) { - result[i] = extractRawOutput(currentPosition, rawTx); - currentPosition += result[i].totalSize; - } - return result; - } - - function calculateLittleEndianFragment(uint fragmentStart, uint fragmentEnd, bytes memory array) - private pure returns (uint) { - require( - fragmentStart < array.length && fragmentEnd < array.length, - "Range can't be bigger than array" - ); - uint result = 0; - for (uint i = fragmentStart; i <= fragmentEnd; i++) { - result += uint8(array[i]) * uint64(2 ** (8 * (i - (fragmentStart)))); - } - return result; - } - - function extractRawOutput(uint position, bytes memory rawTx) private pure returns (TxRawOutput memory) { - TxRawOutput memory result; - result.value = uint64(calculateLittleEndianFragment(position, position + OUTPUT_VALUE_SIZE, rawTx)); - position += OUTPUT_VALUE_SIZE; - - (uint64 scriptLength, uint16 scriptLengthSize) = parseCompactSizeInt(position, rawTx); - position += scriptLengthSize; - - bytes memory pkScript = new bytes(scriptLength); - for (uint64 i = 0; i < scriptLength; i++) { - pkScript[i] = rawTx[position + i]; - } - result.pkScript = pkScript; - result.scriptSize = scriptLength; - result.totalSize = OUTPUT_VALUE_SIZE + scriptLength + scriptLengthSize; - return result; - } - - function parsePayToAddressScript(bytes calldata outputScript, bool mainnet) public pure returns (bytes memory) { - require(outputScript.length == 25, "Script has not the required length"); - require( - outputScript[0] == 0x76 && // OP_DUP - outputScript[1] == 0xa9 && // OP_HASH160 - outputScript[2] == 0x14 && // pubKeyHashSize, should be always 14 (20B) - outputScript[23] == 0x88 && // OP_EQUALVERIFY - outputScript[24] == 0xac, // OP_CHECKSIG - "Script has not the required structure" - ); - - bytes memory destinationAddress = new bytes(PUBKEY_HASH_SIZE); - for(uint8 i = PUBKEY_HASH_START; i < PUBKEY_HASH_SIZE + PUBKEY_HASH_START; i++) { - destinationAddress[i - PUBKEY_HASH_START] = outputScript[i]; - } - - uint8 versionByte = mainnet? 0x00 : 0x6f; - bytes memory result = addVersionByte(bytes1(versionByte), destinationAddress); - - return result; - } - - function parseOpReturnOuput(bytes calldata outputScript) public pure returns (bytes memory) { - require(outputScript[0] == 0x6a, "Not OP_RETURN"); - require( - outputScript.length > 2 && outputScript.length < 85, - "Data out of bounds" - ); - - bytes memory message = new bytes(uint8(outputScript[1])); - for (uint8 i = 0; i < message.length; i++) { - // the addition of two is because the two first bytes correspond to - // the op_return opcode and the length of the message - message[i] = outputScript[i + 2]; - } - return message; - } - - function addVersionByte(bytes1 versionByte, bytes memory source) private pure returns (bytes memory) { - bytes memory dataWithVersion = new bytes(source.length + 1); - dataWithVersion[0] = versionByte; - - uint8 i; - for (i = 0; i < source.length; i++) { - dataWithVersion[i + 1] = source[i]; - } - - return dataWithVersion; - } - - function hashBtcTx(bytes calldata btcTx) public pure returns (bytes32) { - bytes memory doubleSha256 = abi.encodePacked(sha256(abi.encodePacked(sha256(btcTx)))); - bytes1 aux; - for (uint i = 0; i < 16; i++) { - aux = doubleSha256[i]; - doubleSha256[i] = doubleSha256[31 - i]; - doubleSha256[31 - i] = aux; - } - - bytes32 result; - assembly { - result := mload(add(doubleSha256, 32)) - } - return result; - } - - function validateP2SHAdress(bytes memory p2sh, bytes calldata script, bool mainnet) public pure returns (bool) { - return p2sh.length == 25 && keccak256(p2sh) == keccak256(getP2SHAddressFromScript(script, mainnet)); - } - - function getP2SHAddressFromScript(bytes calldata script, bool mainnet) public pure returns (bytes memory) { - bytes20 scriptHash = ripemd160(abi.encodePacked(sha256(script))); - uint8 versionByte = mainnet ? 0x5 : 0xc4; - bytes memory versionAndHash = bytes.concat(bytes1(versionByte), scriptHash); - bytes4 checksum = bytes4(sha256(abi.encodePacked(sha256(versionAndHash)))); - return bytes.concat(versionAndHash, checksum); - } -} \ No newline at end of file diff --git a/contracts/LiquidityBridgeContract.sol b/contracts/LiquidityBridgeContract.sol index b431846..49bd660 100644 --- a/contracts/LiquidityBridgeContract.sol +++ b/contracts/LiquidityBridgeContract.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; import "./Bridge.sol"; import "./Quotes.sol"; import "./SignatureValidator.sol"; -import "./BtcUtils.sol"; +import "@rsksmart/btc-transaction-solidity-helper/contracts/BtcUtils.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; @@ -749,7 +749,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc Quotes.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; require(quote.lbcAddress != address(0), "LBC042"); BtcUtils.TxRawOutput[] memory outputs = BtcUtils.getOutputs(btcTx); - bytes32 txQuoteHash = abi.decode(BtcUtils.parseOpReturnOuput(outputs[QUOTE_HASH_OUTPUT].pkScript), (bytes32)); + bytes32 txQuoteHash = abi.decode(BtcUtils.parseNullDataScript(outputs[QUOTE_HASH_OUTPUT].pkScript), (bytes32)); require(quoteHash == txQuoteHash, "LBC069"); require(msg.sender == quote.lpRskAddress, "LBC048"); require( @@ -762,7 +762,7 @@ contract LiquidityBridgeContract is Initializable, OwnableUpgradeable, Reentranc "LBC049" ); require(quote.value <= outputs[PAY_TO_ADDRESS_OUTPUT].value * (10**10), "LBC067"); // satoshi to wei - bytes memory btcTxDestination = BtcUtils.parsePayToAddressScript(outputs[PAY_TO_ADDRESS_OUTPUT] + bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT] .pkScript, mainnet); require(keccak256(quote.deposityAddress) == keccak256(btcTxDestination), "LBC068"); diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index a2de795..3178a05 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; import "./Bridge.sol"; import "./QuotesV2.sol"; import "./SignatureValidator.sol"; -import "./BtcUtils.sol"; +import "@rsksmart/btc-transaction-solidity-helper/contracts/BtcUtils.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; @@ -735,7 +735,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra 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)); + bytes32 txQuoteHash = abi.decode(BtcUtils.parseNullDataScript(outputs[QUOTE_HASH_OUTPUT].pkScript), (bytes32)); require(quoteHash == txQuoteHash, "LBC069"); require(msg.sender == quote.lpRskAddress, "LBC048"); require( @@ -748,7 +748,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra "LBC049" ); require(quote.value <= outputs[PAY_TO_ADDRESS_OUTPUT].value * (10**10), "LBC067"); // satoshi to wei - bytes memory btcTxDestination = BtcUtils.parsePayToAddressScript(outputs[PAY_TO_ADDRESS_OUTPUT] + bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT] .pkScript, mainnet); require(keccak256(quote.deposityAddress) == keccak256(btcTxDestination), "LBC068"); @@ -934,7 +934,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra .getBtcBlockchainBlockHeaderByHeight(height); require(firstConfirmationHeader.length > 0, "Invalid block height"); - uint256 firstConfirmationTimestamp = getBtcBlockTimestamp( + uint256 firstConfirmationTimestamp = BtcUtils.getBtcBlockTimestamp( firstConfirmationHeader ); @@ -956,7 +956,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra ); require(nConfirmationsHeader.length > 0, "LBC058"); - uint256 nConfirmationsTimestamp = getBtcBlockTimestamp( + uint256 nConfirmationsTimestamp = BtcUtils.getBtcBlockTimestamp( nConfirmationsHeader ); @@ -975,7 +975,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra bytes memory firstConfirmationHeader = bridge.getBtcBlockchainBlockHeaderByHash(blockHash); require(firstConfirmationHeader.length > 0, "LBC059"); - uint256 firstConfirmationTimestamp = getBtcBlockTimestamp(firstConfirmationHeader); + uint256 firstConfirmationTimestamp = BtcUtils.getBtcBlockTimestamp(firstConfirmationHeader); // penalize if the transfer was not made on time if (firstConfirmationTimestamp > pegoutRegistry[quoteHash].depositTimestamp + @@ -990,32 +990,4 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra 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/package-lock.json b/package-lock.json index 438abc2..7324681 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@openzeppelin/contracts": "^4.8.0", "@openzeppelin/contracts-upgradeable": "^4.8.2", + "@rsksmart/btc-transaction-solidity-helper": "^0.0.2", "@truffle/hdwallet-provider": "^2.1.3", "chai": "^4.3.4", "chai-bn": "^0.3.0", @@ -3070,6 +3071,11 @@ "dev": true, "peer": true }, + "node_modules/@rsksmart/btc-transaction-solidity-helper": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@rsksmart/btc-transaction-solidity-helper/-/btc-transaction-solidity-helper-0.0.2.tgz", + "integrity": "sha512-1hpw6YSwEDE+9wSLBrkQDPdVcaIAmI+MRHtj4PVWHq9W45phSiDP6mO8bO8V2hpk4FaTq/2nHGX+m/RG1fyN/A==" + }, "node_modules/@rsksmart/pmt-builder": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@rsksmart/pmt-builder/-/pmt-builder-3.0.0.tgz", @@ -10148,7 +10154,6 @@ "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", "dev": true, - "hasInstallScript": true, "optional": true, "peer": true, "dependencies": { @@ -10534,7 +10539,6 @@ "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", "dev": true, - "hasInstallScript": true, "optional": true, "peer": true, "dependencies": { @@ -21751,6 +21755,11 @@ "dev": true, "peer": true }, + "@rsksmart/btc-transaction-solidity-helper": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@rsksmart/btc-transaction-solidity-helper/-/btc-transaction-solidity-helper-0.0.2.tgz", + "integrity": "sha512-1hpw6YSwEDE+9wSLBrkQDPdVcaIAmI+MRHtj4PVWHq9W45phSiDP6mO8bO8V2hpk4FaTq/2nHGX+m/RG1fyN/A==" + }, "@rsksmart/pmt-builder": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@rsksmart/pmt-builder/-/pmt-builder-3.0.0.tgz", diff --git a/package.json b/package.json index c79fe67..b6b9462 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "dependencies": { "@openzeppelin/contracts": "^4.8.0", "@openzeppelin/contracts-upgradeable": "^4.8.2", + "@rsksmart/btc-transaction-solidity-helper": "^0.0.2", "@truffle/hdwallet-provider": "^2.1.3", "chai": "^4.3.4", "chai-bn": "^0.3.0", diff --git a/test/basic.tests.js b/test/basic.tests.js index dbb3f59..9b37ed0 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -1352,16 +1352,16 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const firstRawTX = "0x0100000001013503c427ba46058d2d8ac9221a2f6fd50734a69f19dae65420191e3ada2d40000000006a47304402205d047dbd8c49aea5bd0400b85a57b2da7e139cec632fb138b7bee1d382fd70ca02201aa529f59b4f66fdf86b0728937a91a40962aedd3f6e30bce5208fec0464d54901210255507b238c6f14735a7abe96a635058da47b05b61737a610bef757f009eea2a4ffffffff0200879303000000001976a9143c5f66fe733e0ad361805b3053f23212e5755c8d88ac0000000000000000426a403938343934346435383039323135366335613139643936356239613735383530326536646263326439353337333135656266343839373336333134656233343700000000"; const firstTxOutputs = await btcUtils.getOutputs(firstRawTX); - const firstQuoteHash = web3.utils.hexToAscii(await btcUtils.parseOpReturnOuput(firstTxOutputs[1].pkScript)); - const firstDestinationAddress = await btcUtils.parsePayToAddressScript(firstTxOutputs[0].pkScript, false); + const firstQuoteHash = web3.utils.hexToAscii(await btcUtils.parseNullDataScript(firstTxOutputs[1].pkScript)); + const firstDestinationAddress = await btcUtils.parsePayToPubKeyHash(firstTxOutputs[0].pkScript, false); const firstValue = firstTxOutputs[0].value; const firstHash = await btcUtils.hashBtcTx(firstRawTX); const secondRawTX = "0x01000000010178a1cf4f2f0cb1607da57dcb02835d6aa8ef9f06be3f74cafea54759a029dc000000006a473044022070a22d8b67050bee57564279328a2f7b6e7f80b2eb4ecb684b879ea51d7d7a31022057fb6ece52c23ecf792e7597448c7d480f89b77a8371dca4700a18088f529f6a012103ef81e9c4c38df173e719863177e57c539bdcf97289638ec6831f07813307974cffffffff02801d2c04000000001976a9143c5f66fe733e0ad361805b3053f23212e5755c8d88ac0000000000000000426a406539346138393731323632396262633966636364316630633034613237386330653130353265623736323666393365396137663130363762343036663035373600000000"; const secondTxOutputs = await btcUtils.getOutputs(secondRawTX); - const secondQuoteHash = web3.utils.hexToAscii(await btcUtils.parseOpReturnOuput(secondTxOutputs[1].pkScript)); - const secondDestinationAddress = await btcUtils.parsePayToAddressScript(secondTxOutputs[0].pkScript, true); + const secondQuoteHash = web3.utils.hexToAscii(await btcUtils.parseNullDataScript(secondTxOutputs[1].pkScript)); + const secondDestinationAddress = await btcUtils.parsePayToPubKeyHash(secondTxOutputs[0].pkScript, true); const secondValue = secondTxOutputs[0].value; const secondHash = await btcUtils.hashBtcTx(secondRawTX); diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index 9260d7f..32fa180 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -419,36 +419,4 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { instance.initialize(bridgeMockInstance.address, MINIMUM_COLLATERAL, 1, 101, RESIGN_DELAY_BLOCKS, 1, 1, false) ); }); - - it("should extract timestamp from btc block header", async () => { - const btcHeader = - "0x0080cf2a0857bdec9d66f5feb52d00d5061ff02a904112d9b0cd1ac401000000000000003d2d2b5733c820a1f07ce6e0acd2ea47f27016b49ccb405b1e3e5786f8ae962e3ce30c63bc292d1919856362"; - - let timestamp = await instance.getBtcBlockTimestamp(btcHeader); - expect(timestamp).to.be.a.bignumber.eq(web3.utils.toBN(1661788988)); - - const btcHeader2 = "0x" + "00".repeat(68) + "12345678" + "00".repeat(8); - - let timestamp2 = await instance.getBtcBlockTimestamp(btcHeader2); - expect(timestamp2).to.be.a.bignumber.eq(web3.utils.toBN("0x78563412")); - }); - - it("should fail to extract timestamp from btc block header with invalid length", async () => { - const btcHeaderEmpty = "0x"; - const btcHeader79 = "0x" + "00".repeat(79); - const btcHeader81 = "0x" + "00".repeat(81); - - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeaderEmpty), - "LBC061" - ); - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeader79), - "LBC061" - ); - await truffleAssertions.reverts( - instance.getBtcBlockTimestamp(btcHeader81), - "LBC061" - ); - }); }); From 10f96a6d8479a6692270897c537f98f80fafd934 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 21 Dec 2023 18:55:59 -0300 Subject: [PATCH 07/12] fix unsafe integer calculation in test --- test/basic.tests.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/basic.tests.js b/test/basic.tests.js index 9b37ed0..b726697 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -854,11 +854,11 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { liquidityProviderRskAddress ); const usedInGas = refund.receipt.gasUsed * refund.receipt.effectiveGasPrice; - const refundedAmount = +quote.value + +quote.callFee; + const refundedAmount = quote.value.add(quote.callFee); const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[9]); - expect(+lpBalanceAfter).to.be.eq( - +lpBalanceBefore + refundedAmount - usedInGas + expect(lpBalanceAfter).to.be.a.bignumber.eq( + web3.utils.toBN(lpBalanceBefore).add(refundedAmount).sub(web3.utils.toBN(usedInGas)) ); expect(+daoFeeCollectorBefore + +quote.productFeeAmount.toString()).to.be.eq(+daoFeeCollectorAfter) truffleAssertions.eventEmitted(refund, "PegOutRefunded"); From 8751b047392948fbd9343422e460fbb4c02effcb Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Tue, 2 Jan 2024 13:10:02 -0300 Subject: [PATCH 08/12] update to avoid tx to the fee collector if productFee is 0 --- contracts/LiquidityBridgeContractV2.sol | 42 +++---- test/basic.tests.js | 140 +++++++++++++++++++++--- test/penalization.tests.js | 2 +- test/refund.tests.js | 8 +- test/utils/index.js | 2 +- 5 files changed, 150 insertions(+), 44 deletions(-) diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index 3178a05..5b37e7e 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -55,7 +55,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra } 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); @@ -77,12 +76,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra 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, @@ -133,11 +126,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra _; } - modifier onlyEoa() { - require(tx.origin == msg.sender, "LBC003"); - _; - } - function initializeV2( uint256 _productFeePercentage, address _daoFeeCollectorAddress @@ -230,7 +218,8 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra string memory _apiBaseUrl, bool _status, string memory _providerType - ) external payable onlyEoa returns (uint) { + ) external payable returns (uint) { + require(tx.origin == msg.sender, "LBC003"); //require(collateral[msg.sender] == 0, "Already registered"); validateRegisterParameters( _name, @@ -597,11 +586,11 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra increaseBalance(quote.liquidityProviderRskAddress, refundAmount); uint remainingAmount = transferredAmount - refundAmount; - (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{ - value: quote.productFeeAmount - }(""); - - if(daoSuccess) { + if (quote.productFeeAmount > 0) { + (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{ + value: quote.productFeeAmount + }(""); + require(daoSuccess, "LBC074"); emit DaoFeeSent(quoteHash, quote.productFeeAmount); } @@ -673,7 +662,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra bytes memory signature ) external payable { require(isRegisteredForPegout(quote.lpRskAddress), "LBC037"); - require(quote.value + quote.callFee <= msg.value, "LBC063"); + require(quote.value + quote.callFee + quote.productFeeAmount <= msg.value, "LBC063"); require(block.timestamp <= quote.depositDateLimit, "LBC065"); require(block.timestamp <= quote.expireDate, "LBC046"); require(block.number <= quote.expireBlock, "LBC047"); @@ -772,10 +761,13 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra }(""); require(sent, "LBC050"); - (bool sentDAO,) = payable(daoFeeCollectorAddress).call{ - value: quote.productFeeAmount - }(""); - require(sentDAO, "LBC074"); + if (quote.productFeeAmount > 0) { + (bool sentDAO,) = payable(daoFeeCollectorAddress).call{ + value: quote.productFeeAmount + }(""); + require(sentDAO, "LBC074"); + emit DaoFeeSent(quoteHash, quote.productFeeAmount); + } delete registeredPegoutQuotes[txQuoteHash]; pegoutRegistry[txQuoteHash].completed = true; @@ -836,7 +828,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra "LBC054" ); require( - quote.value + quote.callFee >= minPegIn, + quote.value + quote.callFee + quote.productFeeAmount >= minPegIn, "LBC055" ); require( @@ -926,7 +918,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra uint256 height ) private view returns (bool) { // do not penalize if deposit amount is insufficient - if (amount > 0 && uint256(amount) < quote.value + quote.callFee) { + if (amount > 0 && uint256(amount) < quote.value + quote.callFee + quote.productFeeAmount) { return false; } diff --git a/test/basic.tests.js b/test/basic.tests.js index b726697..2e83e4b 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -21,7 +21,6 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let signatureValidatorInstance; let btcUtils; const liquidityProviderRskAddress = accounts[0]; - const MAX_UINT32 = Math.pow(2, 32) - 1; var providerList = []; before(async () => { const proxy = await LiquidityBridgeContractV2.deployed(); @@ -591,6 +590,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { rskRefundAddress, web3.utils.toBN(0) ); + quote.productFeeAmount = web3.utils.toBN(0) await truffleAssertions.reverts( instance.hashQuote.call(utils.asArray(quote)), @@ -656,10 +656,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { await bridgeMockInstance.setPegin(quoteHash, { value: peginAmount }); await bridgeMockInstance.setHeader(height, firstHeader); - await bridgeMockInstance.setHeader( - height + quote.depositConfirmations - 1, - nHeader - ); + await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); let initialLPDeposit = await instance.getCollateral( liquidityProviderRskAddress ); @@ -727,6 +724,69 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { web3.utils.toBN(daoFeeCollectorInitialBalance).add(quote.productFeeAmount)); }); + it("Should not generate transaction to DAO when product fee is 0 in registerPegIn", async () => { + const daoFeeCollectorInitialBalance = await web3.eth.getBalance(accounts[8]); + let quote = utils.getTestQuote( + instance.address, + accounts[1], + "0x00", + liquidityProviderRskAddress, + accounts[2], + web3.utils.toBN(100) + ); + quote.productFeeAmount = web3.utils.toBN(0) + + let btcRawTransaction = "0x101"; + let partialMerkleTree = "0x202"; + let height = 10; + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let quoteHash = await instance.hashQuote(utils.asArray(quote)); + let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); + let firstConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTime + 300).substring(2) + ); + let nConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTime + 600).substring(2) + ); + let firstHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + firstConfirmationTime + + "0000000000000000"; + let nHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + nConfirmationTime + + "0000000000000000"; + + await bridgeMockInstance.setPegin(quoteHash, { value: peginAmount }); + await bridgeMockInstance.setHeader(height, firstHeader); + await bridgeMockInstance.setHeader(height + quote.depositConfirmations - 1, nHeader); + + await instance.callForUser(utils.asArray(quote), { value: quote.val }); + + await instance.registerPegIn.call( + utils.asArray(quote), + signature, + btcRawTransaction, + partialMerkleTree, + height + ); + + const registerPegin = await instance.registerPegIn( + utils.asArray(quote), + signature, + btcRawTransaction, + partialMerkleTree, + height + ); + + const daoFeeCollectorFinalBalance = await web3.eth.getBalance(accounts[8]); + expect(daoFeeCollectorFinalBalance).to.be.a.bignumber.eq(web3.utils.toBN(daoFeeCollectorInitialBalance)); + truffleAssertions.eventNotEmitted(registerPegin, "DaoFeeSent", { + quoteHash: quoteHash, + amount: quote.productFeeAmount + }); + }); + it("should resign", async () => { let lbcAddress = instance.address; const lpAddress = accounts[5]; @@ -789,7 +849,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { value: web3.utils.toWei("30000", "wei"), from: liquidityProviderRskAddress, }); - const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[9]); + const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[8]); const blockHeaderHash = "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; const partialMerkleTree = @@ -826,7 +886,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -855,15 +915,69 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { ); const usedInGas = refund.receipt.gasUsed * refund.receipt.effectiveGasPrice; const refundedAmount = quote.value.add(quote.callFee); - const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[9]); + const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[8]); expect(lpBalanceAfter).to.be.a.bignumber.eq( web3.utils.toBN(lpBalanceBefore).add(refundedAmount).sub(web3.utils.toBN(usedInGas)) ); - expect(+daoFeeCollectorBefore + +quote.productFeeAmount.toString()).to.be.eq(+daoFeeCollectorAfter) + expect(web3.utils.toBN(daoFeeCollectorBefore).add(quote.productFeeAmount)).to.be.a.bignumber.eq(web3.utils.toBN(daoFeeCollectorAfter)) truffleAssertions.eventEmitted(refund, "PegOutRefunded"); }); + it("Should not generate transaction to DAO when product fee is 0 in refundPegOut", async () => { + await instance.addPegoutCollateral({ + value: web3.utils.toWei("30000", "wei"), + from: liquidityProviderRskAddress, + }); + const daoFeeCollectorBefore = await web3.eth.getBalance(accounts[8]); + const blockHeaderHash = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326"; + const partialMerkleTree = + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb426"; + const merkleBranchHashes = [ + "0x02327049330a25d4d17e53e79f478cbb79c53a509679b1d8a1505c5697afb326", + ]; + + let quote = utils.getTestPegOutQuote( + instance.address, //lbc address + liquidityProviderRskAddress, + accounts[2], + web3.utils.toBN(1) + ); + quote.transferConfirmations = 0; + quote.productFeeAmount = web3.utils.toBN(0); + + // configure mocked block on mockBridge + const firstConfirmationTime = utils.reverseHexBytes( + web3.utils.toHex(quote.agreementTimestamp + 100).substring(2) + ); + const firstHeader = + "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + firstConfirmationTime + + "0000000000000000"; + await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); + + const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); + const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); + const msgValue = quote.value.add(quote.callFee); + const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { + value: msgValue.toNumber() + }); + await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); + + const btcTx = await utils.generateRawTx(instance, quote); + const refund = await instance.refundPegOut( + quoteHash, + btcTx, + blockHeaderHash, + partialMerkleTree, + merkleBranchHashes + ); + const daoFeeCollectorAfter = await web3.eth.getBalance(accounts[8]); + expect(daoFeeCollectorBefore).to.be.a.bignumber.eq(daoFeeCollectorAfter) + truffleAssertions.eventNotEmitted(refund, "DaoFeeSent"); + }); + it("Should not allow user to re deposit a refunded quote", async () => { await instance.addPegoutCollateral({ value: web3.utils.toWei("30000", "wei"), @@ -898,7 +1012,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); @@ -1034,7 +1148,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1452,7 +1566,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1494,7 +1608,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { diff --git a/test/penalization.tests.js b/test/penalization.tests.js index 4c3a0bf..67c195e 100644 --- a/test/penalization.tests.js +++ b/test/penalization.tests.js @@ -299,7 +299,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); truffleAssert.eventEmitted(pegOut, "PegOutDeposit"); diff --git a/test/refund.tests.js b/test/refund.tests.js index 1a3198b..8ee96e9 100644 --- a/test/refund.tests.js +++ b/test/refund.tests.js @@ -291,7 +291,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let initialLPDeposit = await instance.getCollateral(liquidityProviderRskAddress); let reward = Math.floor(quote.penaltyFee.div(web3.utils.toBN(10))); let initialLbcBalance = await web3.eth.getBalance(instance.address); - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -322,7 +322,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000001), + amount: web3.utils.toBN(1000000000002), success: true, quoteHash: quoteHash, }); @@ -353,7 +353,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let btcRawTransaction = '0x101'; let partialMerkleTree = '0x202'; let height = 10; - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -391,7 +391,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000001), + amount: web3.utils.toBN(1000000000002), success: false, quoteHash: quoteHash, }); diff --git a/test/utils/index.js b/test/utils/index.js index de49c77..4bee98b 100644 --- a/test/utils/index.js +++ b/test/utils/index.js @@ -15,7 +15,7 @@ function getTestQuote( let gasLimit = 150000; let nonce = 0; let data = callData || "0x00"; - let agreementTime = 1661788988; + let agreementTime = Math.floor(Date.now() / 1000); let timeForDeposit = 600; let callTime = 600; let depositConfirmations = 10; From dc6dd34e6526e76a847e222269e0c546a0d64a97 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Tue, 2 Jan 2024 14:22:10 -0300 Subject: [PATCH 09/12] contract size optimizations --- contracts/LiquidityBridgeContractV2.sol | 81 +++++++++---------------- 1 file changed, 30 insertions(+), 51 deletions(-) diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index 5b37e7e..e13e861 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -31,7 +31,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra 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; @@ -146,7 +145,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra function setProviderStatus( uint _providerId, bool status - ) public onlyOwnerAndProvider(_providerId) { + ) external onlyOwnerAndProvider(_providerId) { liquidityProviders[_providerId].status = status; } @@ -221,10 +220,21 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra ) external payable returns (uint) { require(tx.origin == msg.sender, "LBC003"); //require(collateral[msg.sender] == 0, "Already registered"); - validateRegisterParameters( - _name, - _apiBaseUrl, - _providerType + require(bytes(_name).length > 0, "LBC010"); + require( + bytes(_apiBaseUrl).length > 0, + "LBC017" + ); + + // Check if _providerType is one of the valid strings + require( + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("pegin")) || + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("pegout")) || + keccak256(abi.encodePacked(_providerType)) == + keccak256(abi.encodePacked("both")), + "LBC018" ); require(collateral[msg.sender] == 0 && pegoutCollateral[msg.sender] == 0, "LBC070"); @@ -259,32 +269,6 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra 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) { @@ -474,7 +458,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra bytes memory btcRawTransaction, bytes memory partialMerkleTree, uint256 height - ) public nonReentrant returns (int256) { + ) external nonReentrant returns (int256) { bytes32 quoteHash = validateAndHashQuote(quote); // TODO: allow multiple registerPegIns for the same quote with different transactions @@ -586,13 +570,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra increaseBalance(quote.liquidityProviderRskAddress, refundAmount); uint remainingAmount = transferredAmount - refundAmount; - if (quote.productFeeAmount > 0) { - (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{ - value: quote.productFeeAmount - }(""); - require(daoSuccess, "LBC074"); - emit DaoFeeSent(quoteHash, quote.productFeeAmount); - } + payToFeeCollector(quote.productFeeAmount, quoteHash); if (remainingAmount > dust) { // refund rskRefundAddress, if remaining amount greater than dust @@ -683,7 +661,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra function refundUserPegOut( bytes32 quoteHash - ) public nonReentrant { + ) external nonReentrant { QuotesV2.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; require(quote.lbcAddress != address(0), "LBC042"); @@ -719,7 +697,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra bytes32 btcBlockHeaderHash, uint256 partialMerkleTree, bytes32[] memory merkleBranchHashes - ) public nonReentrant onlyRegisteredForPegout { + ) external nonReentrant onlyRegisteredForPegout { require(pegoutRegistry[quoteHash].completed == false, "LBC064"); QuotesV2.PegOutQuote storage quote = registeredPegoutQuotes[quoteHash]; require(quote.lbcAddress != address(0), "LBC042"); @@ -737,8 +715,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra "LBC049" ); require(quote.value <= outputs[PAY_TO_ADDRESS_OUTPUT].value * (10**10), "LBC067"); // satoshi to wei - bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT] - .pkScript, mainnet); + bytes memory btcTxDestination = BtcUtils.parsePayToPubKeyHash(outputs[PAY_TO_ADDRESS_OUTPUT].pkScript, mainnet); require(keccak256(quote.deposityAddress) == keccak256(btcTxDestination), "LBC068"); if ( @@ -761,13 +738,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra }(""); require(sent, "LBC050"); - if (quote.productFeeAmount > 0) { - (bool sentDAO,) = payable(daoFeeCollectorAddress).call{ - value: quote.productFeeAmount - }(""); - require(sentDAO, "LBC074"); - emit DaoFeeSent(quoteHash, quote.productFeeAmount); - } + payToFeeCollector(quote.productFeeAmount, quoteHash); delete registeredPegoutQuotes[txQuoteHash]; pegoutRegistry[txQuoteHash].completed = true; @@ -982,4 +953,12 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra return false; } + + function payToFeeCollector(uint amount, bytes32 quoteHash) private { + if (amount > 0) { + (bool daoSuccess,) = payable(daoFeeCollectorAddress).call{value: amount}(""); + require(daoSuccess, "LBC074"); + emit DaoFeeSent(quoteHash, amount); + } + } } From 334abbb1f257757c1cd63b3bca1230f6d4a93280 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Wed, 3 Jan 2024 15:38:33 -0300 Subject: [PATCH 10/12] separate gasFee from callFee --- contracts/LiquidityBridgeContractV2.sol | 12 +++++----- contracts/QuotesV2.sol | 10 +++++--- migrations/2_deploy_contracts.js | 2 +- test/basic.tests.js | 31 ++++++++++++++----------- test/miscellaneous.tests.js | 14 ++++++++--- test/penalization.tests.js | 6 ++--- test/refund.tests.js | 18 +++++++------- test/utils/index.js | 8 +++++-- 8 files changed, 60 insertions(+), 41 deletions(-) diff --git a/contracts/LiquidityBridgeContractV2.sol b/contracts/LiquidityBridgeContractV2.sol index e13e861..b90736d 100644 --- a/contracts/LiquidityBridgeContractV2.sol +++ b/contracts/LiquidityBridgeContractV2.sol @@ -562,10 +562,10 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra if (callRegistry[quoteHash].success) { refundAmount = min( transferredAmount, - quote.value + quote.callFee + quote.value + quote.callFee + quote.gasFee ); } else { - refundAmount = min(transferredAmount, quote.callFee); + refundAmount = min(transferredAmount, quote.callFee + quote.gasFee); } increaseBalance(quote.liquidityProviderRskAddress, refundAmount); @@ -640,7 +640,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra bytes memory signature ) external payable { require(isRegisteredForPegout(quote.lpRskAddress), "LBC037"); - require(quote.value + quote.callFee + quote.productFeeAmount <= msg.value, "LBC063"); + require(quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee <= msg.value, "LBC063"); require(block.timestamp <= quote.depositDateLimit, "LBC065"); require(block.timestamp <= quote.expireDate, "LBC046"); require(block.number <= quote.expireBlock, "LBC047"); @@ -671,7 +671,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra "LBC041" ); - uint valueToTransfer = quote.value + quote.callFee + quote.productFeeAmount; + uint valueToTransfer = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee; address addressToTransfer = quote.rskRefundAddress; uint penalty = min(quote.penaltyFee, pegoutCollateral[quote.lpRskAddress]); @@ -799,7 +799,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra "LBC054" ); require( - quote.value + quote.callFee + quote.productFeeAmount >= minPegIn, + quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee >= minPegIn, "LBC055" ); require( @@ -889,7 +889,7 @@ contract LiquidityBridgeContractV2 is Initializable, OwnableUpgradeable, Reentra uint256 height ) private view returns (bool) { // do not penalize if deposit amount is insufficient - if (amount > 0 && uint256(amount) < quote.value + quote.callFee + quote.productFeeAmount) { + if (amount > 0 && uint256(amount) < quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee) { return false; } diff --git a/contracts/QuotesV2.sol b/contracts/QuotesV2.sol index f8943e0..2ca6db5 100644 --- a/contracts/QuotesV2.sol +++ b/contracts/QuotesV2.sol @@ -22,6 +22,7 @@ library QuotesV2 { uint16 depositConfirmations; bool callOnRegister; uint256 productFeeAmount; + uint256 gasFee; } struct PegOutQuote { @@ -43,6 +44,7 @@ library QuotesV2 { uint32 expireDate; uint32 expireBlock; uint256 productFeeAmount; + uint256 gasFee; } function encodeQuote( @@ -90,7 +92,8 @@ library QuotesV2 { quote.callTime, quote.depositConfirmations, quote.callOnRegister, - quote.productFeeAmount + quote.productFeeAmount, + quote.gasFee ); } @@ -124,7 +127,8 @@ library QuotesV2 { quote.transferTime, quote.expireDate, quote.expireBlock, - quote.productFeeAmount + quote.productFeeAmount, + quote.gasFee ); } @@ -133,7 +137,7 @@ library QuotesV2 { uint transferredAmount ) external pure { uint agreedAmount = 0; - agreedAmount = quote.value + quote.callFee + quote.productFeeAmount; + agreedAmount = quote.value + quote.callFee + quote.productFeeAmount + quote.gasFee; uint delta = agreedAmount / 10000; diff --git a/migrations/2_deploy_contracts.js b/migrations/2_deploy_contracts.js index aa0942c..d1deebd 100644 --- a/migrations/2_deploy_contracts.js +++ b/migrations/2_deploy_contracts.js @@ -98,7 +98,7 @@ module.exports = async function (deployer, network) { state.address = btcUtilsInstance.address; }); - minimumPegIn = 2; + minimumPegIn = 3; } let config = read(); diff --git a/test/basic.tests.js b/test/basic.tests.js index 2e83e4b..d9eedca 100644 --- a/test/basic.tests.js +++ b/test/basic.tests.js @@ -636,7 +636,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { liquidityProviderRskAddress ); let initialLBCBalance = await web3.eth.getBalance(instance.address); - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes( @@ -739,7 +739,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let btcRawTransaction = "0x101"; let partialMerkleTree = "0x202"; let height = 10; - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); let firstConfirmationTime = utils.reverseHexBytes( @@ -886,7 +886,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -959,7 +959,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee); + const msgValue = quote.value.add(quote.callFee).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1012,7 +1012,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); await truffleAssertions.eventEmitted(pegOut, "PegOutDeposit"); @@ -1148,7 +1148,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1566,7 +1566,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); @@ -1608,7 +1608,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { "0000000000000000"; await bridgeMockInstance.setHeaderByHash(blockHeaderHash, firstHeader); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { @@ -1883,9 +1883,10 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime: 7200, depositConfirmations: 10, callOnRegister: false, - productFeeAmount: BigInt("6000000000000000") + productFeeAmount: BigInt("6000000000000000"), + gasFee: BigInt("3000000000000000") }, - address: '2NFWum5hWgScV2QPPEfFGF1hWV6EcX5Gicg' + address: '2N54HuutZf7Xkmv85wCtBkQg5nCC3k432rf' }, { quote: { @@ -1907,9 +1908,10 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime: 7200, depositConfirmations: 10, callOnRegister: false, - productFeeAmount: BigInt("7000000000000000") + productFeeAmount: BigInt("7000000000000000"), + gasFee: BigInt("4000000000000000") }, - address: '2Mwk75Usb1GyQB2AtFLmr3iqrET3ByP9M9j' + address: '2NARRvPtz2ch1KozGfZg6FahLSVVaSG2fQr' }, { quote: { @@ -1931,9 +1933,10 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime: 7200, depositConfirmations: 10, callOnRegister: false, - productFeeAmount: BigInt("8000000000000000") + productFeeAmount: BigInt("8000000000000000"), + gasFee: BigInt("5000000000000000") }, - address: '2N56hAFac2aULZNw9SQfjdjLhT3qHtSrnFo' + address: '2Mwfc2XRxm64dDbNowHUFj2pZ4owePhNFyQ' } ] diff --git a/test/miscellaneous.tests.js b/test/miscellaneous.tests.js index 32fa180..1ea17fc 100644 --- a/test/miscellaneous.tests.js +++ b/test/miscellaneous.tests.js @@ -90,6 +90,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let penaltyFee = web3.utils.toBN(0); let callOnRegister = true; let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -109,7 +110,8 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime, depositConfirmations, callOnRegister, - productFeeAmount + productFeeAmount, + gasFee ]; // Let's now register our quote in the bridge... note that the // value is only a hundred wei @@ -171,17 +173,20 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let data = "0x00"; let callFee = 1; const productFeeAmount = 1; + const gasFee = 1; let gasLimit = 150000; let nonce = 0; let delta = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) .add(web3.utils.toBN(productFeeAmount)) + .add(web3.utils.toBN(gasFee)) .div(web3.utils.toBN(10000)); let peginAmount = web3.utils .toBN(val) .add(web3.utils.toBN(callFee)) .add(web3.utils.toBN(productFeeAmount)) + .add(web3.utils.toBN(gasFee)) .sub(delta); let lbcAddress = instance.address; let agreementTime = 1661788988; @@ -209,7 +214,8 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime, depositConfirmations, callOnRegister, - productFeeAmount + productFeeAmount, + gasFee ]; let quoteHash = await instance.hashQuote(quote); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -321,6 +327,7 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { let penaltyFee = 0; let callOnRegister = false; let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = [ fedBtcAddress, lbcAddress, @@ -340,7 +347,8 @@ contract("LiquidityBridgeContractV2.sol", async (accounts) => { callTime, depositConfirmations, callOnRegister, - productFeeAmount + productFeeAmount, + gasFee ]; 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 67c195e..5b7311f 100644 --- a/test/penalization.tests.js +++ b/test/penalization.tests.js @@ -141,7 +141,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { val); quote.penaltyFee = web3.utils.toBN(10); quote.callTime = 1; - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let btcRawTransaction = '0x101'; let partialMerkleTree = '0x202'; @@ -227,7 +227,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let height = 10; let initialLPBalance = await instance.getBalance(lpAddress, { from: lpAddress }); - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let initialLPDeposit = await instance.getCollateral(lpAddress, { from: lpAddress }); let rewardPercentage = await instance.getRewardPercentage(); let quoteHash = await instance.hashQuote(utils.asArray(quote)); @@ -299,7 +299,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { const quoteHash = await instance.hashPegoutQuote(utils.asArray(quote)); const signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); - const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount); + const msgValue = quote.value.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); const pegOut = await instance.depositPegout(utils.asArray(quote), signature, { value: msgValue.toNumber() }); truffleAssert.eventEmitted(pegOut, "PegOutDeposit"); diff --git a/test/refund.tests.js b/test/refund.tests.js index 8ee96e9..280e656 100644 --- a/test/refund.tests.js +++ b/test/refund.tests.js @@ -50,7 +50,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let initialRefundBalance = await web3.eth.getBalance(rskRefundAddress); let additionalFunds = web3.utils.toBN(1000000000000); - let peginAmount = val.add(quote.callFee).add(additionalFunds); + let peginAmount = val.add(quote.callFee).add(additionalFunds).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -136,7 +136,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let initialLBCBalance = await web3.eth.getBalance(instance.address); let initialRefundBalance = await web3.eth.getBalance(rskRefundAddress); let additionalFunds = web3.utils.toBN(1000000000000); - let peginAmount = val.add(quote.callFee).add(additionalFunds); + let peginAmount = val.add(quote.callFee).add(additionalFunds).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -219,7 +219,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let partialMerkleTree = '0x202'; let height = 10; let initialLPBalance = await instance.getBalance(liquidityProviderRskAddress); - let peginAmount = quote.val.add(quote.callFee); + let peginAmount = quote.val.add(quote.callFee).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -264,8 +264,8 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { amount: quote.val, success: true }); - expect(lpBal).to.be.a.bignumber.eq(quote.val.add(quote.callFee)); - expect(usrBal).to.be.a.bignumber.eq(peginAmount.sub(quote.callFee)); + expect(lpBal).to.be.a.bignumber.eq(quote.val.add(quote.callFee).add(quote.gasFee)); + expect(usrBal).to.be.a.bignumber.eq(peginAmount.sub(quote.callFee).sub(quote.gasFee)); expect(finalLPDeposit).to.be.a.bignumber.eq(initialLPDeposit); }); @@ -291,7 +291,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let initialLPDeposit = await instance.getCollateral(liquidityProviderRskAddress); let reward = Math.floor(quote.penaltyFee.div(web3.utils.toBN(10))); let initialLbcBalance = await web3.eth.getBalance(instance.address); - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -322,7 +322,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000002), + amount: web3.utils.toBN(1000000000003), success: true, quoteHash: quoteHash, }); @@ -353,7 +353,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let btcRawTransaction = '0x101'; let partialMerkleTree = '0x202'; let height = 10; - let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount); + let peginAmount = quote.val.add(quote.callFee).add(quote.productFeeAmount).add(quote.gasFee); let quoteHash = await instance.hashQuote(utils.asArray(quote)); let signature = await web3.eth.sign(quoteHash, liquidityProviderRskAddress); @@ -391,7 +391,7 @@ contract('LiquidityBridgeContractV2.sol', async accounts => { let lpCol = web3.utils.toBN(initialLPDeposit).sub(web3.utils.toBN(finalLPDeposit)); truffleAssert.eventEmitted(tx, "Refund", { dest: rskRefundAddress, - amount: web3.utils.toBN(1000000000002), + amount: web3.utils.toBN(1000000000003), success: false, quoteHash: quoteHash, }); diff --git a/test/utils/index.js b/test/utils/index.js index 4bee98b..b01fa65 100644 --- a/test/utils/index.js +++ b/test/utils/index.js @@ -22,6 +22,7 @@ function getTestQuote( let penaltyFee = web3.utils.toBN(0); let callOnRegister = false; let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = { fedBtcAddress, lbcAddress, @@ -41,7 +42,8 @@ function getTestQuote( callTime, depositConfirmations, callOnRegister, - productFeeAmount + productFeeAmount, + gasFee }; return quote; @@ -60,6 +62,7 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { let transferConfirmations = 10; let penaltyFee = web3.utils.toBN(0); let productFeeAmount = web3.utils.toBN(1); + const gasFee = web3.utils.toBN(1); let quote = { lbcAddress, @@ -79,7 +82,8 @@ function getTestPegOutQuote(lbcAddress, lpRskAddress, rskRefundAddress, value) { transferTime, expireDate, expireBlock, - productFeeAmount + productFeeAmount, + gasFee }; return quote; From 0eb6e308e72551c437c695e2cebec4f1ee4600e3 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Wed, 3 Jan 2024 15:41:26 -0300 Subject: [PATCH 11/12] update integration tests --- integration-test/common.js | 2 +- integration-test/pegin.test.js | 40 +++++++++++++++++------ integration-test/pegout.test.js | 32 +++++++++++++----- integration-test/test.config.example.json | 3 +- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/integration-test/common.js b/integration-test/common.js index 47d8ac7..742f3f2 100644 --- a/integration-test/common.js +++ b/integration-test/common.js @@ -67,7 +67,7 @@ async function loadConfig() { return JSON.parse(buffer.toString()) } async function sendBtc({ toAddress, amountInBtc, rpc, data }) { - const outputs = [ { [toAddress]: amountInBtc } ] + const outputs = [ { [toAddress]: amountInBtc.toString() } ] const fundOptions = { fee_rate: 25 } if (data) { outputs.push({ data }) diff --git a/integration-test/pegin.test.js b/integration-test/pegin.test.js index 034b775..5563189 100644 --- a/integration-test/pegin.test.js +++ b/integration-test/pegin.test.js @@ -1,6 +1,6 @@ const { expect } = require('chai') -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract") +const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContractV2") const BtcUtils = artifacts.require("BtcUtils") const bs58check = require('bs58check') const bs58 = require('bs58') @@ -29,8 +29,10 @@ describe('Flyover pegin process should', () => { let lpAccount let interval + let config + before(async () => { - const config = await loadConfig() + config = await loadConfig() interval = config.pollingIntervalInSeconds * 1000 lpAccount = web3.eth.accounts.privateKeyToAccount(config.lpPrivateKey) const userAccount = web3.eth.accounts.privateKeyToAccount(config.userPrivateKey) @@ -47,7 +49,8 @@ describe('Flyover pegin process should', () => { const timestamp = Math.floor(Date.now() / 1000) const transferTime = 1800 const gasLimit = await web3.eth.estimateGas({ to: userAddress, data: '0x' }) - const gasCost = await web3.eth.getGasPrice().then(price => price * gasLimit) + const gasPrice = await web3.eth.getGasPrice() + const cfuGasCost = gasPrice * gasLimit const fedAddress = await web3.eth.call({ to: '0x0000000000000000000000000000000001000006', data: web3.eth.abi.encodeFunctionSignature('getFederationAddress()') @@ -58,6 +61,13 @@ describe('Flyover pegin process should', () => { }) const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) + const productFeePercentage = await lbc.methods.productFeePercentage().call() + const value = BigInt(600000000000000000) // 0.6 eth + const productFee = (BigInt(productFeePercentage) * value) / BigInt(100) + const daoAddress = await lbc.methods.daoFeeCollectorAddress().call() + const daoGas = await web3.eth.estimateGas({ to: daoAddress, value: productFee.toString() }) + const daoGasCost = gasPrice * daoGas + quote = { fedBtcAddress: fedAddress, lbcAddress: config.lbcAddress, @@ -65,18 +75,20 @@ describe('Flyover pegin process should', () => { btcRefundAddress: userBtcAddress, rskRefundAddress: userAddress, liquidityProviderBtcAddress: lpBtcAddress, - callFee: BigInt(100000000000000 + gasCost), // fee is 0.0001 eth + callFee: BigInt(10000000000000000), // fee is 0.01 eth penaltyFee: 1000000, contractAddress: userAddress, data: '0x', gasLimit: gasLimit, nonce: nonce, - value: BigInt(6000000000000000), // 0.006 eth + value: value, agreementTimestamp: timestamp, timeForDeposit: transferTime, callTime: transferTime * 2, depositConfirmations: 1, - callOnRegister: false + callOnRegister: false, + productFeeAmount: productFee, + gasFee: cfuGasCost + daoGasCost } @@ -98,10 +110,18 @@ describe('Flyover pegin process should', () => { it('execute registerPegIn', async () => { const derivationAddress = await getDervivationAddress({ quote, quoteHash, btcUtilsInstance: btcUtils, lbc}) - - const amountInSatoshi = (quote.value + quote.callFee) / BigInt(10**10) - const amountInBtc = Number(amountInSatoshi) / 10**8 - + const total = web3.utils.toBN(quote.value.toString()) + .add(web3.utils.toBN(quote.callFee.toString())) + .add(web3.utils.toBN(quote.gasFee.toString())) + .add(web3.utils.toBN(quote.productFeeAmount.toString())); + const amountInBtc = web3.utils.fromWei(total, 'ether') + + if (config.btc.walletPassphrase) { + await bitcoinRpc('walletpassphrase', { + passphrase: config.btc.walletPassphrase, + timeout: 60 + }) + } const txHash = await sendBtc({ rpc: bitcoinRpc, amountInBtc, toAddress: derivationAddress }) const tx = await waitForBtcTransaction({ rpc: bitcoinRpc, hash: txHash, confirmations: quote.depositConfirmations, interval }) diff --git a/integration-test/pegout.test.js b/integration-test/pegout.test.js index 54bc16c..5594f95 100644 --- a/integration-test/pegout.test.js +++ b/integration-test/pegout.test.js @@ -1,6 +1,6 @@ const { expect } = require('chai') -const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContract") +const LiquidityBridgeContract = artifacts.require("LiquidityBridgeContractV2") const bs58check = require('bs58check') const pmtBuilder = require("@rsksmart/pmt-builder") @@ -29,8 +29,10 @@ describe('Flyover pegout process should', () => { let interval let userBtcEncodedAddress + let config + before(async () => { - const config = await loadConfig() + config = await loadConfig() interval = config.pollingIntervalInSeconds * 1000 lpAccount = web3.eth.accounts.privateKeyToAccount(config.lpPrivateKey) userAccount = web3.eth.accounts.privateKeyToAccount(config.userPrivateKey) @@ -46,22 +48,28 @@ describe('Flyover pegout process should', () => { const timestamp = Math.floor(Date.now() / 1000) const transferTime = 1800, expireDate = 3600 - const gasLimit = await web3.eth.estimateGas({ to: userAddress, data: '0x' }) - const gasCost = await web3.eth.getGasPrice().then(price => price * gasLimit) const nonce = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER) const height = await web3.eth.getBlock("latest").then(block => block.number); + const productFeePercentage = await lbc.methods.productFeePercentage().call() + const value = BigInt(600000000000000000) // 0.6 eth + const productFee = (BigInt(productFeePercentage) * value) / BigInt(100) + const daoAddress = await lbc.methods.daoFeeCollectorAddress().call() + const daoGas = await web3.eth.estimateGas({ to: daoAddress, value: productFee.toString() }) + const gasPrice = await web3.eth.getGasPrice() + const daoGasCost = gasPrice * daoGas + const btcNetworkFee = 0.00006700 * 10**18 + quote = { lbcAddress: config.lbcAddress, lpRskAddress: lpAddress, btcRefundAddress: userBtcAddress, rskRefundAddress: userAddress, lpBtcAddress: lpBtcAddress, - callFee: BigInt(100000000000000 + gasCost), // fee is 0.0001 eth + callFee: BigInt(100000000000000), // fee is 0.0001 eth penaltyFee: 1000000, nonce: nonce, deposityAddress: userBtcAddress, - gasLimit: gasLimit, value: BigInt(6000000000000000), // 0.006 eth agreementTimestamp: timestamp, depositDateLimit: timestamp + transferTime, @@ -69,7 +77,9 @@ describe('Flyover pegout process should', () => { transferConfirmations: 1, transferTime: transferTime, expireDate: timestamp + expireDate, - expireBlock: height + 50 + expireBlock: height + 50, + productFeeAmount: productFee, + gasFee: daoGasCost + btcNetworkFee } quoteHash = await lbc.methods.hashPegoutQuote(quote).call() @@ -79,7 +89,7 @@ describe('Flyover pegout process should', () => { it('execute depositPegout', async () => { const receipt = await sendFromAccount({ account: userAccount, - value: quote.callFee + quote.value, + value: quote.callFee + quote.value + quote.productFeeAmount + BigInt(quote.gasFee), call: lbc.methods.depositPegout(quote, signedQuote.signature) }) const parsedReceipt = decodeLogs({ abi: LiquidityBridgeContract.abi, receipt }) @@ -89,6 +99,12 @@ describe('Flyover pegout process should', () => { it('execute refundPegOut', async () => { const amountInSatoshi = (quote.value + quote.callFee) / BigInt(10**10) const amountInBtc = Number(amountInSatoshi) / 10**8 + if (config.btc.walletPassphrase) { + await bitcoinRpc('walletpassphrase', { + passphrase: config.btc.walletPassphrase, + timeout: 60 + }) + } const txHash = await sendBtc({ rpc: bitcoinRpc, amountInBtc, diff --git a/integration-test/test.config.example.json b/integration-test/test.config.example.json index 2a9f476..4c73a05 100644 --- a/integration-test/test.config.example.json +++ b/integration-test/test.config.example.json @@ -9,6 +9,7 @@ "btc": { "url": "http://127.0.0.1:5555/", "user": "test", - "pass": "test" + "pass": "test", + "walletPassphrase": "pass" } } \ No newline at end of file From 5d026bf0d0480f0a6615b6af6af479af29410fa8 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Fri, 5 Jan 2024 15:28:58 -0300 Subject: [PATCH 12/12] remove addresses from regtest config --- config.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config.json b/config.json index c3ac571..7ee07e2 100644 --- a/config.json +++ b/config.json @@ -1,23 +1,18 @@ { "rskRegtest": { "Migrations": { - "address": "0x1Af2844A588759D0DE58abD568ADD96BB8B3B6D8", "deployed": false }, "SignatureValidator": { - "address": "0xCd5805d60Bbf9Afe69a394c2BDa10F6Dae2c39AF", "deployed": false }, "Quotes": { - "address": "0xdac5481925A298B95Bf5b54c35b68FC6fc2eF423", "deployed": false }, "BtcUtils": { - "address": "0xfD1dda8C3BC734Bc1C8e71F69F25BFBEe9cE9535", "deployed": false }, "LiquidityBridgeContract": { - "address": "0x987c1f13d417F7E04d852B44badc883E4E9782e1", "deployed": false } },