Cardano API Usage Example

View an example showing how to send a transaction using Blockdaemon Cardano Staking API.

In this guide, you will find a TypeScript example showing how to send a Cardano transaction using the Cardano Staking API.

import * as CardanoWasm from '@emurgo/cardano-serialization-lib-nodejs';
import * as cbor from 'cbor';

const apiAddress = 'https://svc.blockdaemon.com/boss';

// Function to decode a hex-encoded transaction string into a TransactionBody object
function decodeTransaction(
    transaction: string,
): CardanoWasm.TransactionBody {
    return CardanoWasm.TransactionBody.from_bytes(
        Buffer.from(transaction, 'hex'),
    );
}

// Function to sign a transaction body with a list of keys, returning a full Transaction object
function signTransaction(
    transactionBody: CardanoWasm.TransactionBody,
    keys: CardanoWasm.PrivateKey[],
): CardanoWasm.Transaction {
    const transactionHash = CardanoWasm.hash_transaction(transactionBody);
    const witnesses = CardanoWasm.TransactionWitnessSet.new();

    const vkeyWitnesses = CardanoWasm.Vkeywitnesses.new();

    keys.forEach(key => {
        vkeyWitnesses.add(CardanoWasm.make_vkey_witness(transactionHash, key));
    });
    witnesses.set_vkeys(vkeyWitnesses);

    return CardanoWasm.Transaction.new(transactionBody, witnesses);
}

// Function to fetch Cardano variables from the environment
function getCardanoVariables() {
    if (!process.env["BOSS_API_KEY"]) {
        throw new Error('Please set the BOSS_API_KEY env variable.');
    }

    if (!process.env["PAYMENT_PRIVATE_KEY"]) {
        throw new Error('Please set the PAYMENT_PRIVATE_KEY env variable. It should be base58 encoded');
    }

    if (!process.env["STAKE_PRIVATE_KEY"]) {
        throw new Error('Please set the STAKE_PRIVATE_KEY env variable. It should be base58 encoded');
    }

    return {
        paymentKey:  CardanoWasm.PrivateKey.from_normal_bytes(cbor.decode( Buffer.from(process.env["PAYMENT_PRIVATE_KEY"],'hex'))),
        stakeKey:   CardanoWasm.PrivateKey.from_normal_bytes(cbor.decode( Buffer.from(process.env["STAKE_PRIVATE_KEY"],'hex'))),
        bossApiKey: process.env["BOSS_API_KEY"],
    };
}

// Helper function to create a base address
function getBaseAddress(paymentKey, stakeKey) {
    return CardanoWasm.BaseAddress.new(
        0, // network number 0 for preprod and 1 for mainnet
        CardanoWasm.StakeCredential.from_keyhash(paymentKey.to_public().hash()),
        CardanoWasm.StakeCredential.from_keyhash(stakeKey.to_public().hash()),
    );
}

// Helper function to handle common logic across transaction types
async function processTransaction(transactionType, requestOptions) {
    const { paymentKey, stakeKey, bossApiKey } = getCardanoVariables();
    const baseAddress = getBaseAddress(paymentKey, stakeKey);

    const request = {
        base_address: baseAddress.to_address().to_bech32(),
    };
    requestOptions.body = JSON.stringify(request);

    const response = await fetch(`${apiAddress}/v1/cardano/preprod/${transactionType}`, requestOptions);

    if (!response.ok) {
        throw new Error('Response from BOSS API was not ok: ' + await response.text());
    }

    const responseJson = await response.json();

    if (!responseJson.cardano) {
        throw new Error('Response from BOSS API did not include cardano field: ' + JSON.stringify(responseJson));
    }

    const decodedTransaction = decodeTransaction(responseJson.cardano.unsigned_transaction);
    const signedTransaction = signTransaction(decodedTransaction, [paymentKey, stakeKey]);
    const signedTransactionHex = Buffer.from(signedTransaction.to_bytes()).toString('hex');

    const submitResponse = await fetch(`${apiAddress}/v1/cardano/preprod/transaction-submission`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': bossApiKey
        },
        body: JSON.stringify({
            signed_transaction: signedTransactionHex,
        }),
    });

    if (!submitResponse.ok) {
        throw new Error('Response from transaction submission was not ok: ' + await submitResponse.text());
    }

    return await submitResponse.json();
}

// Function to create a stake intent
async function createStake() {
    return await processTransaction('stake-intents', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': process.env["BOSS_API_KEY"],
        },
    });
}

// Function to create a deactivation intent
async function deactivateStake() {
    return await processTransaction('deactivation-intents', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': process.env["BOSS_API_KEY"]
        },
    });
}

// Function to create a rewards withdrawal intent
async function rewardsWithdrawal() {
    return await processTransaction('rewards/withdrawal-intents', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'X-API-Key': process.env["BOSS_API_KEY"],
        },
    });
}

createStake()
    .then(() => process.exit(0))
    .catch(err => {
        console.error(err);
        process.exit(1);
    });

👋 Need Help?

Contact us through email or our support page for any issues, bugs, or assistance you may need.