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
17 changes: 17 additions & 0 deletions packages/core/src/signer/btc/signerBtc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,21 @@ export abstract class SignerBtc extends Signer {
tx.setWitnessArgsAt(info.position, witness);
return tx;
}

/**
* Signs a Partially Signed Bitcoin Transaction (PSBT).
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
* @todo Add support for Taproot signing options (useTweakedSigner, etc.)
*/
abstract signPsbt(psbtHex: string): Promise<string>;

/**
* Broadcasts a signed PSBT to the Bitcoin network.
*
* @param psbtHex - The hex string of signed PSBT to broadcast
* @returns A promise that resolves to the transaction ID
*/
abstract pushPsbt(psbtHex: string): Promise<string>;
}
8 changes: 8 additions & 0 deletions packages/core/src/signer/btc/signerBtcPublicKeyReadonly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,12 @@ export class SignerBtcPublicKeyReadonly extends SignerBtc {
async getBtcPublicKey(): Promise<Hex> {
return this.publicKey;
}

async signPsbt(_: string): Promise<string> {
throw new Error("Read-only signer does not support signPsbt");
}

async pushPsbt(_: string): Promise<string> {
throw new Error("Read-only signer does not support pushPsbt");
}
}
71 changes: 71 additions & 0 deletions packages/joy-id/src/btc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,16 @@ export class BitcoinSigner extends ccc.SignerBtc {
throw new Error("Not connected");
}

// Additional validation to ensure connection has valid address
if (
!this.connection.address ||
typeof this.connection.address !== "string"
) {
throw new Error(
"Invalid connection - missing or invalid Bitcoin address",
);
}

return this.connection;
}

Expand Down Expand Up @@ -198,4 +208,65 @@ export class BitcoinSigner extends ccc.SignerBtc {
);
return signature;
}

/**
* Signs a PSBT using JoyID wallet.
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
*/
async signPsbt(psbtHex: string): Promise<string> {
const { address } = await this.assertConnection();

const config = this.getConfig();
const { tx: signedPsbtHex } = await createPopup(
buildJoyIDURL(
{
...config,
tx: psbtHex,
signerAddress: address,
autoFinalized: true,
},
"popup",
"/sign-psbt",
),
{ ...config, type: DappRequestType.SignPsbt },
);

return signedPsbtHex;
}

/**
* Signs and broadcasts a PSBT to the Bitcoin network using JoyID wallet.
*
* This method combines both signing and broadcasting in a single operation.
*
* @param psbtHex - The hex string of PSBT to sign and broadcast
* @returns A promise that resolves to the transaction ID
*
* @remarks
* Use this method directly for sign+broadcast operations to avoid double popups.
* While calling signPsbt() then pushPsbt() will still work, it triggers two popups and requires double signing.
*/
async pushPsbt(psbtHex: string): Promise<string> {
const { address } = await this.assertConnection();

const config = this.getConfig();
const { tx: txid } = await createPopup(
buildJoyIDURL(
{
...config,
tx: psbtHex,
signerAddress: address,
autoFinalized: true, // sendPsbt always finalizes
isSend: true,
},
"popup",
"/sign-psbt",
),
{ ...config, type: DappRequestType.SignPsbt }, // Use SignPsbt type for both operations
);

return txid;
}
}
17 changes: 15 additions & 2 deletions packages/okx/src/advancedBarrel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@ import { Nip07A } from "@ckb-ccc/nip07/advanced";
import { UniSatA } from "@ckb-ccc/uni-sat/advanced";

export interface BitcoinProvider
extends Pick<UniSatA.Provider, "on" | "removeListener" | "signMessage">,
Partial<Omit<UniSatA.Provider, "on" | "removeListener" | "signMessage">> {
extends Pick<
UniSatA.Provider,
"on" | "removeListener" | "signMessage" | "signPsbt" | "pushPsbt"
>,
Partial<
Omit<
UniSatA.Provider,
| "on"
| "removeListener"
| "signMessage"
| "signPsbt"
| "pushPsbt"
| "pushTx"
>
> {
connect?(): Promise<{
address: string;
publicKey: string;
Expand Down
20 changes: 20 additions & 0 deletions packages/okx/src/btc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,24 @@ export class BitcoinSigner extends ccc.SignerBtc {

return this.provider.signMessage(challenge, "ecdsa");
}

/**
* Signs a PSBT using OKX wallet.
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
*/
async signPsbt(psbtHex: string): Promise<string> {
return this.provider.signPsbt(psbtHex);
}

/**
* Broadcasts a signed PSBT to the Bitcoin network.
*
* @param psbtHex - The hex string of signed PSBT to broadcast
* @returns A promise that resolves to the transaction ID
*/
async pushPsbt(psbtHex: string): Promise<string> {
return this.provider.pushPsbt(psbtHex);
}
}
17 changes: 17 additions & 0 deletions packages/uni-sat/src/advancedBarrel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@
* Interface representing a provider for interacting with accounts and signing messages.
*/
export interface Provider {
/**
* Signs a PSBT using UniSat wallet.
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
* @todo Add support for Taproot signing options (useTweakedSigner, etc.)
*/
signPsbt(psbtHex: string): Promise<string>;

/**
* Broadcasts a signed PSBT to the Bitcoin network.
*
* @param psbtHex - The hex string of signed PSBT to broadcast
* @returns A promise that resolves to the transaction ID
*/
pushPsbt(psbtHex: string): Promise<string>;

/**
* Requests user accounts.
* @returns A promise that resolves to an array of account addresses.
Expand Down
20 changes: 20 additions & 0 deletions packages/uni-sat/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,24 @@ export class Signer extends ccc.SignerBtc {

return this.provider.signMessage(challenge, "ecdsa");
}

/**
* Signs a PSBT using UniSat wallet.
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
*/
async signPsbt(psbtHex: string): Promise<string> {
return this.provider.signPsbt(psbtHex);
}

/**
* Broadcasts a signed PSBT to the Bitcoin network.
*
* @param psbtHex - The hex string of signed PSBT to broadcast
* @returns A promise that resolves to the transaction ID
*/
async pushPsbt(psbtHex: string): Promise<string> {
return this.provider.pushPsbt(psbtHex);
}
}
22 changes: 22 additions & 0 deletions packages/utxo-global/src/btc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,26 @@ export class SignerBtc extends ccc.SignerBtc {
this.accountCache ?? (await this.getBtcAccount()),
);
}

/**
* Signs a PSBT using UTXO Global wallet.
*
* @param psbtHex - The hex string of PSBT to sign
* @returns A promise that resolves to the signed PSBT hex string
* @todo Implement PSBT signing with UTXO Global
*/
async signPsbt(_: string): Promise<string> {
throw new Error("UTXO Global PSBT signing not implemented yet");
}

/**
* Broadcasts a signed PSBT to the Bitcoin network.
*
* @param psbtHex - The hex string of signed PSBT to broadcast
* @returns A promise that resolves to the transaction ID
* @todo Implement PSBT broadcasting with UTXO Global
*/
async pushPsbt(_: string): Promise<string> {
throw new Error("UTXO Global PSBT broadcasting not implemented yet");
}
}
8 changes: 8 additions & 0 deletions packages/xverse/src/signer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,12 @@ export class Signer extends ccc.SignerBtc {
)
).signature;
}

async signPsbt(_: string): Promise<string> {
throw new Error("Not implemented");
}

async pushPsbt(_: string): Promise<string> {
throw new Error("Not implemented");
}
}