Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Add testnet4 support (`CaipScope.TESTNET4`, `Chain.TESTNET4`, `scopeToChain` mapping)
- Support `ordinals` purpose in `bitcoin:connect` to return P2TR addresses

## [1.0.0]

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ const { accounts } = await metamask_wallet.features['bitcoin:connect'].connect({
purposes: ['payment'],
})

// Request a taproot account for ordinals
const { accounts: ordinalAccounts } = await metamask_wallet.features['bitcoin:connect'].connect({
purposes: ['ordinals'],
})

wallet.features['bitcoin:events'].on('change', (event => {
// Handle events
}));
Expand Down Expand Up @@ -191,6 +196,12 @@ const connectResponse = await WalletV4.request('getAddresses', {
message: 'Address for receiving BTC',
});

// Connect and get a taproot address for ordinals
const ordinalsResponse = await WalletV4.request('getAddresses', {
purposes: ['ordinals'],
message: 'Address for receiving ordinals',
});

if (connectResponse.status === 'error') {
throw new Error(connectResponse.error.message);
}
Expand Down
24 changes: 24 additions & 0 deletions src/wallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,30 @@ describe('MetamaskWallet', () => {
},
});
});

it('should return P2TR address type for ordinals purpose', async () => {
await reconnectAndSetAccount(address);

const result = await wallet.features[BitcoinSatsConnect].provider.request('getAddresses', {
purposes: [AddressPurpose.Ordinals],
});

expect(result).toMatchObject({
jsonrpc: '2.0',
result: {
addresses: [
{
address,
publicKey: Buffer.from(address).toString('hex'),
purpose: AddressPurpose.Ordinals,
addressType: AddressType.p2tr,
walletType: WalletType.SOFTWARE,
},
],
network: expectedMainnetNetwork,
},
});
});
});

describe('getAccounts', () => {
Expand Down
15 changes: 7 additions & 8 deletions src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,9 +436,6 @@ export class MetaMaskWallet implements Wallet {
}

const { purposes } = payload as unknown as BitcoinConnectInput;
if (purposes.length !== 1 || purposes.at(0) !== AddressPurpose.Payment) {
throw new Error(`Only payment addresses are supported. Received: ${purposes.join(', ')}`);
}

await this.#connect();

Expand All @@ -447,7 +444,7 @@ export class MetaMaskWallet implements Wallet {
}

return {
addresses: this.accounts.map(this.#standardAccountToSatsAccount),
addresses: this.accounts.map((account) => this.#standardAccountToSatsAccount(account, purposes)),
};
},

Expand Down Expand Up @@ -519,7 +516,8 @@ export class MetaMaskWallet implements Wallet {
if (method === 'wallet_requestPermissions') {
return success(true);
}
const addresses = this.accounts.map(this.#standardAccountToSatsAccount);
const reqPurposes = (options as Record<string, unknown>)?.purposes as readonly string[] | undefined;
const addresses = this.accounts.map((account) => this.#standardAccountToSatsAccount(account, reqPurposes));
if (method === 'getAccounts') {
return success(addresses);
}
Expand Down Expand Up @@ -656,12 +654,13 @@ export class MetaMaskWallet implements Wallet {
};
};

#standardAccountToSatsAccount(account: WalletStandardWalletAccount): Address {
#standardAccountToSatsAccount(account: WalletStandardWalletAccount, purposes?: readonly string[]): Address {
const isOrdinals = purposes?.includes('ordinals');
return {
address: account.address,
publicKey: Buffer.from(account.publicKey).toString('hex'),
purpose: AddressPurpose.Payment,
addressType: AddressType.p2wpkh,
purpose: isOrdinals ? AddressPurpose.Ordinals : AddressPurpose.Payment,
addressType: isOrdinals ? AddressType.p2tr : AddressType.p2wpkh,
walletType: WalletType.SOFTWARE,
};
}
Expand Down
Loading