diff --git a/package-lock.json b/package-lock.json index 53de93a77..46ad238f9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4477,10 +4477,6 @@ "resolved": "packages/profile", "link": true }, - "node_modules/@stacks/sbtc-developer-release": { - "resolved": "packages/sbtc", - "link": true - }, "node_modules/@stacks/stacking": { "resolved": "packages/stacking", "link": true @@ -21001,6 +20997,10 @@ "node": ">=10" } }, + "node_modules/sbtc": { + "resolved": "packages/sbtc", + "link": true + }, "node_modules/sbtc-bridge-lib": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sbtc-bridge-lib/-/sbtc-bridge-lib-1.1.2.tgz", @@ -25262,12 +25262,11 @@ } }, "packages/sbtc": { - "name": "@stacks/sbtc-developer-release", - "version": "0.1.0", + "version": "0.1.7", "license": "MIT", "dependencies": { "@btc-helpers/rpc": "^2.0.0", - "@noble/secp256k1": "1.7.1", + "@noble/secp256k1": "^2.0.0", "@scure/base": "^1.1.3", "@scure/btc-signer": "^1.1.0", "@stacks/common": "^6.7.0", @@ -25282,7 +25281,7 @@ "@scure/bip39": "^1.2.1", "jest-fetch-mock": "^3.0.3", "rimraf": "^3.0.2", - "sbtc-bridge-lib": "^1.1.2", + "sbtc-bridge-lib": "latest", "vitest": "^0.34.6" } }, diff --git a/packages/sbtc/package.json b/packages/sbtc/package.json index 0dcc8097c..43802e3fb 100644 --- a/packages/sbtc/package.json +++ b/packages/sbtc/package.json @@ -36,7 +36,7 @@ "@scure/bip39": "^1.2.1", "jest-fetch-mock": "^3.0.3", "rimraf": "^3.0.2", - "sbtc-bridge-lib": "^1.1.2", + "sbtc-bridge-lib": "latest", "vitest": "^0.34.6" }, "sideEffects": false, diff --git a/packages/sbtc/tests/api.test.ts b/packages/sbtc/tests/api.test.ts index afe3c2e50..ec5684d4d 100644 --- a/packages/sbtc/tests/api.test.ts +++ b/packages/sbtc/tests/api.test.ts @@ -1,23 +1,14 @@ +import { base58 } from '@scure/base'; import { bytesToHex } from '@stacks/common'; import { BufferCV, SomeCV } from '@stacks/transactions'; import { expect, test } from 'vitest'; -import { - DEFAULT_UTXO_TO_SPENDABLE, - DevEnvHelper, - SBTC_FT_ADDRESS, - TestnetHelper, - WALLET_00, - WALLET_01, - WALLET_02, - sleep, - utxoSelect, -} from '../src'; +import { DevEnvHelper, SBTC_FT_ADDRESS, TestnetHelper, WALLET_00, sleep } from '../src'; const dev = new DevEnvHelper(); const tnet = new TestnetHelper(); test('minting bitcoin increases balance', async () => { - const wallet = await dev.getBitcoinAccount(WALLET_00); + const wallet = await dev.getBitcoinAccount(WALLET_00, 1); const balance = await dev.getBalance(wallet.wpkh.address); console.log('balance', balance); @@ -33,8 +24,8 @@ test('minting bitcoin increases balance', async () => { expect(balanceAfter).toBeGreaterThan(balance); }); -test('fetch utxos', async () => { - const wallet = await dev.getBitcoinAccount(WALLET_00); +test('devenv, fetch utxos', async () => { + const wallet = await dev.getBitcoinAccount(WALLET_00, 1); const unspent = await dev.fetchUtxos(wallet.wpkh.address); @@ -49,24 +40,8 @@ test('testnet, fetch utxos', async () => { expect(unspent.length).toBeGreaterThan(0); }); -test('reproduce, not enough funds', async () => { - const utxos = await tnet.fetchUtxos('tb1q3zl64vadtuh3vnsuhdgv6pm93n82ye8qc36c07'); - - const hex = await utxos[0].tx; - console.log('hex', hex); - - const select = await utxoSelect({ - feeRate: await tnet.estimateFeeRate('high'), - outputs: [], - utxos, - utxoToSpendable: DEFAULT_UTXO_TO_SPENDABLE, - }); - - expect(select).toBeDefined(); // did not throw -}); - test('get btc balance', async () => { - const wallet = await dev.getBitcoinAccount(WALLET_00); + const wallet = await dev.getBitcoinAccount(WALLET_00, 1); const balance = await dev.getBalance(wallet.wpkh.address); console.log('balance', balance); @@ -111,3 +86,13 @@ test('peg address compare', async () => { expect(pegPublicKeyA).toEqual(pegPublicKeyB); expect(pegAddressA).toEqual(pegAddressB); }); + +test('bitcoin core rpc returns regtest privatekey wif in testnet format', async () => { + const address = await dev.btcRpc.getnewaddress(); + expect(address.slice(0, 4)).toBe('bcrt'); // is regtest address + + // 80 = mainnet + // ef = testnet + const wif = await dev.btcRpc.dumpprivkey({ address }); + expect(bytesToHex(base58.decode(wif).slice(0, 1))).toBe('ef'); // regtest wif uses testnet prefix +}); diff --git a/packages/sbtc/tests/deposit.test.ts b/packages/sbtc/tests/deposit.test.ts index e507ea11a..e5f51b3d7 100644 --- a/packages/sbtc/tests/deposit.test.ts +++ b/packages/sbtc/tests/deposit.test.ts @@ -1,20 +1,18 @@ import * as btc from '@scure/btc-signer'; import { hexToBytes } from '@stacks/common'; -import { UTXO, buildDepositTransaction } from 'sbtc-bridge-lib'; import { expect, test } from 'vitest'; -import { bytesToHex } from '../../common/src'; import { DevEnvHelper, WALLET_00, sbtcDepositHelper } from '../src'; const dev = new DevEnvHelper(); -test('deposit, tx compare', async () => { - const bitcoinAccount = await dev.getBitcoinAccount(WALLET_00); +test('btc tx, deposit to sbtc, tx compare', async () => { + const bitcoinAccount = await dev.getBitcoinAccount(WALLET_00, 1); const stacksAccount = await dev.getStacksAccount(WALLET_00, 1); const utxos = await dev.fetchUtxos(bitcoinAccount.wpkh.address); const pegAccount = await dev.getBitcoinAccount(WALLET_00); - const pegPublicKey = bytesToHex(pegAccount.tr.publicKey); + // const pegPublicKey = bytesToHex(pegAccount.tr.publicKey); const pegAddress = pegAccount.tr.address; // TODO: SHOULD THIS WORK INSTEAD? public key / address should be fetchable with little knowledge // const pegAddress = await dev.getSbtcPegAddress('ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.asset'); @@ -40,27 +38,29 @@ test('deposit, tx compare', async () => { bitcoinChangeAddress: bitcoinAccount.wpkh.address, }); - const txLib = buildDepositTransaction( - 'development', - pegPublicKey, - { - amountSats: 1000, - bitcoinAddress: bitcoinAccount.wpkh.address, - paymentPublicKey: bytesToHex(bitcoinAccount.publicKey), - principal: stacksAccount.address, - reclaimPublicKey: 'NOT-USED-FOR-OP-DROP', - sbtcWalletPublicKey: pegPublicKey, - }, - { high_fee_per_kb: 3 * 1024, medium_fee_per_kb: 2 * 1024, low_fee_per_kb: 1 * 1024 }, - utxos as UTXO[] - ); + // todo: does regtest work for this yet? + // const txLib = buildDepositTransaction( + // 'development', + // pegPublicKey, + // { + // amountSats: 1000, + // bitcoinAddress: bitcoinAccount.wpkh.address, + // paymentPublicKey: bytesToHex(bitcoinAccount.publicKey), + // principal: stacksAccount.address, + // reclaimPublicKey: 'NOT-USED-FOR-OP-DROP', + // sbtcWalletPublicKey: pegPublicKey, + // }, + // { high_fee_per_kb: 3 * 1024, medium_fee_per_kb: 2 * 1024, low_fee_per_kb: 1 * 1024 }, + // utxos as UTXO[] + // ); // todo: fails right now (0 suffix) // expect(txStacksjs.getOutput(0)).toEqual(txLib.getOutput(0)); // expect(txStacksjs.getOutput(1)).toEqual(txLib.getOutput(1)); + // generated with ./utils/deposit.sh (set to amount=1000) const hexCli = - '010000000001010221549531adce86b1fbcc178c0535d85d90b7f521d284df018b43f5529650730000000000feffffff0300000000000000001b6a1969643c051ab3a14500ad8ac4e6d823ab20fd2c6d1369aa5bb7e8030000000000002251205e682db7c014ab76f2b4fdcbbdb76f9b8111468174cdb159df6e88fe9d078ce6eb2a50090000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e002473044022042ff63f361a5c3be42525a4dbfd8410a4d932449e6f849aebd97b3076f7b3d14022061c80cf8ecbcc2a633370e026a11ae0ad3e0be2f2838e073a15fae5a08fe48f0012103969ff3e2bf7f2f73dc903cd11442032c8c7811d57d96ce327ee89c9edea63fa899040000'; + '0100000000010101717cad71b870970c55e7fa9fdc5ddf09fa928e33bd6b0b10d32d0e3d1c80e10000000000feffffff0300000000000000001b6a1969643c051ab3a14500ad8ac4e6d823ab20fd2c6d1369aa5bb7e8030000000000002251205e682db7c014ab76f2b4fdcbbdb76f9b8111468174cdb159df6e88fe9d078ce6db77814a0000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e00247304402205a43e26a8f372bc97c7c44b045effd23eaaf2a747c0a237082f0505e752dd32f02201610945844e438b4a27c08149d04be3dce544d9318c280f7405ae0e56a2e24e0012103969ff3e2bf7f2f73dc903cd11442032c8c7811d57d96ce327ee89c9edea63fa8d5010000'; const txCli = btc.Transaction.fromRaw(hexToBytes(hexCli), { allowUnknownInputs: true, allowUnknownOutputs: true, @@ -68,10 +68,11 @@ test('deposit, tx compare', async () => { expect(txStacksjs.getOutput(0)).toEqual(txCli.getOutput(0)); expect(txStacksjs.getOutput(1)).toEqual(txCli.getOutput(1)); + expect(txStacksjs.outputsLength).toEqual(txCli.outputsLength); }); test('btc tx, deposit to sbtc, broadcast', async () => { - const bitcoinAccount = await dev.getBitcoinAccount(WALLET_00); + const bitcoinAccount = await dev.getBitcoinAccount(WALLET_00, 1); const stacksAccount = await dev.getStacksAccount(WALLET_00, 1); const pegAccount = await dev.getBitcoinAccount(WALLET_00); diff --git a/packages/sbtc/tests/withdraw.test.ts b/packages/sbtc/tests/withdraw.test.ts index e7420cd4e..42881fadb 100644 --- a/packages/sbtc/tests/withdraw.test.ts +++ b/packages/sbtc/tests/withdraw.test.ts @@ -1,23 +1,18 @@ +import * as btc from '@scure/btc-signer'; import { bytesToHex, hexToBytes } from '@stacks/common'; import { hashMessage } from '@stacks/encryption'; import { createStacksPrivateKey, signMessageHashRsv } from '@stacks/transactions'; -import { test } from 'vitest'; -import { - DevEnvHelper, - MagicBytes, - REGTEST, - WALLET_00, - sbtcWithdrawHelper, - sbtcWithdrawMessage, -} from '../src'; +import { expect, test } from 'vitest'; +import { DevEnvHelper, WALLET_00, sbtcWithdrawHelper, sbtcWithdrawMessage } from '../src'; const dev = new DevEnvHelper(); test('btc tx, withdraw from sbtc, tx compare, broadcast', async () => { - const bitcoinAccountA = await dev.getBitcoinAccount(WALLET_00); - const bitcoinAccountB = await dev.getBitcoinAccount(WALLET_00, 1); // recipient can be anybody + const bitcoinAccountA = await dev.getBitcoinAccount(WALLET_00, 1); // funds the tx, can be anybody const stacksAccount = await dev.getStacksAccount(WALLET_00, 1); + const bitcoinAccountB = await dev.getBitcoinAccount(WALLET_00, 1); // recipient, can be anybody + const pegAccount = await dev.getBitcoinAccount(WALLET_00); const pegAddress = pegAccount.tr.address; @@ -29,7 +24,7 @@ test('btc tx, withdraw from sbtc, tx compare, broadcast', async () => { // - A browser extension could do this step const signature = signMessageHashRsv({ - messageHash: bytesToHex(hashMessage(hexToBytes(message))), + messageHash: bytesToHex(hashMessage(message)), privateKey: createStacksPrivateKey(stacksAccount.stxPrivateKey), }).data; @@ -37,7 +32,6 @@ test('btc tx, withdraw from sbtc, tx compare, broadcast', async () => { // Tx building const txStacksjs = await sbtcWithdrawHelper({ - network: { ...REGTEST, magicBytes: MagicBytes.Testnet }, // CLI uses TESTNET for some reason pegAddress, amountSats: 1_000, @@ -55,21 +49,22 @@ test('btc tx, withdraw from sbtc, tx compare, broadcast', async () => { txStacksjs.sign(bitcoinAccountA.privateKey); // same account as utxo (which are funding the tx) txStacksjs.finalize(); - // // CLI uses TESTNET magic bytes for some reason - // const hexCli = - // '01000000000101eab0bde2677f322225c89af3e4a457c7705e90e6265c839fe6cb1c4e9c6bc0490200000000feffffff0400000000000000004f6a4c4c54323e00000000000003e800083852dc4d33a732f009d4b89e0c77d4bff8b2ee79be58323cafa87bcb0f97e7067e703eb32e3539b374b1eb4d302791d8379067400bb072f2e05511b58a27b7260100000000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e0d0070000000000002251205e682db7c014ab76f2b4fdcbbdb76f9b8111468174cdb159df6e88fe9d078ce67d6f814a0000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e002473044022012a10a937ce3567daea29e4a5464b8553a2093a80d78418cf9240bc2c3f960be022074e97c05a722fde9eab75fe0906944a7379fee46bba565495d4bc19019a0ab39012103969ff3e2bf7f2f73dc903cd11442032c8c7811d57d96ce327ee89c9edea63fa8d4010000'; - // const txCli = btc.Transaction.fromRaw(hexToBytes(hexCli), { - // allowUnknownInputs: true, - // allowUnknownOutputs: true, - // }); + const hexCli = + '010000000001010221549531adce86b1fbcc178c0535d85d90b7f521d284df018b43f5529650730000000000feffffff0400000000000000004f6a4c4c54323e00000000000003e800070a03a62bf8ba00ebe1712ce17546e11a0b62b7e21ee45db6feaa5559951f106b8ae37ee0af60af2378261ac401fb417e46a46ac09d21e939236c77d8c52faa260100000000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e0d0070000000000002251205e682db7c014ab76f2b4fdcbbdb76f9b8111468174cdb159df6e88fe9d078ce68a2550090000000016001488bfaab3ad5f2f164e1cbb50cd07658ccea264e00247304402202252b47a3fc9df631b304017a43eac30e115167549f287f7a0b4449ac372b37102203f2f6b6dd9879a1f2cc69a654ff922f3d2bdac9a6fe85ca13a10f0c6e72c06b4012103969ff3e2bf7f2f73dc903cd11442032c8c7811d57d96ce327ee89c9edea63fa8a0030000'; + const txCli = btc.Transaction.fromRaw(hexToBytes(hexCli), { + allowUnknownInputs: true, + allowUnknownOutputs: true, + }); + // todo: output[0] uses incorrect magic bytes on regtest for withdraw via CLI (uses testnet magic bytes) // expect(txStacksjs.getOutput(0).script!).toEqual(txCli.getOutput(0).script!); - // expect(txStacksjs.getOutput(1).script!).toEqual(txCli.getOutput(1).script!); - // expect(txStacksjs.getOutput(2).script!).toEqual(txCli.getOutput(2).script!); + expect(txStacksjs.getOutput(1).script!).toEqual(txCli.getOutput(1).script!); + expect(txStacksjs.getOutput(2).script!).toEqual(txCli.getOutput(2).script!); + expect(txStacksjs.outputsLength).toEqual(txCli.outputsLength); const txid = await dev.broadcastTx(txStacksjs); console.log('txid', txid); // todo: output[1], can have less amount than signature signs, and sbtc will send less - // todo: sbtc doens't burn the sbtc, the sbtc balance is still on stacks + // todo: sbtc doesn't burn the sbtc, the sbtc balance is still on stacks });