diff --git a/.changeset/violet-masks-hunt.md b/.changeset/violet-masks-hunt.md new file mode 100644 index 00000000..33dfe694 --- /dev/null +++ b/.changeset/violet-masks-hunt.md @@ -0,0 +1,5 @@ +--- +'@stacks/connect': minor +--- + +Add experimental support for BTC transfers diff --git a/lerna.json b/lerna.json index 3c56ac6f..5d6e273b 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "5.0.5", + "version": "independent", "npmClient": "yarn", "command": { "version": { diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts index 0ff17133..03a97540 100644 --- a/packages/connect/src/index.ts +++ b/packages/connect/src/index.ts @@ -1,5 +1,6 @@ export * from './auth'; export * from './transactions'; +export * from './transactions/btc'; // todo: tmp export * from './signature'; export * from './signature/structuredData'; export * from './profile'; diff --git a/packages/connect/src/transactions/btc.ts b/packages/connect/src/transactions/btc.ts new file mode 100644 index 00000000..4daf0752 --- /dev/null +++ b/packages/connect/src/transactions/btc.ts @@ -0,0 +1,55 @@ +import { createUnsecuredToken, Json } from 'jsontokens'; +import { BtcRecipient, BTCTransferOptions, BTCTransferPayload, TransactionTypes } from '../types'; +import { getStacksProvider } from '../utils'; + +const openGenericTransactionPopup = async ({ + token, + options, +}: { + token: string; + options: BTCTransferOptions; // todo: add generic alternative +}) => { + const provider = getStacksProvider(); + if (!provider) { + throw new Error('Hiro Wallet not installed'); + } + + try { + const txResponse = await provider.transactionRequest(token); + + options.onFinish?.(txResponse as any); // todo: add new `onFinish` for parsed BTC tx data + } catch (error) { + console.error('[Connect] Error during transaction request', error); + options.onCancel?.(); + } +}; + +/** + * ⚠ Experimental method for opening a BTC transfer request. This API interface + * subject to change and does not adhere to semantic versioning. + * @experimental + */ +export function openBTCTransfer(options: BTCTransferOptions): Promise { + const { recipients, ..._options } = { + // todo: do we maybe want userSession, stxAddress, ...? + // ...getDefaults(options), + ...options, + }; + + const payLoadRecipients: BtcRecipient[] = []; + recipients.forEach(recipient => { + payLoadRecipients.push({ + recipient: recipient.recipient, + amount: recipient.amount.toString(10), + }); + }); + + const payload: Partial = { + ..._options, + recipients: payLoadRecipients, + txType: TransactionTypes.BTCTransfer, + }; + + const token = createUnsecuredToken(payload as Json); + return openGenericTransactionPopup({ token, options }); +} diff --git a/packages/connect/src/transactions/index.ts b/packages/connect/src/transactions/index.ts index 53300879..8d8eb9f0 100644 --- a/packages/connect/src/transactions/index.ts +++ b/packages/connect/src/transactions/index.ts @@ -74,7 +74,8 @@ export function getStxAddress(options: TransactionOptions) { return address; } -function getDefaults(options: TransactionOptions) { +/** @internal */ +export function getDefaults(options: TransactionOptions) { const network = options.network || new StacksTestnet(); const userSession = getUserSession(options.userSession); diff --git a/packages/connect/src/types/transactions.ts b/packages/connect/src/types/transactions.ts index 4f338e20..e7e8490d 100644 --- a/packages/connect/src/types/transactions.ts +++ b/packages/connect/src/types/transactions.ts @@ -48,6 +48,7 @@ export enum TransactionTypes { ContractCall = 'contract_call', ContractDeploy = 'smart_contract', STXTransfer = 'token_transfer', + BTCTransfer = 'token_transfer_btc', // todo: `btc_transfer` ? (generice interface for selecting token/chain) } /** @@ -153,10 +154,36 @@ export interface STXTransferPayload extends STXTransferBase { * Transaction Popup */ -export type TransactionOptions = ContractCallOptions | ContractDeployOptions | STXTransferOptions; -export type TransactionPayload = ContractCallPayload | ContractDeployPayload | STXTransferPayload; +export type TransactionOptions = + | ContractCallOptions + | ContractDeployOptions + | STXTransferOptions + | BTCTransferOptions; +export type TransactionPayload = + | ContractCallPayload + | ContractDeployPayload + | STXTransferPayload + | BTCTransferPayload; export interface TransactionPopup { token: string; options: TransactionOptions; } + +/** + * BTC Transfer + */ + +export interface BtcRecipient { + recipient: string; + amount: bigint | number | string; +} + +export interface BTCTransferOptions extends RegularOptionsBase { + recipients: BtcRecipient[]; +} + +export interface BTCTransferPayload extends BTCTransferOptions { + recipients: BtcRecipient[]; + txType: TransactionTypes.BTCTransfer; +}