Note that the “raw sign” functionality signs the provided hash directly without any additional byte manipulation. Ensure that your hash includes any required prefixes or suffixes before signing.
Cosmos utilizes the ECDSA signing algorithm with the secp256k1 curve. Below is an implementation example for signing hashes on Cosmos:
Show Code example
Copy
Ask AI
import { Secp256k1, Secp256k1Signature } from "@cosmjs/amino";// Prepare the hash for signingconst hash = '0x6503b027a625549f7be691646404f275f149d17a119a6804b855bac3030037aa';// Obtain the raw signature from Privy's raw_sign endpointconst rawSignature = ... // call privy raw_sign on `hash`// Retrieve the wallet's public key from Privyconst publicKey = ... // the wallet's public key from Privy// Verify the signatureconst signatureBytes = Secp256k1Signature.fromFixedLength( Buffer.from(rawSignature.slice(2), "hex"));const verified = await Secp256k1.verifySignature( signatureBytes, Buffer.from(hash.slice(2), "hex"), Buffer.from(publicKey, "hex"));console.log("Signature valid?", verified); // true
Sui supports multiple cryptographic schemes, with Privy’s implementation utilizing the ed25519 curve and EdDSA signing algorithm. The following example demonstrates transaction signing for Sui:
Show Code example
Copy
Ask AI
import { messageWithIntent, toSerializedSignature,} from "@mysten/sui/cryptography";import { blake2b } from "@noble/hashes/blake2b";import { verifyTransactionSignature } from "@mysten/sui/verify";// After you've added the data to your transactionconst txBytes = await tx.build({ client });const intentMessage = messageWithIntent("TransactionData", txBytes);const digest = blake2b(intentMessage, { dkLen: 32 });// Convert the digest to a hex string for signingconst hashToSign = '0x' + toHex(digest);// Obtain the raw signature from Privy's raw_sign endpointconst rawSignature = ... // call privy raw_sign on `hashToSign`// Create and verify the transaction signatureconst txSignature = toSerializedSignature({signature: rawSignature,signatureScheme: "ED25519",publicKey,});const signer = await verifyTransactionSignature(txBytes, txSignature, {address});console.log(signer.toSuiAddress() === address); // true
Tron implements the ECDSA signing algorithm using the secp256k1 curve. Privy’s implementation returns 64-byte ECDSA signatures (r || s), while Tron requires 65-byte signatures that include a recovery ID (v) as the final byte.
The recovery ID is essential because a 64-byte signature could correspond to two different addresses/private keys. The 65th byte, which can be either 0x1b or 0x1c (derived from 0 or 1 plus 27, following Ethereum standards), resolves this ambiguity.
The following example demonstrates message signing and verification for Tron:
Show Code example
Copy
Ask AI
import { TronWeb } from "tronweb";import { hashMessage } from "tronweb/utils";// Initialize with the wallet's Tron addressconst address = <the wallet's tron address>;// Determine the recovery ID for signature verificationconst getRecoveryId = async ({ message, rawSignature,}: { message: string; rawSignature: string;}) => { return (await tronWeb.trx.verifyMessageV2(message, rawSignature + '1b')) === address ? '1b' : '1c';};// Initialize TronWebconst tronWeb = new TronWeb({ fullHost: "xxx",});// Prepare and sign the messageconst message = "Hello world";const hash = hashMessage(message);// Obtain the raw signature from Privy's raw_sign endpointconst rawSignature = ... // call privy raw_sign on `hash`// Verify the signature with the recovery IDconst signerAddress = await tronWeb.trx.verifyMessageV2( message, signature + (await getRecoveryId({message, rawSignature}));console.log(signerAddress === address); // true
Bitcoin (segwit) supports the ECDSA signing algorithm using the secp256k1 curve. Use Privy’s raw sign functionality to sign each input utxo for your Bitcoin segwit transaction. Note that segwit support is separate from Bitcoin taproot or legacy transactions.
Show Code example
Copy
Ask AI
import { p2pkh, OutScript } from "@scure/btc-signer/payment";import { getInputType, getPrevOut, Transaction,} from "@scure/btc-signer/transaction";import { concatBytes } from "@scure/btc-signer/utils";import secp256k1 from "secp256k1";const publicKey = <the wallet's public key>;const publicKeyBuffer = Buffer.from(publicKey, "hex");const tx = new Transaction({ version: 1, allowLegacyWitnessUtxo: true });// add as many outputs as needed, in this example there is only onetx.addOutputAddress(outputAddress, outputAmount);tx.addInput({ txid, // buffer of utxo txid index: 0, // index of the output in the tx witnessUtxo: { amount: inputAmount, script: p2pkh(publicKeyBuffer).script, },});for (let i = 0; i < tx.inputsLength; i++) { const input = tx.getInput(i); const inputType = getInputType(input, tx.opts.allowLegacyWitnessUtxo); const prevOut = getPrevOut(input); let script = inputType.lastScript; if (inputType.last.type === "wpkh") { script = OutScript.encode({ type: "pkh", hash: inputType.last.hash }); } const hash = tx.preimageWitnessV0( i, script, inputType.sighash, prevOut.amount );const signature = // call Privy's raw sign function with bytesToHex(hash), returns '0x...' // convert to DER format const derSig = secp256k1.signatureImport(signature); tx.updateInput( i, { partialSig: [ [publicKeyBuffer, concatBytes(derSig, new Uint8Array([inputType.sighash]))], ], }, true );}tx.finalize();return tx;
Assistant
Responses are generated using AI and may contain mistakes.