import { DynamicWidget, useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { isBitcoinWallet } from '@dynamic-labs/bitcoin';
import * as bitcoin from 'bitcoinjs-lib';
import { hexToBytes, utf8ToBytes } from '@stacks/common';
import { sha256 } from '@noble/hashes/sha256';
import ecc from '@bitcoinerlab/secp256k1';
import { useState } from "react";
bitcoin.initEccLib(ecc);
function isString(value) {
return typeof value === 'string';
}
const bip322MessageTag = 'BIP0322-signed-message';
const messageTagHash = Uint8Array.from([
...sha256(utf8ToBytes(bip322MessageTag)),
...sha256(utf8ToBytes(bip322MessageTag)),
]);
export const bip322TransactionToSignValues = {
prevoutHash: hexToBytes('0000000000000000000000000000000000000000000000000000000000000000'),
prevoutIndex: 0xffffffff,
sequence: 0,
};
export function hashBip322Message(message) {
return sha256(
Uint8Array.from([...messageTagHash, ...(isString(message) ? utf8ToBytes(message) : message)])
);
}
const generatePsbt = (address, message) => {
const { prevoutHash, prevoutIndex, sequence } = bip322TransactionToSignValues;
// Generate the script for the given address
const script = bitcoin.address.toOutputScript(
address,
bitcoin.networks.bitcoin
);
// Hash the message
const hash = hashBip322Message(message);
// Create the scriptSig with the hashed message
const commands = [0, Buffer.from(hash)];
const scriptSig = bitcoin.script.compile(commands);
// Create a virtual transaction to spend
const virtualToSpend = new bitcoin.Transaction();
virtualToSpend.version = 0;
virtualToSpend.addInput(Buffer.from(prevoutHash), prevoutIndex, sequence, scriptSig);
virtualToSpend.addOutput(script, 0);
// Create the PSBT
const virtualToSign = new bitcoin.Psbt({ network: bitcoin.networks.bitcoin });
virtualToSign.setVersion(0);
const prevTxHash = virtualToSpend.getHash();
const prevOutIndex = 0;
const toSignScriptSig = bitcoin.script.compile([bitcoin.script.OPS.OP_RETURN]);
try {
virtualToSign.addInput({
hash: prevTxHash,
index: prevOutIndex,
sequence: 0,
witnessUtxo: { script, value: 0 },
});
} catch (e) {
console.log(e);
throw e;
}
virtualToSign.addOutput({ script: toSignScriptSig, value: 0 });
return virtualToSign.toBase64();
}
const SignMessageViaTransaction = () => {
const { primaryWallet } = useDynamicContext();
const [message, setMessage] = useState('Hello World');
if (!isBitcoinWallet(primaryWallet)) {
return null;
}
const signMessageViaTransaction = async () => {
// Get the Bitcoin address from the wallet
const address = await primaryWallet.address;
// Generate the PSBT
const psbt = generatePsbt(address, message);
// Define the parameters for signing the PSBT
const params = {
allowedSighash: [1], // Only allow SIGHASH_ALL
unsignedPsbtBase64: psbt, // The unsigned PSBT in Base64 format
signature: [{
address, // The address that is signing
signingIndexes: [0] // The index of the input being signed
}]
};
try {
// Request the wallet to sign the PSBT
const signedPsbt = await primaryWallet.signPsbt(params);
console.log(signedPsbt); // Log the signed PSBT
} catch (e) {
console.log(e); // Handle any errors that occur during signing
}
}
return (
<div>
<button onClick={signMessageViaTransaction}>Sign Transaction Via Message</button>
</div>
)
}
const Main = () => {
return (
<div className="min-h-screen bg-gradient-to-b from-gray-900 to-black flex flex-col items-center justify-center text-white">
<DynamicWidget />
<SignMessageViaTransaction />
</div>
);
}
export default Main;