Transaction Basics

Working with transactions and its various options

Transactions are used to send assets from one wallet to another and to smart contracts.

To initiate a transaction, we import the Transaction class from the @meshsdk/core package and assign the wallet to the initiator property. We build the transaction with .build() constructs the transaction and returns a transaction CBOR. Behind the scenes, it selects all of the necessary inputs belonging to the wallet, calculates the fee for this transaction and returns the remaining assets to the change address. Use wallet.signTx() to sign transaction CBOR.

The verbose is optional and set to false by default, setting it to true will enable verbose logging for the txBodyJson prior going into build.

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet, fetcher: provider, verbose: true });
tx.foo(...); // add transaction methods
tx.bar(...); // add transaction methods

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

In this page, you will find the APIs to create transactions for sending assets and various options to customize the transaction.

Send Lovelace

You can chain the component to send lovelace to multiple recipients. For each recipients, append:

tx.sendLovelace(address: string, lovelace: string);
Send Lovelace

Send lovelace to a recipient

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet })
  .sendLovelace(
    'addr_test1vpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0c7e4cxr',
    '1000000'
  );

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Send Assets

You can chain the component to send assets to multiple recipients. For each recipients, append:

tx.sendAssets(address: string, assets: Asset[]);

The Asset object is defined as:

{
  unit: '{policy_ID}{asset_name_in_hex}',
  quantity: '1',
}

The unit field is the policy ID and asset name in hex format. The quantity field is the amount of the asset to send.

Send Assets

Send assets to a recipient

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.sendAssets(
  { address: 'addr_test1vpvx0sacufuypa2k4sngk7q40zc5c4npl337uusdh64kv0c7e4cxr' },
  [
    {
      unit: 'd9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e',
      quantity: '1',
    },
  ]
);
const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Send Value

Specify an output for the transaction. This funcion allows you to design the output UTXOs, either by splitting the outputs from multiple UTxOs or by creating reference inputs.

sendValue() is useful when working with smart contracts, when you want to redeem a UTxO from the script.

tx.sendValue(address: Recipient, value: UTxO);

where UTxO has the following format (use one of our providers):

{
  input: {
      outputIndex: number;
      txHash: string;
  };
  output: {
      address: string;
      amount: Asset[];
      dataHash?: string;
      plutusData?: string;
      scriptRef?: string;
  };
}
Send value

Add UTXO as input and send to a recipient. In this demo, the first UTXO from your wallet is selected.

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.sendValue(recipient, UTxO);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Multi-signature Transaction

The main idea of a multi-signature transaction is to have multiple signatures to authorize a transaction.

Here are a few scenarios where multi-signature transactions are useful:

  • you want to create a transaction with a backend and send to the frontend for the user to sign
  • when you use a forge script which requires the user to pay for the minting fee
  • when redeeming an asset from a script
const tx = new Transaction({ initiator: wallet });
tx.mintAsset(forgingScript, asset);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx, true);
const signedTx2 = await mintingWallet.signTx(signedTx, true);

In the above code snippet, we are signing the transaction with the user wallet and then signing the transaction with the minting wallet. ThesignTx function is used to sign the transaction. The second argument is a boolean value that indicates whether the transaction is a multi-signature transaction.

await wallet.signTx(unsignedTx, true);
Multi-signature Transaction

Create a multi-signature transaction. In this demo, we will create a transaction with two signatures, where one signature is from the user wallet and the other is from a minting wallet.

import { MeshWallet, ForgeScript, Mint, Transaction } from '@meshsdk/core';

const mintingWallet = new MeshWallet({
  networkId: 0,
  fetcher: provider,
  submitter: provider,
  key: {
    type: "mnemonic",
    words: demoMnemonic,
  },
});
const forgingScript = ForgeScript.withOneSignature(
  mintingWallet.getChangeAddress(),
);

const usedAddress = await wallet.getUsedAddresses();
const address = usedAddress[0];

const asset: Mint = {
  assetName: "MeshToken",
  assetQuantity: "1",
  metadata: demoAssetMetadata,
  label: "721",
  recipient: address,
};

const tx = new Transaction({ initiator: wallet });
tx.mintAsset(forgingScript, asset);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx, true);
const signedTx2 = await mintingWallet.signTx(signedTx, true);
const txHash = await wallet.submitTx(signedTx2);

Connect wallet to run this demo

No wallets installed

Coin selection

There are currently three coin selection algorithms available:

  • Keep Relevant
  • Largest First
  • Largest First Multi-Asset

Keep Relevant

keepRelevant is a two-step coin selection algorithm. First, given a Map (of requested assets and respective quantities) and a set of UTxOs, it tries to eliminate the irrelevant UTxOs from the set. Next, it checks that this UTxO set includes enough lovelace to cover all/any multi-assets in the set. If the set does not include enough lovelace, then it will try to also pick up another UTxO from the wallet, containing the largest amount of lovelace.

