Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 1 addition & 11 deletions modules/sdk-coin-stx/src/lib/sbtcWithdrawBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import { decodeBtcAddress, isValidBtcAddress } from './btcAddressUtils';

const SBTC_TOKEN_CONTRACT_NAME = 'sbtc-token';
const SBTC_TOKEN_ASSET_NAME = 'sbtc-token';
const HASHBYTES_BUFFER_LENGTH = 32;

export class SbtcWithdrawBuilder extends AbstractContractBuilder {
private _withdrawParams: SbtcWithdrawParams | undefined;
Expand Down Expand Up @@ -130,20 +129,11 @@ export class SbtcWithdrawBuilder extends AbstractContractBuilder {

private withdrawParamsToFunctionArgs(params: SbtcWithdrawParams) {
const decoded = decodeBtcAddress(params.btcAddress);

// Pad 20-byte hashes to 32 bytes with trailing zeros per sBTC contract spec (buff 32)
let hashBytes = decoded.hashBytes;
if (hashBytes.length < HASHBYTES_BUFFER_LENGTH) {
const padded = Buffer.alloc(HASHBYTES_BUFFER_LENGTH, 0);
hashBytes.copy(padded);
hashBytes = padded;
}

return [
uintCV(params.amount),
tupleCV({
version: bufferCV(Buffer.from([decoded.version])),
hashbytes: bufferCV(hashBytes),
hashbytes: bufferCV(decoded.hashBytes),
}),
uintCV(params.maxFee),
];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import should from 'should';
import { pubKeyfromPrivKey, publicKeyToString } from '@stacks/transactions';
import { ClarityType, pubKeyfromPrivKey, publicKeyToString } from '@stacks/transactions';
import { ContractCallPayload } from '@stacks/transactions/dist/payload';

import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { BitGoAPI } from '@bitgo/sdk-api';
Expand Down Expand Up @@ -52,6 +53,18 @@ describe('Stacks: sBTC Withdraw Builder', function () {
should.exist(txJson.payload);
txJson.payload.should.have.property('contractName', 'sbtc-withdrawal');
txJson.payload.should.have.property('functionName', 'initiate-withdrawal-request');

// Verify hashbytes is 20 bytes for P2PKH
const payload = (tx as StxLib.Transaction).stxTransaction.payload as ContractCallPayload;
const recipientTuple = payload.functionArgs[1];
should.equal(recipientTuple.type, ClarityType.Tuple);
if (recipientTuple.type === ClarityType.Tuple) {
const hashbytes = recipientTuple.data['hashbytes'];
should.equal(hashbytes.type, ClarityType.Buffer);
if (hashbytes.type === ClarityType.Buffer) {
hashbytes.buffer.length.should.equal(20);
}
}
});

it('a withdrawal with P2SH address', async () => {
Expand All @@ -68,6 +81,16 @@ describe('Stacks: sBTC Withdraw Builder', function () {
const tx = await builder.build();
const txJson = tx.toJson();
txJson.payload.should.have.property('functionName', 'initiate-withdrawal-request');

// Verify hashbytes is 20 bytes for P2SH
const payload = (tx as StxLib.Transaction).stxTransaction.payload as ContractCallPayload;
const recipientTuple = payload.functionArgs[1];
if (recipientTuple.type === ClarityType.Tuple) {
const hashbytes = recipientTuple.data['hashbytes'];
if (hashbytes.type === ClarityType.Buffer) {
hashbytes.buffer.length.should.equal(20);
}
}
});

it('a withdrawal with P2WPKH (bech32) address', async () => {
Expand All @@ -84,6 +107,16 @@ describe('Stacks: sBTC Withdraw Builder', function () {
const tx = await builder.build();
const txJson = tx.toJson();
txJson.payload.should.have.property('functionName', 'initiate-withdrawal-request');

// Verify hashbytes is 20 bytes for P2WPKH
const payload = (tx as StxLib.Transaction).stxTransaction.payload as ContractCallPayload;
const recipientTuple = payload.functionArgs[1];
if (recipientTuple.type === ClarityType.Tuple) {
const hashbytes = recipientTuple.data['hashbytes'];
if (hashbytes.type === ClarityType.Buffer) {
hashbytes.buffer.length.should.equal(20);
}
}
});

it('a withdrawal with P2WSH (bech32) address', async () => {
Expand All @@ -100,6 +133,16 @@ describe('Stacks: sBTC Withdraw Builder', function () {
const tx = await builder.build();
const txJson = tx.toJson();
txJson.payload.should.have.property('functionName', 'initiate-withdrawal-request');

// Verify hashbytes is 32 bytes for P2WSH
const payload = (tx as StxLib.Transaction).stxTransaction.payload as ContractCallPayload;
const recipientTuple = payload.functionArgs[1];
if (recipientTuple.type === ClarityType.Tuple) {
const hashbytes = recipientTuple.data['hashbytes'];
if (hashbytes.type === ClarityType.Buffer) {
hashbytes.buffer.length.should.equal(32);
}
}
});

it('a withdrawal with P2TR (bech32m) address', async () => {
Expand All @@ -116,6 +159,16 @@ describe('Stacks: sBTC Withdraw Builder', function () {
const tx = await builder.build();
const txJson = tx.toJson();
txJson.payload.should.have.property('functionName', 'initiate-withdrawal-request');

// Verify hashbytes is 32 bytes for P2TR
const payload = (tx as StxLib.Transaction).stxTransaction.payload as ContractCallPayload;
const recipientTuple = payload.functionArgs[1];
if (recipientTuple.type === ClarityType.Tuple) {
const hashbytes = recipientTuple.data['hashbytes'];
if (hashbytes.type === ClarityType.Buffer) {
hashbytes.buffer.length.should.equal(32);
}
}
});
});

Expand Down
Loading