# Low-level API

The Low-level API provides granular control over the entire transaction lifecycle. Unlike the [High-level API](./api-reference-transaction-helpers.md) which abstracts away the details, these methods allow you to manually construct, encode, sign, and send transactions. This is ideal for advanced scenarios, such as multi-signature workflows where different parties need to sign a transaction before it's broadcasted.

This API is organized into four main groups of methods, each corresponding to a stage in the transaction lifecycle:

1. **Encode**: Prepare a transaction and serialize it into a binary buffer.
2. **Sign**: Add a digital signature to an encoded transaction.
3. **Multi-sign**: Add multiple digital signatures to a transaction.
4. **Send**: Broadcast a signed transaction to the blockchain.


## Encoding Transactions

Encoding is the first step in creating a transaction. The `encode[Type]Tx` methods take the core transaction data (`itx`) and wrap it in a standard transaction structure, adding necessary details like the `chainId` and `nonce`. The result is a human-readable transaction object and a binary buffer ready for signing.

There is an `encode` method for every transaction type supported by the chain. You can get a full list by calling `client.getTxEncodeMethods()`.

### `encode[Type]Tx(payload)`

Encodes a transaction without signing it.

**Parameters**

<x-field-group>
  <x-field data-name="tx" data-type="object" data-required="true">
    <x-field-desc markdown>The transaction data object.</x-field-desc>
    <x-field data-name="itx" data-type="object" data-required="true" data-desc="The inner transaction object specific to the transaction type."></x-field>
    <x-field data-name="from" data-type="string" data-required="false" data-desc="Sender's address. If not provided, it's derived from the wallet."></x-field>
    <x-field data-name="nonce" data-type="number" data-required="false" data-desc="Transaction nonce. Defaults to `Date.now()` if not set."></x-field>
    <x-field data-name="chainId" data-type="string" data-required="false" data-desc="The chain ID. Fetched from the connected node if not provided."></x-field>
  </x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="The wallet object used to derive the sender's address and public key."></x-field>
  <x-field data-name="delegator" data-type="string" data-required="false" data-desc="The address of the account delegating permissions, if applicable."></x-field>
</x-field-group>

**Returns**

<x-field data-name="Promise<object>" data-type="Promise<object>" data-desc="A promise that resolves to an object containing the encoded transaction.">
  <x-field data-name="object" data-type="object" data-desc="The human-readable transaction object."></x-field>
  <x-field data-name="buffer" data-type="Buffer" data-desc="The serialized transaction binary buffer, ready for signing."></x-field>
</x-field>

**Example**

```javascript TransferV2Tx icon=logos:javascript
const { encodeTransferV2Tx } = client;
const senderWallet = fromRandom();
const receiverAddress = 'z1...';

const { object, buffer } = await encodeTransferV2Tx({
  tx: {
    itx: {
      to: receiverAddress,
      value: await client.fromTokenToUnit(10), // Transfer 10 native tokens
    },
  },
  wallet: senderWallet,
});

console.log('Encoded TX Object:', object);
console.log('Buffer to Sign:', buffer.toString('hex'));
```


## Signing Transactions

The `sign[Type]Tx` methods build on the encoding step by adding a digital signature. These methods encode the transaction and then use the provided wallet to sign the resulting binary buffer.

You can get a full list of available signing methods by calling `client.getTxSignMethods()`.

### `sign[Type]Tx(payload)`

Encodes and signs a transaction.

**Parameters**

<x-field-group>
  <x-field data-name="tx" data-type="object" data-required="true" data-desc="The transaction data object, same as for encoding."></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="The wallet used to sign the transaction."></x-field>
  <x-field data-name="delegator" data-type="string" data-required="false" data-desc="The address of the delegator, if applicable."></x-field>
  <x-field data-name="encoding" data-type="string" data-required="false" data-desc="Optional encoding for the output ('base16', 'hex', 'base58', 'base64'). If omitted, returns the transaction object."></x-field>
</x-field-group>

**Returns**

<x-field data-name="Promise<object|string>" data-type="Promise<object|string>" data-desc="A promise that resolves to the signed transaction object, or an encoded string if `encoding` is specified."></x-field>

**Example**

```javascript TransferV2Tx icon=logos:javascript
const { signTransferV2Tx } = client;
const senderWallet = fromRandom();
const receiverAddress = 'z1...';

const signedTx = await signTransferV2Tx({
  tx: {
    itx: {
      to: receiverAddress,
      value: await client.fromTokenToUnit(10),
    },
  },
  wallet: senderWallet,
});

console.log('Signed TX:', signedTx);
```


## Sending Transactions