keepRelevant = (
  requestedOutputSet: Map<Unit, Quantity>,
  initialUTxOSet: UTxO[],
  minimumLovelaceRequired = '5000000',
);

Here is an example how you can use keepRelevant():

import { keepRelevant } from '@meshsdk/core';
import type { Unit, Quantity } from '@meshsdk/core';

const utxos = await wallet.getUtxos();

const assetMap = new Map<Unit, Quantity>();
assetMap.set(
  'd9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e',
  '1'
);
// if you need to include lovelace
assetMap.set(
  'lovelace',
  '10000000'
);

const selectedUtxos = keepRelevant(assetMap, utxos);

Largest First

To select UTXOs for transaction that only requires lovelace, use largestFirst.

largestFirst = (
  lovelace: Quantity, initialUTxOSet: UTxO[], includeTxFees = false,
  { maxTxSize, minFeeA, minFeeB } = DEFAULT_PROTOCOL_PARAMETERS,
): UTxO[]

For example, selecting the UTXOs for sending 10000000 lovelace:

import { largestFirst } from '@meshsdk/core';

const utxos = await wallet.getUtxos();

const costLovelace = '10000000';
const selectedUtxos = largestFirst(costLovelace, utxos, true);

Largest First Multi-Asset

largestFirstMultiAsset allows you to define which native assets you require for sending out by defining a Map. The Map is matches the Unit with the quantity of each asset.

largestFirstMultiAsset = (
  requestedOutputSet: Map<Unit, Quantity>, initialUTxOSet: UTxO[],
  includeTxFees = false, parameters = DEFAULT_PROTOCOL_PARAMETERS,
): UTxO[]

Note that if lovelace, aside from the "minimum Ada" which in any case needs to accompany the other assets, this must be explicitly specified. This can also be useful in the case that your transaction only requires transfer of lovelace. In this case, the algorithm will exclude all multiasset UTxOs from the selection, which can result in more efficient selection of the required UTxOs.

import { largestFirstMultiAsset } from '@meshsdk/core';
import type { Unit, Quantity } from '@meshsdk/core';

const utxos = await wallet.getUtxos();

const assetMap = new Map<Unit, Quantity>();
assetMap.set(
  'd9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e',
  '1'
);
// if you need to include lovelace
assetMap.set(
  'lovelace',
  '10000000'
);
// if you need to include more than 1 native asset
assetMap.set(
  'another asset unit',
  '1'
);

const selectedUtxos = largestFirstMultiAsset(assetMap, utxos, true);

The third parameter is includeTxFees. If True, Mesh will calculate the fees required for the transaction, and include additional UTxOs to necessary to fulfill the fees requirements.

Coin selection - Keep Relevant

Select the UTXOs with a two-step coin selection algorithm

const utxos = await wallet.getUtxos();

const assetMap = new Map<Unit, Quantity>();
assetMap.set("lovelace", '15000000');
assetMap.set('d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e', "1");

const selectedUtxos = keepRelevant(assetMap, utxos);

Connect wallet to run this demo

No wallets installed
Coin selection - Largest First

Select the UTXOs with the most ADA first

const utxos = await wallet.getUtxos();
const selectedUtxos = largestFirst('15000000', utxos, true);

Connect wallet to run this demo

No wallets installed
Coin selection - Largest First Multi-Asset

Select the UTXOs with the most ADA and asset first

const utxos = await wallet.getUtxos();

const assetMap = new Map<Unit, Quantity>();
assetMap.set("lovelace", '15000000');
assetMap.set('d9312da562da182b02322fd8acb536f37eb9d29fba7c49dc172555274d657368546f6b656e', "1");

const selectedUtxos = largestFirstMultiAsset(assetMap, utxos, true);

Connect wallet to run this demo

No wallets installed

Get Txbuilder

The TxBuilder is a powerful low-level APIs that allows you to build and sign transactions. You can get a new instance of TxBuilder by calling txBuilder. Doing so allows you to access the low-level APIs of TxBuilder, check the docs for all the available methods.

const tx = new Transaction({ initiator: wallet });
tx.foo();

tx.txBuilder.bar()

For example, you can get the meshTxBuilderBody to retrieve the transaction JSON. This is useful for debugging and understanding how the transaction is built.

tx.txBuilder.meshTxBuilderBody;

Send Assets to Handle

Send assets to a handle. Initialize a provider and fetch the address of a handle.

The handle should be a valid handle on the mainnet.

const provider = new BlockfrostProvider('API_KEY_HERE');
const address = await provider.fetchHandleAddress('mesh');

const tx = new Transaction({ initiator: wallet });
tx.sendLovelace(address, '1000000');
Send ADA to handle

