Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
886300b
feat: support Taproot account in KeyringHandler.ts
Battambang Jan 20, 2026
447f0e5
test: fix Taproot compatibility current KeyringHandler.ts unit tests
Battambang Jan 21, 2026
3764391
test: add Taproot KeyringHandler unit tests
Battambang Jan 21, 2026
0a69195
test: remove obsolete skipped tests and unused imports
Battambang Jan 21, 2026
4984a50
test: fix current integration tests compatibility
Battambang Jan 21, 2026
38afe7a
test: add Taproot integration tests
Battambang Jan 21, 2026
598ccf8
Merge remote-tracking branch 'origin/main' into feat/taproot
Battambang Feb 6, 2026
773784a
chore: update unit test and fix linter warning
Battambang Feb 6, 2026
501da4f
chore: separate assert from act for better readability
Battambang Feb 9, 2026
81a37f3
chore: add TEMPLATE_PSBT as a shared exported constant for integratio…
Battambang Feb 9, 2026
40f6243
fix: remove KeyringRequest typecast from integration tests
Battambang Feb 9, 2026
0381062
refactor: extract trace helpers from createAccount
Battambang Feb 10, 2026
8dd7895
refactor: extract to #assertSupportedAddressType to deduplicate valid…
Battambang Feb 10, 2026
278273f
refactor: extract to #resolveAccountIndex method from createAccount
Battambang Feb 10, 2026
9f86cf1
refactor: extract to #resolveAddressType method to flatten createAcco…
Battambang Feb 10, 2026
1336726
fix: address the invalid Bech32m checksum in regtest P2TR recipient a…
Battambang Feb 10, 2026
4066f32
fix: revert createAccount() refactoring, move it to another PR
Battambang Feb 10, 2026
13c3bcf
Merge branch 'main' into feat/taproot
Battambang Feb 10, 2026
7372244
refactor: reapply the previous refactoring of createAccount()
Battambang Feb 10, 2026
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
13 changes: 13 additions & 0 deletions packages/snap/integration-test/blockchain-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,17 @@ export class BlockchainTestUtils {
const balanceSats = await this.getBalance(address);
return Number(balanceSats) / 100_000_000;
}

/**
* Generate a new Bitcoin address from the regtest node wallet.
*
* @param addressType - The address type to generate. Defaults to 'bech32'.
* Valid values: 'legacy', 'p2sh-segwit', 'bech32', 'bech32m'.
* @returns A new Bitcoin address.
*/
async getNewAddress(
addressType: 'legacy' | 'p2sh-segwit' | 'bech32' | 'bech32m' = 'bech32',
): Promise<string> {
return this.#execCli(`-rpcwallet=default getnewaddress "" ${addressType}`);
}
}
11 changes: 11 additions & 0 deletions packages/snap/integration-test/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ export const TEST_ADDRESS_REGTEST =
'bcrt1qjtgffm20l9vu6a7gacxvpu2ej4kdcsgcgnly6t';
export const TEST_ADDRESS_MAINNET =
'bc1q832zlt4tgnqy88vd20mazw77dlt0j0wf2naw8q';

// P2TR (Taproot) addresses
export const TEST_ADDRESS_P2TR_MAINNET =
'bc1p4rue37y0v9snd4z3fvw43d29u97qxf9j3fva72xy2t7hekg24dzsaz40mz';
export const TEST_ADDRESS_P2TR_TESTNET =
'tb1pwwjax3vpq6h69965hcr22vkpm4qdvyu2pz67wyj8eagp9vxkcz0q0ya20h';