The `send[Type]Tx` methods are responsible for broadcasting a transaction to the blockchain. These methods can perform the signing step implicitly if an unsigned transaction and a wallet are provided, or they can send a transaction that has already been signed.

A full list of send methods is available via `client.getTxSendMethods()`.

### `send[Type]Tx(payload)`

Signs (if necessary) and sends a transaction to the chain.

**Parameters**

<x-field-group>
  <x-field data-name="tx" data-type="object" data-required="true" data-desc="The transaction object. Can be signed or unsigned."></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="The wallet to sign the transaction. Still required for identifying the sender even if the transaction is pre-signed."></x-field>
  <x-field data-name="signature" data-type="string" data-required="false" data-desc="A pre-computed signature for the transaction. If provided, the wallet will not be used to sign again."></x-field>
  <x-field data-name="delegator" data-type="string" data-required="false" data-desc="The address of the delegator, if applicable."></x-field>
  <x-field data-name="commit" data-type="boolean" data-default="false" data-required="false" data-desc="Whether to wait for the transaction to be committed to a block before resolving."></x-field>
</x-field-group>

**Returns**

<x-field data-name="Promise<string>" data-type="Promise<string>" data-desc="A promise that resolves to the transaction hash."></x-field>

**Example: Auto-Signing**

```javascript TransferV2Tx icon=logos:javascript
const { sendTransferV2Tx } = client;
const senderWallet = fromRandom();
const receiverAddress = 'z1...';

// The client will sign this transaction using senderWallet before sending.
const txHash = await sendTransferV2Tx({
  tx: {
    itx: {
      to: receiverAddress,
      value: await client.fromTokenToUnit(10),
    },
  },
  wallet: senderWallet,
});

console.log('Transaction sent with hash:', txHash);
```

**Example: Sending a Pre-Signed Transaction**

```javascript TransferV2Tx icon=logos:javascript
// Assume signedTx is from the sign[Type]Tx example
const { sendTransferV2Tx } = client;

const txHash = await sendTransferV2Tx({
  tx: signedTx, // Pass the entire signed transaction object
  wallet: senderWallet,
});

console.log('Pre-signed transaction sent with hash:', txHash);
```


## Multi-Signature Transactions

For workflows requiring multiple signatures (like an atomic swap), the `multiSign[Type]Tx` methods are used. The process involves one party signing the transaction first (using a standard `sign[Type]Tx` method), and subsequent parties adding their signatures using the corresponding `multiSign[Type]Tx` method.

You can get a list of transactions that support multiple signatures via `client.getTxMultiSignMethods()`.

### `multiSign[Type]Tx(payload)`

Adds a signature to a transaction that already has one or more signatures.

**Parameters**

<x-field-group>
  <x-field data-name="tx" data-type="object" data-required="true" data-desc="The transaction object, which should already contain at least one signature."></x-field>
  <x-field data-name="wallet" data-type="WalletObject" data-required="true" data-desc="The wallet of the current signer."></x-field>
  <x-field data-name="delegator" data-type="string" data-required="false" data-desc="The address of the delegator for the current signer, if applicable."></x-field>
  <x-field data-name="data" data-type="any" data-required="false" data-desc="Optional data to include with the signature."></x-field>
  <x-field data-name="encoding" data-type="string" data-required="false" data-desc="Optional encoding for the output ('base16', 'hex', 'base58', 'base64')."></x-field>
</x-field-group>

**Returns**

<x-field data-name="Promise<object|string>" data-type="Promise<object|string>" data-desc="A promise that resolves to the transaction object with the new signature added."></x-field>

**Example: Atomic Swap (`ExchangeV2Tx`)**

```javascript ExchangeV2Tx icon=logos:javascript
// Wallets for two parties
const aliceWallet = fromRandom();
const bobWallet = fromRandom();

// 1. Alice prepares and signs the initial exchange transaction
const exchangeTx = {
  itx: {
    to: bobWallet.address,
    sender: {
      value: await client.fromTokenToUnit(10), // Alice offers 10 tokens
    },
    receiver: {
      value: await client.fromTokenToUnit(5), // Alice demands 5 tokens
    },
  },
};

const signedByAlice = await client.signExchangeV2Tx({
  tx: exchangeTx,
  wallet: aliceWallet,
});

// 2. Alice sends `signedByAlice` to Bob. Bob adds his signature.
const signedByBoth = await client.multiSignExchangeV2Tx({
  tx: signedByAlice,
  wallet: bobWallet,
});

// 3. Bob sends `signedByBoth` back to Alice. Alice sends the final transaction.
const txHash = await client.sendExchangeV2Tx({
  tx: signedByBoth,
  wallet: aliceWallet, // The sender wallet is used to submit
});

console.log('Atomic swap transaction sent:', txHash);
```