Send assets to a handle. (Note: this demo only works on mainnet)

import { BlockfrostProvider, Transaction } from '@meshsdk/core';

const provider = new BlockfrostProvider('API_KEY_HERE');
const address = await provider.fetchHandleAddress('mesh');

const tx = new Transaction({ initiator: wallet });
tx.sendLovelace(address, '1000000');

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Send Assets to Begin ID

Send assets to a Begin ID. Initialize BeginProvider and fetch the address.

Must be a valid name on the mainnet.

const provider = new BeginProvider();
const beginId = await provider.resolveAddress('mesh');
const address = beginId.address;

const tx = new Transaction({ initiator: wallet });
tx.sendLovelace(address, '1000000');
Send ADA to Begin ID

Send assets to a Begin ID. (Note: this demo only works on mainnet)

import { BeginProvider, Transaction } from '@meshsdk/core';

const provider = new BeginProvider();
const beginId = await provider.resolveAddress('mesh');
const address = beginId.address;

const tx = new Transaction({ initiator: wallet });
tx.sendLovelace(address, '1000000');

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Set Metadata

Add messages/comments/memos as transaction metadata. This is useful for attaching additional information to a transaction.

tx.setMetadata(0, 'Your message here');
Transaction message

Add messages/comments/memos as transaction metadata

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.setMetadata(0, 'This is a message from the Mesh SDK');

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Transaction message

Add messages/comments/memos as transaction metadata. This is useful for attaching additional information to a transaction.

tx.setMetadata(674, { msg: ['Multi-line', 'Message', 'Here'] });

The specification for the individual strings follow the general design specification for JSON metadata, which is already implemented and in operation on the cardano blockchain. The used metadatum label is 674:, this number was chosen because it is the T9 encoding of the stringmsg. The message content has the key msg: and consists of an array of individual message-strings. The number of theses message-strings must be at least one for a single message, more for multiple messages/lines. Each of theses individual message-strings array entries must be at most 64 bytes when UTF-8 encoded.

Transaction message

Add messages/comments/memos as transaction metadata

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.setMetadata(674, {
  msg: [
    'Invoice-No: 1234567890',
    'Customer-No: 555-1234',
  ],
});

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Set Collateral

Specify the UTXOs that you want to use as collateral.

tx.setCollateral(collateral: UTxO[]);
Set collateral

Set the UTXOs that you want to use as collateral. In this demo, the first UTXO from your wallet is selected.

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.setCollateral([utxo]);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Set Required Signers

Sets the required signers for the transaction. This is useful when you want to include multiple signers, such as in a multi-signature transaction or smart contracts.

tx.setRequiredSigners(addresses: string[]);
Set required signers

Set the signers for this transaction. In this demo, every used addresses in your wallet is selected.

import { Transaction } from '@meshsdk/core';

const tx = new Transaction({ initiator: wallet });
tx.setRequiredSigners(['addr1...']);

const unsignedTx = await tx.build();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);

Connect wallet to run this demo

No wallets installed

Set Start and Expire Time

We can define the time-to-live (TTL) for the transaction. TTL is the time limit for our transaction to be included in a blockchain, if it is not in a blockchain by then the transaction will be cancelled. This time limit is defined as slot.

In order to get the slot of the time you wish the transaction would expire, you can use resolveSlotNo. For example, if you would like the transaction to expire in 5 minutes, you can get the slot in the following way:

import { resolveSlotNo } from '@meshsdk/core';
let minutes = 5; // add 5 minutes
let nowDateTime = new Date();
let dateTimeAdd5Min = new Date(nowDateTime.getTime() + minutes*60000);
const slot = resolveSlotNo('mainnet', dateTimeAdd5Min.getTime());

Next, we set the TTL with setTimeToExpire and providing the slot, this means that if the transaction is submitted after after slot will not be valid.

const tx = new Transaction({ initiator: wallet });
// do tx.sendLovelace() or any transactions actions
tx.setTimeToExpire(slot)
const unsignedTx = await tx.build();

Likewise, we can set a "validity start interval" for the transaction, where it is the time the transaction will be valid. We can define the start time with setTimeToStart and providing the slot:

const tx = new Transaction({ initiator: wallet });
// do tx.sendLovelace() or any transactions actions
tx.setTimeToStart(slot)
const unsignedTx = await tx.build();

Note that, if you are using a policy locking script, you must define setTimeToExpire before the expiry; otherwise, you will catch the ScriptWitnessNotValidatingUTXOW error.

Set Network

Sets the network to use, this is mainly to know the cost models to be used to calculate script integrity hash. You can set the network for the transaction with setNetwork.

tx.setNetwork(network: Network)

The network parameter is a string that can be one of the following:

"testnet" | "preview" | "preprod" | "mainnet"