export const ORIGIN = 'metamask';
export const FUNDING_TX = {
account: expect.any(Number),
Expand Down Expand Up @@ -50,3 +57,7 @@ export const scopeToCoinType: Record<BtcScope, string> = {
[BtcScope.Signet]: "1'",
[BtcScope.Regtest]: "1'",
};

// PSBTs can be decoded here: https://bitcoincore.tech/apps/bitcoinjs-ui/index.html
export const TEMPLATE_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAAA=';
63 changes: 25 additions & 38 deletions packages/snap/integration-test/keyring-request.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { KeyringAccount, KeyringRequest } from '@metamask/keyring-api';
import type { KeyringAccount } from '@metamask/keyring-api';
import { BtcScope } from '@metamask/keyring-api';
import type { Snap } from '@metamask/snaps-jest';
import { assertIsConfirmationDialog, installSnap } from '@metamask/snaps-jest';

import { BlockchainTestUtils } from './blockchain-utils';
import { MNEMONIC, ORIGIN } from './constants';
import { MNEMONIC, ORIGIN, TEMPLATE_PSBT } from './constants';
import { getRequiredOutpoint } from './test-helpers';
import { AccountCapability } from '../src/entities';
import type { FillPsbtResponse } from '../src/handlers/KeyringRequestHandler';

Expand Down Expand Up @@ -81,7 +82,7 @@ describe('KeyringRequestHandler', () => {
request: {
method: AccountCapability.SignPsbt,
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand All @@ -104,7 +105,7 @@ describe('KeyringRequestHandler', () => {
request: {
method: 'invalidMethod',
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand Down Expand Up @@ -134,7 +135,7 @@ describe('KeyringRequestHandler', () => {
request: {
method: AccountCapability.ListUtxos,
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -155,6 +156,7 @@ describe('KeyringRequestHandler', () => {
const utxos = (
response.response as { result: { result: { outpoint: string }[] } }
).result.result;
const outpoint = getRequiredOutpoint(utxos);

response = await snap.onKeyringRequest({
origin: ORIGIN,
Expand All @@ -167,10 +169,10 @@ describe('KeyringRequestHandler', () => {
request: {
method: AccountCapability.GetUtxo,
params: {
outpoint: utxos[0]?.outpoint,
outpoint,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -191,7 +193,7 @@ describe('KeyringRequestHandler', () => {
request: {
method: AccountCapability.PublicDescriptor,
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -203,9 +205,6 @@ describe('KeyringRequestHandler', () => {
});

describe('signPsbt', () => {
// PSBTs can be decoded here: https://bitcoincore.tech/apps/bitcoinjs-ui/index.html
const TEMPLATE_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAAA=';
const SIGNED_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAA==';

Expand All @@ -229,7 +228,7 @@ describe('KeyringRequestHandler', () => {
},
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand Down Expand Up @@ -261,7 +260,7 @@ describe('KeyringRequestHandler', () => {
},
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand Down Expand Up @@ -293,7 +292,7 @@ describe('KeyringRequestHandler', () => {
},
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand Down Expand Up @@ -324,7 +323,7 @@ describe('KeyringRequestHandler', () => {
},
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand Down Expand Up @@ -353,7 +352,7 @@ describe('KeyringRequestHandler', () => {
psbt: TEMPLATE_PSBT,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand All @@ -366,10 +365,6 @@ describe('KeyringRequestHandler', () => {
});

describe('fillPsbt', () => {
// PSBTs can be decoded here: https://bitcoincore.tech/apps/bitcoinjs-ui/index.html
const TEMPLATE_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAAA=';

it('fills a PSBT successfully', async () => {
const response = await snap.onKeyringRequest({
origin: ORIGIN,
Expand All @@ -386,7 +381,7 @@ describe('KeyringRequestHandler', () => {
feeRate: 3,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -412,7 +407,7 @@ describe('KeyringRequestHandler', () => {
psbt: 'notAPsbt',
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand All @@ -428,10 +423,6 @@ describe('KeyringRequestHandler', () => {
});

describe('computeFee', () => {
// PSBTs can be decoded here: https://bitcoincore.tech/apps/bitcoinjs-ui/index.html
const TEMPLATE_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAAA=';

it('computes the fee for a PSBT successfully', async () => {
const response = await snap.onKeyringRequest({
origin: ORIGIN,
Expand All @@ -448,7 +439,7 @@ describe('KeyringRequestHandler', () => {
feeRate: 3,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -474,7 +465,7 @@ describe('KeyringRequestHandler', () => {
psbt: 'notAPsbt',
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand All @@ -490,10 +481,6 @@ describe('KeyringRequestHandler', () => {
});

describe('broadcastPsbt', () => {
// PSBTs can be decoded here: https://bitcoincore.tech/apps/bitcoinjs-ui/index.html
const TEMPLATE_PSBT =
'cHNidP8BAI4CAAAAAAM1gwEAAAAAACJRIORP1Ndiq325lSC/jMG0RlhATHYmuuULfXgEHUM3u5i4AAAAAAAAAAAxai8AAUSx+i9Igg4HWdcpyagCs8mzuRCklgA7nRMkm69rAAAAAAAAAAAAAQACAAAAACp2AAAAAAAAFgAUgpMvYEJ/dp36svRJyRtNnpSo7bQAAAAAAAAAAAA=';

it('broadcasts a PSBT successfully', async () => {
// Prepare the PSBT to broadcast so we have a valid PSBT to broadcast
let response = await snap.onKeyringRequest({
Expand All @@ -515,7 +502,7 @@ describe('KeyringRequestHandler', () => {
},
},
},
} as KeyringRequest,
},
});

const { result } = (
Expand All @@ -536,7 +523,7 @@ describe('KeyringRequestHandler', () => {
psbt: result.psbt,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -562,7 +549,7 @@ describe('KeyringRequestHandler', () => {
psbt: 'notAPsbt',
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand Down Expand Up @@ -603,7 +590,7 @@ describe('KeyringRequestHandler', () => {
feeRate: 3,
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWith({
Expand All @@ -629,7 +616,7 @@ describe('KeyringRequestHandler', () => {
recipients: [{ address: 'notAnAddress', amount: '1000' }],
},
},
} as KeyringRequest,
},
});

expect(response).toRespondWithError({
Expand Down Expand Up @@ -657,7 +644,7 @@ describe('KeyringRequestHandler', () => {
message: 'Hello, world!',
},
},
} as KeyringRequest,
},
});

const ui = await response.getInterface();
Expand Down
Loading