Bitcoin

Examples:

There are two main ways to interact with Bitcoin on ICP: through the management canister and through the ckBTC canister.

management canister

To sign Bitcoin transactions using threshold ECDSA and interact with the Bitcoin blockchain directly from ICP, make cross-canister calls to the following methods on the management canister: ecdsa_public_key, sign_with_ecdsa, bitcoin_get_balance, bitcoin_get_balance_query, bitcoin_get_utxos, bitcoin_get_utxos_query, bitcoin_send_transaction, bitcoin_get_current_fee_percentiles.

To construct your cross-canister calls to these methods, use canister id aaaaa-aa and the management canister's Candid type information to construct the arguments to send in the body of your fetch call.

Here's an example of doing a test cross-canister call to the bitcoin_get_balance method:

import { serialize } from 'azle';

// ...

const response = await fetch(`icp://aaaaa-aa/bitcoin_get_balance`, {
    body: serialize({
        args: [
            {
                'bc1q34aq5drpuwy3wgl9lhup9892qp6svr8ldzyy7c',
                min_confirmations: [],
                network: { regtest: null }
            }
        ],
        cycles: 100_000_000n
    })
});
const responseJson = await response.json();

// ...

ckBTC

ckBTC is an ICRC canister that wraps underlying bitcoin controlled with threshold ECDSA.

ICRCs are a set of standards for ICP canisters that define the method signatures and corresponding types for those canisters.

You interact with the ckBTC canister by calling its methods. You can do this from the frontend with @dfinity/agent, or from an Azle canister through cross-canister calls.

Here's an example of doing a test cross-canister call to the ckBTC icrc1_balance_of method:

import { ic, serialize } from 'azle';

// ...

const response = await fetch(
    `icp://mc6ru-gyaaa-aaaar-qaaaq-cai/icrc1_balance_of`,
    {
        body: serialize({
            candidPath: `/candid/icp/icrc.did`,
            args: [
                {
                    owner: ic.id(),
                    subaccount: [
                        padPrincipalWithZeros(ic.caller().toUint8Array())
                    ]
                }
            ]
        })
    }
);
const responseJson = await response.json();

// ...

function padPrincipalWithZeros(principalBlob: Uint8Array): Uint8Array {
    let newUin8Array = new Uint8Array(32);
    newUin8Array.set(principalBlob);
    return newUin8Array;
}