From b626b5aa56b2c0e008e65f29ec8704e3295b7d14 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 26 Dec 2024 18:55:43 -0300 Subject: [PATCH 1/2] feat: migrate truffle scripts to hardhat tasks --- hardhat.config.ts | 94 ++++++++++++++ scripts/deployment-utils/constants.ts | 1 + scripts/deployment-utils/deploy-proxy.ts | 3 +- tasks/btc-best-height.ts | 15 +++ tasks/get-versions.ts | 43 +++++++ tasks/hash-quote.example.json | 22 ++++ tasks/hash-quote.ts | 64 ++++++++++ tasks/refund-user-pegout.ts | 42 +++++++ tasks/register-pegin.ts | 121 ++++++++++++++++++ tasks/utils/quote.ts | 148 +++++++++++++++++++++++ test/deployment.test.ts | 6 +- 11 files changed, 553 insertions(+), 6 deletions(-) create mode 100644 scripts/deployment-utils/constants.ts create mode 100644 tasks/btc-best-height.ts create mode 100644 tasks/get-versions.ts create mode 100644 tasks/hash-quote.example.json create mode 100644 tasks/hash-quote.ts create mode 100644 tasks/refund-user-pegout.ts create mode 100644 tasks/register-pegin.ts create mode 100644 tasks/utils/quote.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index d40a331..c8b5f43 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,6 +1,30 @@ +import dotenv from "dotenv"; import { HardhatUserConfig } from "hardhat/config"; import "@nomicfoundation/hardhat-toolbox"; import "@openzeppelin/hardhat-upgrades"; +import "./tasks/get-versions"; +import "./tasks/btc-best-height"; +import "./tasks/hash-quote"; +import "./tasks/refund-user-pegout"; +import "./tasks/register-pegin"; + +dotenv.config(); + +const { + MAINNET_RPC_URL, + TESTNET_RPC_URL, + MAINNET_SIGNER_PRIVATE_KEY, + MAINNET_MNEMONIC, + TESTNET_SIGNER_PRIVATE_KEY, + TESTNET_MNEMONIC, + DEV_SIGNER_PRIVATE_KEY, + DEV_MNEMONIC, +} = process.env; + +const rskMainnetDerivationPath = "m/44'/137'/0'/0/0"; +const rskTestnetDerivationPath = "m/44'/37310'/0'/0/0"; + +const rpcDefaultTimeout = 3 * 60 * 1000; // 3 minutes const config: HardhatUserConfig = { networks: { @@ -8,6 +32,24 @@ const config: HardhatUserConfig = { url: "http://localhost:4444", chainId: 33, }, + rskDevelopment: { + url: TESTNET_RPC_URL, + timeout: rpcDefaultTimeout, + chainId: 31, + accounts: getAccounts("development"), + }, + rskTestnet: { + url: TESTNET_RPC_URL, + timeout: rpcDefaultTimeout, + chainId: 31, + accounts: getAccounts("testnet"), + }, + rskMainnet: { + url: MAINNET_RPC_URL, + timeout: rpcDefaultTimeout, + chainId: 30, + accounts: getAccounts("mainnet"), + }, }, solidity: { version: "0.8.18", @@ -21,3 +63,55 @@ const config: HardhatUserConfig = { }; export default config; + +function getAccounts(network: "mainnet" | "testnet" | "development") { + switch (network) { + case "mainnet": + return getMainnetAccounts(); + case "testnet": + return getTestnetAccounts(); + case "development": + return getDevAccounts(); + default: + return undefined; + } +} + +function getMainnetAccounts() { + if (MAINNET_MNEMONIC) { + return { + mnemonic: MAINNET_MNEMONIC, + path: rskMainnetDerivationPath, + }; + } else if (MAINNET_SIGNER_PRIVATE_KEY) { + return [MAINNET_SIGNER_PRIVATE_KEY]; + } else { + return undefined; + } +} + +function getTestnetAccounts() { + if (TESTNET_MNEMONIC) { + return { + mnemonic: TESTNET_MNEMONIC, + path: rskTestnetDerivationPath, + }; + } else if (TESTNET_SIGNER_PRIVATE_KEY) { + return [TESTNET_SIGNER_PRIVATE_KEY]; + } else { + return undefined; + } +} + +function getDevAccounts() { + if (DEV_MNEMONIC) { + return { + mnemonic: DEV_MNEMONIC, + path: rskTestnetDerivationPath, + }; + } else if (DEV_SIGNER_PRIVATE_KEY) { + return [DEV_SIGNER_PRIVATE_KEY]; + } else { + return undefined; + } +} diff --git a/scripts/deployment-utils/constants.ts b/scripts/deployment-utils/constants.ts new file mode 100644 index 0000000..0c589ca --- /dev/null +++ b/scripts/deployment-utils/constants.ts @@ -0,0 +1 @@ +export const BRIDGE_ADDRESS = "0x0000000000000000000000000000000001000006"; diff --git a/scripts/deployment-utils/deploy-proxy.ts b/scripts/deployment-utils/deploy-proxy.ts index 4aac246..8d14898 100644 --- a/scripts/deployment-utils/deploy-proxy.ts +++ b/scripts/deployment-utils/deploy-proxy.ts @@ -1,6 +1,7 @@ import { ethers, upgrades } from "hardhat"; import { deploy, DeployedContractInfo, LOCAL_NETWORK } from "./deploy"; import { deployContract, REMOTE_NETWORKS, TEST_NETWORKS } from "./utils"; +import { BRIDGE_ADDRESS } from "./constants"; interface LiquidityBridgeContractInitParams { bridgeAddress: string; @@ -20,8 +21,6 @@ interface LiquidityBridgeContractLibraries { bridge: string; } -export const BRIDGE_ADDRESS = "0x0000000000000000000000000000000001000006"; - async function deployProxyLibraries( network: string ): Promise { diff --git a/tasks/btc-best-height.ts b/tasks/btc-best-height.ts new file mode 100644 index 0000000..cf2271d --- /dev/null +++ b/tasks/btc-best-height.ts @@ -0,0 +1,15 @@ +import { task } from "hardhat/config"; +import { BRIDGE_ADDRESS } from "../scripts/deployment-utils/constants"; + +task("btc-best-height") + .setDescription( + "Prints the best height of the Bitcoin network seen by the Rootstock Bridge" + ) + .setAction(async (_, hre) => { + const { ethers } = hre; + const bridge = await ethers.getContractAt("Bridge", BRIDGE_ADDRESS); + const bestHeight = await bridge.getBtcBlockchainBestChainHeight(); + console.info( + `Best BTC blockchain height: \x1b[32m${bestHeight.toString()}\x1b[0m` + ); + }); diff --git a/tasks/get-versions.ts b/tasks/get-versions.ts new file mode 100644 index 0000000..9476cc3 --- /dev/null +++ b/tasks/get-versions.ts @@ -0,0 +1,43 @@ +import { task } from "hardhat/config"; +import { DeploymentConfig, read } from "../scripts/deployment-utils/deploy"; + +task("get-versions") + .setDescription( + "Prints the versions of the LiquidityBridgeContract and its libraries where applicable" + ) + .setAction(async (_, hre) => { + const { ethers, network } = hre; + const addresses: Partial = read(); + const networkDeployments: Partial | undefined = + addresses[network.name]; + + if (!networkDeployments?.LiquidityBridgeContract?.address) { + throw new Error( + "LiquidityBridgeContract proxy deployment info not found" + ); + } + const lbcAddress = networkDeployments.LiquidityBridgeContract.address; + + if (!networkDeployments.BtcUtils?.address) { + throw new Error( + "LiquidityBridgeContract proxy deployment info not found" + ); + } + const btcUtilsAddress = networkDeployments.BtcUtils.address; + + const lbc = await ethers.getContractAt( + "LiquidityBridgeContractV2", + lbcAddress + ); + const lbcVersion = await lbc.version().catch(() => "Not found"); + + const btcUtils = await ethers.getContractAt("BtcUtils", btcUtilsAddress); + const btcUtilsVersion = await btcUtils.version().catch(() => "Not found"); + + console.info("======================================="); + console.info( + `LiquidityBridgeContract version: \x1b[32m${lbcVersion}\x1b[0m` + ); + console.info(`BtcUtils version: \x1b[32m${btcUtilsVersion}\x1b[0m`); + console.info("======================================="); + }); diff --git a/tasks/hash-quote.example.json b/tasks/hash-quote.example.json new file mode 100644 index 0000000..60deb09 --- /dev/null +++ b/tasks/hash-quote.example.json @@ -0,0 +1,22 @@ +{ + "fedBTCAddr": "2N9uY615Mxk6KSSjv6F3FnvSPgZMer7FF39", + "lbcAddr": "0x18D8212bC00106b93070123f325021C723D503a3", + "lpRSKAddr": "0xdfcf32644e6cc5badd1188cddf66f66e21b24375", + "btcRefundAddr": "mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8", + "rskRefundAddr": "0x8dcCD82443B80DDdE3690aF86746BfD9D766f8d2", + "lpBTCAddr": "mwEceC31MwWmF6hc5SSQ8FmbgdsSoBSnbm", + "callFee": 150000000000000, + "penaltyFee": 10000000000000, + "contractAddr": "0x8dcCD82443B80DDdE3690aF86746BfD9D766f8d2", + "data": "0x", + "gasLimit": 21000, + "nonce": "3307065858190946360", + "value": "5000000000000000", + "agreementTimestamp": 1735243258, + "timeForDeposit": 9800, + "lpCallTime": 10800, + "confirmations": 2, + "callOnRegister": false, + "gasFee": 114524739000, + "productFeeAmount": 0 +} diff --git a/tasks/hash-quote.ts b/tasks/hash-quote.ts new file mode 100644 index 0000000..11f7c86 --- /dev/null +++ b/tasks/hash-quote.ts @@ -0,0 +1,64 @@ +import { task, types } from "hardhat/config"; +import { readFileSync } from "fs"; +import { DeploymentConfig, read } from "../scripts/deployment-utils/deploy"; +import { + ApiPeginQuote, + ApiPegoutQuote, + parsePeginQuote, + parsePegoutQuote, +} from "./utils/quote"; + +task("hash-quote") + .setDescription("Prints the hash of the quote provided in the input file") + .addParam( + "file", + "The file containing the quote to hash", + undefined, + types.inputFile + ) + .addParam( + "type", + "Wether the quote is a PegIn or PegOut quote", + undefined, + types.string + ) + .setAction(async (args, hre) => { + const { network, ethers } = hre; + const typedArgs = args as { file: string; type: string }; + const type: string = typedArgs.type.toLowerCase(); + const inputFile: string = typedArgs.file; + + if (!["pegin", "pegout"].includes(type)) { + throw new Error("Invalid type. Must be 'pegin' or 'pegout'"); + } + const fileContent = readFileSync(inputFile); + const quote: unknown = JSON.parse(fileContent.toString()); + + const addresses: Partial = read(); + const networkDeployments: Partial | undefined = + addresses[network.name]; + const lbcAddress = networkDeployments?.LiquidityBridgeContract?.address; + if (!lbcAddress) { + throw new Error( + "LiquidityBridgeContract proxy deployment info not found" + ); + } + const lbc = await ethers.getContractAt( + "LiquidityBridgeContractV2", + lbcAddress + ); + + if (type === "pegin") { + const hash = await lbc.hashQuote(parsePeginQuote(quote as ApiPeginQuote)); + console.info( + `Hash of the provided PegIn quote: \x1b[32m${hash.slice(2)}\x1b[0m` + ); + } else { + const hash = await lbc.hashPegoutQuote( + parsePegoutQuote(quote as ApiPegoutQuote) + ); + console.info( + `Hash of the provided PegOut quote: \x1b[32m${hash.slice(2)}\x1b[0m` + ); + } + }); diff --git a/tasks/refund-user-pegout.ts b/tasks/refund-user-pegout.ts new file mode 100644 index 0000000..243d402 --- /dev/null +++ b/tasks/refund-user-pegout.ts @@ -0,0 +1,42 @@ +import { task, types } from "hardhat/config"; +import { DeploymentConfig, read } from "../scripts/deployment-utils/deploy"; + +task("refund-user-pegout") + .setDescription( + "Refund a user that didn't receive their PegOut in the agreed time" + ) + .addParam( + "quotehash", + "The hash of the accepted PegOut quote", + undefined, + types.string + ) + .setAction(async (args, hre) => { + const { ethers, network } = hre; + const typedArgs = args as { quotehash: string }; + const quoteHash: string = "0x" + typedArgs.quotehash; + + const addresses: Partial = read(); + const networkDeployments: Partial | undefined = + addresses[network.name]; + + const lbcAddress = networkDeployments?.LiquidityBridgeContract?.address; + if (!lbcAddress) { + throw new Error( + "LiquidityBridgeContract proxy deployment info not found" + ); + } + const lbc = await ethers.getContractAt( + "LiquidityBridgeContractV2", + lbcAddress + ); + + const gasEstimation = await lbc.refundUserPegOut.estimateGas(quoteHash); + console.info("Gas estimation for refundUserPegOut:", gasEstimation); + + const tx = await lbc.refundUserPegOut(quoteHash); + const receipt = await tx.wait(); + console.info(`Transaction hash: ${receipt!.hash}`); + console.info("Transaction receipt: "); + console.info(receipt); + }); diff --git a/tasks/register-pegin.ts b/tasks/register-pegin.ts new file mode 100644 index 0000000..af2a961 --- /dev/null +++ b/tasks/register-pegin.ts @@ -0,0 +1,121 @@ +import { task, types } from "hardhat/config"; +import { DeploymentConfig, read } from "../scripts/deployment-utils/deploy"; +import { ApiPeginQuote, parsePeginQuote } from "./utils/quote"; +import { readFileSync } from "fs"; +import mempoolJS from "@mempool/mempool.js"; +import { BigNumberish } from "ethers"; +import { Transaction } from "bitcoinjs-lib"; +import pmtBuilder from "@rsksmart/pmt-builder"; + +task("register-pegin") + .setDescription( + "Register a PegIn bitcoin transaction within the Liquidity Bridge Contract" + ) + .addParam( + "file", + "The file containing the PegIn quote to register", + undefined, + types.inputFile + ) + .addParam( + "signature", + "The signature of the Liquidity Provider committing to pay for the quote", + undefined, + types.string + ) + .addParam( + "txid", + "The transaction id of the Bitcoin transaction that pays for the specific PegIn", + undefined, + types.string + ) + .setAction(async (args, hre) => { + const { ethers, network } = hre; + + const typedArgs = args as { file: string; signature: string; txid: string }; + const txId: string = typedArgs.txid; + const inputFile: string = typedArgs.file; + const signature: string = "0x" + typedArgs.signature; + + const addresses: Partial = read(); + const networkDeployments: Partial | undefined = + addresses[network.name]; + const lbcAddress = networkDeployments?.LiquidityBridgeContract?.address; + if (!lbcAddress) { + throw new Error( + "LiquidityBridgeContract proxy deployment info not found" + ); + } + const lbc = await ethers.getContractAt( + "LiquidityBridgeContractV2", + lbcAddress + ); + + const fileContent = readFileSync(inputFile); + const quote = parsePeginQuote( + JSON.parse(fileContent.toString()) as ApiPeginQuote + ); + const { rawTx, pmt, height } = await getRegisterParams( + txId, + network.name === "rskMainnet" + ); + + const gasEstimation = await lbc.registerPegIn.estimateGas( + quote, + signature, + rawTx, + pmt, + height + ); + console.info("Gas estimation for registerPegIn:", gasEstimation); + + const result = await lbc.registerPegIn.staticCall( + quote, + signature, + rawTx, + pmt, + height + ); + console.info("Expected result:", result); + + const tx = await lbc.registerPegIn(quote, signature, rawTx, pmt, height); + const receipt = await tx.wait(); + console.info(`Transaction hash: ${receipt!.hash}`); + console.info("Transaction receipt: "); + console.info(receipt); + }); + +async function getRegisterParams( + txId: string, + mainnet: boolean +): Promise<{ + rawTx: string; + pmt: string; + height: BigNumberish; +}> { + const { + bitcoin: { blocks, transactions }, + } = mempoolJS({ + hostname: "mempool.space", + network: mainnet ? "mainnet" : "testnet", + }); + + const btcRawTxFull = await transactions.getTxHex({ txid: txId }).catch(() => { + throw new Error("Transaction not found"); + }); + const tx = Transaction.fromHex(btcRawTxFull); + tx.ins.forEach((input) => { + input.witness = []; + }); + const btcRawTx = tx.toHex(); + + const txStatus = await transactions.getTxStatus({ txid: txId }); + const blockTxs = await blocks.getBlockTxids({ hash: txStatus.block_hash }); + const pmt = pmtBuilder.buildPMT(blockTxs, txId); + + return { + rawTx: "0x" + btcRawTx, + pmt: "0x" + pmt.hex, + height: txStatus.block_height, + }; +} diff --git a/tasks/utils/quote.ts b/tasks/utils/quote.ts new file mode 100644 index 0000000..03d3320 --- /dev/null +++ b/tasks/utils/quote.ts @@ -0,0 +1,148 @@ +import * as bs58check from "bs58check"; +import { bech32, bech32m } from "bech32"; +import { BytesLike } from "ethers"; +import { QuotesV2 } from "../../typechain-types"; + +export interface ApiPeginQuote { + fedBTCAddr: string; + lbcAddr: string; + lpRSKAddr: string; + btcRefundAddr: string; + rskRefundAddr: string; + lpBTCAddr: string; + callFee: number; + penaltyFee: number; + contractAddr: string; + data: string; + gasLimit: number; + nonce: string; + value: string; + agreementTimestamp: number; + timeForDeposit: number; + lpCallTime: number; + confirmations: number; + callOnRegister: boolean; + gasFee: number; + productFeeAmount: number; +} + +export interface ApiPegoutQuote { + lbcAddress: string; + liquidityProviderRskAddress: string; + btcRefundAddress: string; + rskRefundAddress: string; + lpBtcAddr: string; + callFee: number; + penaltyFee: number; + nonce: string; + depositAddr: string; + value: string; + agreementTimestamp: number; + depositDateLimit: number; + transferTime: number; + depositConfirmations: number; + transferConfirmations: number; + productFeeAmount: number; + gasFee: number; + expireBlocks: number; + expireDate: number; +} + +export function parsePeginQuote( + quote: ApiPeginQuote +): QuotesV2.PeginQuoteStruct { + return { + fedBtcAddress: bs58check.decode(quote.fedBTCAddr).slice(1), + lbcAddress: quote.lbcAddr.toLowerCase(), + liquidityProviderRskAddress: quote.lpRSKAddr.toLowerCase(), + btcRefundAddress: bs58check.decode(quote.btcRefundAddr), + rskRefundAddress: quote.rskRefundAddr.toLowerCase(), + liquidityProviderBtcAddress: bs58check.decode(quote.lpBTCAddr), + callFee: quote.callFee, + penaltyFee: quote.penaltyFee, + contractAddress: quote.contractAddr.toLowerCase(), + data: quote.data, + gasLimit: quote.gasLimit, + nonce: quote.nonce, + value: quote.value, + agreementTimestamp: quote.agreementTimestamp, + timeForDeposit: quote.timeForDeposit, + callTime: quote.lpCallTime, + depositConfirmations: quote.confirmations, + callOnRegister: quote.callOnRegister, + gasFee: quote.gasFee, + productFeeAmount: quote.productFeeAmount, + }; +} + +export function parsePegoutQuote( + quote: ApiPegoutQuote +): QuotesV2.PegOutQuoteStruct { + return { + lbcAddress: quote.lbcAddress.toLowerCase(), + lpRskAddress: quote.liquidityProviderRskAddress.toLowerCase(), + btcRefundAddress: parseBtcAddress(quote.btcRefundAddress), + rskRefundAddress: quote.rskRefundAddress.toLowerCase(), + lpBtcAddress: bs58check.decode(quote.lpBtcAddr), + callFee: quote.callFee, + penaltyFee: quote.penaltyFee, + nonce: quote.nonce, + deposityAddress: parseBtcAddress(quote.depositAddr), + value: quote.value, + agreementTimestamp: quote.agreementTimestamp, + depositDateLimit: quote.depositDateLimit, + transferTime: quote.transferTime, + depositConfirmations: quote.depositConfirmations, + transferConfirmations: quote.transferConfirmations, + productFeeAmount: quote.productFeeAmount, + gasFee: quote.gasFee, + expireBlock: quote.expireBlocks, + expireDate: quote.expireDate, + }; +} + +function parseBtcAddress(address: string): BytesLike { + const MAINNET_P2TR = /^bc1p([ac-hj-np-z02-9]{58})$/; + const TESTNET_P2TR = /^tb1p([ac-hj-np-z02-9]{58})$/; + const REGTEST_P2TR = /^bcrt1p([ac-hj-np-z02-9]{58})$/; + + const MAINNET_P2WSH = /^bc1q([ac-hj-np-z02-9]{58})$/; + const TESTNET_P2WSH = /^tb1q([ac-hj-np-z02-9]{58})$/; + const REGTEST_P2WSH = /^bcrt1q([ac-hj-np-z02-9]{58})$/; + + const MAINNET_P2WPKH = /^bc1q([ac-hj-np-z02-9]{38})$/; + const TESTNET_P2WPKH = /^tb1q([ac-hj-np-z02-9]{38})$/; + const REGTEST_P2WPKH = /^bcrt1q([ac-hj-np-z02-9]{38})$/; + + const MAINNET_P2SH = /^[3]([a-km-zA-HJ-NP-Z1-9]{33,34})$/; + const TESTNET_P2SH = /^[2]([a-km-zA-HJ-NP-Z1-9]{33,34})$/; + + const MAINNET_P2PKH = /^[1]([a-km-zA-HJ-NP-Z1-9]{25,34})$/; + const TESTNET_P2PKH = /^[mn]([a-km-zA-HJ-NP-Z1-9]{25,34})$/; + + const bech32mRegex = [MAINNET_P2TR, TESTNET_P2TR, REGTEST_P2TR]; + const bech32Regex = [ + MAINNET_P2WSH, + TESTNET_P2WSH, + REGTEST_P2WSH, + MAINNET_P2WPKH, + TESTNET_P2WPKH, + REGTEST_P2WPKH, + ]; + const base58Regex = [ + MAINNET_P2SH, + TESTNET_P2SH, + MAINNET_P2PKH, + TESTNET_P2PKH, + ]; + + if (bech32mRegex.some((regex) => regex.test(address))) { + return new Uint8Array(bech32m.decode(address).words); + } else if (bech32Regex.some((regex) => regex.test(address))) { + return new Uint8Array(bech32.decode(address).words); + } else if (base58Regex.some((regex) => regex.test(address))) { + return bs58check.decode(address); + } else { + throw new Error("Invalid btc address type"); + } +} diff --git a/test/deployment.test.ts b/test/deployment.test.ts index 14966f5..016c370 100644 --- a/test/deployment.test.ts +++ b/test/deployment.test.ts @@ -1,12 +1,10 @@ import { expect } from "chai"; import hre from "hardhat"; import { ethers } from "hardhat"; -import { - BRIDGE_ADDRESS, - deployLbcProxy, -} from "../scripts/deployment-utils/deploy-proxy"; +import { deployLbcProxy } from "../scripts/deployment-utils/deploy-proxy"; import { upgradeLbcProxy } from "../scripts/deployment-utils/upgrade-proxy"; import { ZERO_ADDRESS } from "./utils/constants"; +import { BRIDGE_ADDRESS } from "../scripts/deployment-utils/constants"; describe("LiquidityBridgeContract deployment process should", function () { let proxyAddress: string; From c8fe0e3f01dc1ff4ab1639ab11585bfef36fd598 Mon Sep 17 00:00:00 2001 From: Luisfc68 Date: Thu, 26 Dec 2024 19:03:05 -0300 Subject: [PATCH 2/2] fix: sonar warnings --- hardhat.config.ts | 6 +++--- tasks/utils/quote.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index c8b5f43..2bc9a96 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -33,19 +33,19 @@ const config: HardhatUserConfig = { chainId: 33, }, rskDevelopment: { - url: TESTNET_RPC_URL, + url: TESTNET_RPC_URL ?? "https://public-node.testnet.rsk.co", timeout: rpcDefaultTimeout, chainId: 31, accounts: getAccounts("development"), }, rskTestnet: { - url: TESTNET_RPC_URL, + url: TESTNET_RPC_URL ?? "https://public-node.testnet.rsk.co", timeout: rpcDefaultTimeout, chainId: 31, accounts: getAccounts("testnet"), }, rskMainnet: { - url: MAINNET_RPC_URL, + url: MAINNET_RPC_URL ?? "https://public-node.rsk.co", timeout: rpcDefaultTimeout, chainId: 30, accounts: getAccounts("mainnet"), diff --git a/tasks/utils/quote.ts b/tasks/utils/quote.ts index 03d3320..5790eaf 100644 --- a/tasks/utils/quote.ts +++ b/tasks/utils/quote.ts @@ -114,10 +114,10 @@ function parseBtcAddress(address: string): BytesLike { const TESTNET_P2WPKH = /^tb1q([ac-hj-np-z02-9]{38})$/; const REGTEST_P2WPKH = /^bcrt1q([ac-hj-np-z02-9]{38})$/; - const MAINNET_P2SH = /^[3]([a-km-zA-HJ-NP-Z1-9]{33,34})$/; - const TESTNET_P2SH = /^[2]([a-km-zA-HJ-NP-Z1-9]{33,34})$/; + const MAINNET_P2SH = /^3([a-km-zA-HJ-NP-Z1-9]{33,34})$/; + const TESTNET_P2SH = /^2([a-km-zA-HJ-NP-Z1-9]{33,34})$/; - const MAINNET_P2PKH = /^[1]([a-km-zA-HJ-NP-Z1-9]{25,34})$/; + const MAINNET_P2PKH = /^1([a-km-zA-HJ-NP-Z1-9]{25,34})$/; const TESTNET_P2PKH = /^[mn]([a-km-zA-HJ-NP-Z1-9]{25,34})$/; const bech32mRegex = [MAINNET_P2TR, TESTNET_P2TR, REGTEST_P2TR];