Create a Bitcoin Transaction

Who is this tutorial for:

  • API users that DO NOT use their iPhone as the Instruction Key.
  • API users that need to implement the functionality in programming languages other than JavaScript
  • API users that DO NOT want to use our JavaScript SDK.

Brief Outline

  1. Get your subWallet id from the User Wallets Details Query
  2. Create a bitcoin transaction via the Create BTC Transaction Mutation
  3. Verify the transaction and that it was sent by TrustVault
    1. For testing purposes (sandbox environment) you can skip this step
    2. For Production we highly recommend you verify the data
  4. Sign each transaction input
    1. If your signing solution requires the pre-image data then use the input.unverifiedDigestData.signData and use the SHA256 hashing algorithm
    2. If your signing solution can sign hash data then use the input.unverifiedDigestData.shaSignData
  5. Submit the signatures in the same order as the inputs via the Add Signature Mutation
  6. Poll the status of the transaction to confirm it was sent to the network using the Get Request Item Query

JavaScript Example

Below is a brief example for creating, signing and submitting a BTC transaction. Sandbox users can comment out the verifyBitcoinTransaction for fast implementation but MUST implement it for Production. The code for verifyBitcoinTransaction will be made available soon.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
const { ec: EC } = require("elliptic");
const { ApiClient } = require("./api-client");

// For demonstration purposes only. Do not hard code your key, store it somewhere safe.
const privateKey = "43c3324ad96158abd517548d03285c79253e681b4fc80f530f5c1a195a6f7e51";
// Create your key pair from your private key which you need for signing
const keyPair = new EC("p256").keyFromPrivate(privateKey);

// For demonstration purposes only. Do not hard code your API key, store it somewhere safe.
const userApiKey = "test-api-key";
// Production
const prodUrl = "https://tapi-production.trustology-prod.com/graphql";
// API reflects the postman query written in JavaScript
const apiClient = new ApiClient(userApiKey, prodUrl);

/**
* Sends a bitcoin transaction with the given parameters
* 1. Creates the bitcoin transaction from the fromSubWalletId with the given parameters
* 2. Validates the returned transaction
* 3. Signs and submits the key signature pairs
* 2. Polls the request status to see if it has been successfully submitted to the network
*
* @param {string} fromSubWalletId - the unique identifier retrieved from the User Wallets Query where the bitcoin transaction will be sent from
* @param {string} toAddress - the address where the bitcoin transaction that will be sent to (we support P2SH, P2PKH, P2WPKH, P2SH-P2WPKH address types)
* @see https://bitcoin.stackexchange.com/a/64852 for an explanation of different bitcoin addresses
* @param {string} amount - the amount to send in satoshi (integer string)
* @param {string} speed - "FAST" | "MEDIUM" | "SLOW"
* @returns {string} requestId - the unique identifier of the created bitcoin transaction request needed for transaction tracking
*/
async function sendBitcoin(fromSubWalletId, toAddress, amount, speed) {
// call createBitcoinTransaction mutation with the parameters to get a well formed bitcoin transaction
const result = await apiClient.createBitcoinTransaction(fromSubWalletId, toAddress, amount, speed);
if (!result.signData || !result.requestId) {
console.error(`Failed to create bitcoin transaction ${JSON.stringify(result)}`);
throw new Error("Failed to create bitcoin transaction");
}
// IMPORTANT: PRODUCTION users are highly recommended to verify the bitcoin transaction (input/outputs are correct and were sent by TrustVault)
verifyBitcoinTransaction(result.signData, fromSubWalletId, toAddress, amount);

// IMPORTANT: PRODUCTION users are highly recommended to NOT use the unverifiedDigestData but instead recreate the digests
const unverifiedSignedDataDigests: string[] = result.signData.transaction.inputs.map(
// If your signing solution requires the pre-image data then use the `input.unverifiedDigestData.signData`.
(input) => input.unverifiedDigestData.shaSignData,
);

// Sign each signRequest with your key pair
const signRequests = unverifiedSignedDataDigests.map((signedDigest) => {
// using you private key pair, sign the digest.
const { r, s } = keyPair.sign(signedDigest);
// convert the r, s bytes signature to hex format
const hexSignature = r.toString("hex", 64) + s.toString("hex", 64);
return {
publicKeySignaturePairs: [
{
publicKey: keyPair.getPublic("hex"), // should be in hex string format
signature: hexSignature, // should be in hex string format
},
],
};
});

// submit the addSignature payload and receive back the requestId of your bitcoin transaction request
const requestId = await apiClient.addSignature({
requestId: result.requestId,
signRequests,
});

// Check that your transaction was successfully submitted to the network
const expectedStatus = "SUBMITTED";
const status = await pollRequestStatus(requestId, expectedStatus);
console.info(`request (${requestId}) - status: ${status}`);

return requestId;
}