> ## Documentation Index
> Fetch the complete documentation index at: https://sdk-guide.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Multisig Network Fees

> How the Trustodian SDK estimates fees, and who pays at each step on each chain

Two questions matter for a multisig vault: who pays at each lifecycle step, and how to get a number
before committing. They have different answers on every chain, so this page treats them
separately.

## Pricing activation and execution

`estimateFee` builds the transaction the operation would produce and asks the node
or RPC. It prices both vault operations, so pass the `txType` for the one you want:

```typescript theme={null}
import { TransactionEstimatingType } from 'vaults-multichain-sdk';

const activationFee = await sdk.estimateFee({
  chain: EChainType.EVM,
  txType: TransactionEstimatingType.ActivateMultisig,
  payload: { from: myWalletAddress, multisig },
  chainId: multisig.chainId,
});

const executionFee = await sdk.estimateFee({
  chain: EChainType.EVM,
  txType: TransactionEstimatingType.ExecOrder,
  payload: { from: executorAddress, order, multisig },
  chainId: multisig.chainId,
});
```

`ActivateMultisigInput.feeQuote` is a required field, so pass the real quote:

```typescript theme={null}
await sdk.activateMultisig({
  chain: EChainType.EVM,
  multisigId: multisig.id,
  multisigAddress: multisig.address ?? '',
  walletAddress: myWalletAddress,
  multisig,
  feeQuote: activationFee,
  userRef,
});
```

## `estimateFee` in detail

```typescript theme={null}
const fee = await sdk.estimateFee({
  chain: EChainType.EVM,
  txType: TransactionEstimatingType.ExecOrder,
  payload: {
    from: executorAddress,
    order,
    multisig,
  },
  chainId: '1',
});

fee.estimatedFee;  // "0.000082...", human units of the native asset
fee.currency;      // "ETH"
```

It really does reach the chain: the SDK builds the transaction the operation would produce and
prices it against a node. A chain that cannot quote that operation throws
`estimateFee not supported for chain <chainId>`.

```typescript theme={null}
interface EstimateFeeInput {
  chain: ChainType;
  txType: TransactionEstimatingTypeValue;
  payload: {
    from?: string; to?: string; amount?: string;
    tokenAddress?: string; decimals?: number;
    orderId?: string; multisigId?: string;
    owners?: string[]; threshold?: number;
    order?: Order; multisig?: MultisigsItem;
    method?: any; newOwners?: string[]; newThreshold?: number;
    tokenDecimals?: number; multisigAddress?: string;
  };
  chainId?: ChainIdValue;
  walletScope?: WalletScope;
}
```

### `TransactionEstimatingType`

Import the enum and use its members. The wire values are inconsistently cased (three camelCase,
three PascalCase), so a hand-typed string literal is a silent bug waiting to happen:

```typescript theme={null}
import { TransactionEstimatingType } from 'vaults-multichain-sdk';

txType: TransactionEstimatingType.SignOrder   // correct
txType: 'signOrder'                           // wrong, the value is 'SignOrder'
```

An unrecognised `txType` does not throw. It is priced as a plain transfer instead of what you asked
for, which is why you should use the enum members.

| Member | Value | Required `payload` fields |
| - | - | - |
| `Send` | `'send'` | `to`, `amount`, optionally `tokenAddress` + `decimals` |
| `ExecOrder` | `'execOrder'` | `order` (throws without it), `multisig` or `multisigAddress` |
| `ActivateMultisig` | `'activateMultisig'` | `multisig` (throws without it) |
| `SignOrder` | `'SignOrder'` | `order` (throws without it) |
| `CreateOrder` | `'CreateOrder'` | `multisig` (throws without it), `to`, `amount`, `method` |
| `ChangeMultisig` | `'ChangeMultisig'` | `multisig` + owners + threshold (throws without all three) |

`payload.from` is the wallet the fee is priced against.

### `EstimatedFee`

Only `estimatedFee` is always present. The rest are populated by whichever chain produced the quote:

```typescript theme={null}
interface EstimatedFee {
  estimatedFee: string;        // human units of the native asset
  gasLimit?: string;
  gasPrice?: string;
  nativeFeeLamports?: number;  // Solana
  gasFeeRaw?: string;
  maxFeePerGas?: string;
  maxPriorityFeePerGas?: string;
  energyFee?: string;          // Tron
  bandwidthFee?: string;       // Tron
  priorityFee?: string;
  feeRate?: number;            // Bitcoin, sat/vB
  feeInUsd?: string;
  totalFee?: string;
  currency?: string;
}
```

> [!NOTE]
>
> The chains reuse `gasLimit` / `gasPrice` as generic slots that mean different things per chain.
> Read `estimatedFee` and `currency` for display; read the rest only when you know the chain.

### Which `txType` each chain supports

Even inside `estimateFee`, not every chain prices every operation. This table says which
combinations produce a measured number and which do not:

| `txType` member | EVM | Solana | Tron | Bitcoin | Cardano | XRP |
| - | - | - | - | - | - | - |
| `Send` | measured | measured | measured | measured | measured | computed |
| `ExecOrder` | measured | measured | measured | throws | fixed | computed |
| `ActivateMultisig` | measured | measured | measured | throws | fixed | computed |
| `CreateOrder` | zero | measured | zero | throws | measured | zero |
| `SignOrder` | zero | measured | zero | throws | fixed | fixed |
| `ChangeMultisig` | zero | measured | zero | throws | fixed | fixed |

* *measured*: builds the real transaction and queries the node or RPC. Trustworthy.
* *computed*: no RPC call, but a real formula over real inputs. Trustworthy.
* *zero*: that step costs nothing on that chain, so the quote is `'0'`.
* *fixed*: a constant rather than a measurement, about `0.2` ADA or `0.000014` XRP. Order of
  magnitude only.
* *throws*: `Fee estimation not implemented for type: …`.

> [!NOTE]
>
> A *fixed* cell carries the right currency and the right order of magnitude, but it is a constant
> rather than a measurement. Do not present it as an exact cost.

## Who pays, per chain

| Chain | Create order | Sign / reject | Execute order | Activate vault |
| - | - | - | - | - |
| EVM | free (off-chain) | free (off-chain EIP-712) | executor's wallet pays gas | activating wallet pays deploy gas |
| Solana | creator's wallet: tx fee plus rent for the accounts it opens | signer's wallet | executor's wallet | activating wallet, plus about 0.0021 SOL rent for the vault |
| Tron | free (off-chain) | free (off-chain) | vault balance: bandwidth and energy, plus 100 TRX for settings orders | vault balance, and the vault must already hold at least 100 TRX |
| Bitcoin | free, the fee is baked into the transaction at creation | free (off-chain) | vault UTXOs, fee already included | not applicable |
| Cardano | free (off-chain) | free (off-chain witness) | vault balance | not applicable |
| XRP | free (off-chain) | free (off-chain) | vault balance | vault balance, and the vault must cover its reserve |

> [!TIP]
>
> On Tron, Cardano, XRP, and Bitcoin the fee comes out of the shared vault, so fund the vault before
> executing. On EVM and Solana it comes out of the acting owner's personal wallet, so fund that.

## Per-chain details

What actually costs money differs a lot by chain. This is what you need to plan for.

> **EVM**
>
> Only activation and execution cost gas. Signing is free and produces no transaction.
> Both are paid by the wallet that performs them, never by the vault.
>
> Before executing, the SDK checks that the executor can cover `gasLimit × gasPrice` and throws
> `InsufficientBalanceError` rather than letting a doomed execution proceed:
>
> > Executor `0x…` has 0.0001 ETH but needs \~0.0009 ETH to pay gas. Top it up or execute from
> > another owner wallet.
>
> The check is fail-open: if it cannot read a balance or price the gas, it lets the node be the
> judge rather than blocking a valid execution.

> **Solana**
>
> Solana is the only chain where creating and signing also cost money, because each is its own
> on-chain transaction. Creating an order additionally pays rent for the accounts it opens,
> which dominates the cost.
>
> Two hard floors, independent of any quote:
>
> * Activation transfers about 0.0021 SOL to the vault as a rent top-up, on top of the fee.
> * Execution refuses to start when the executing wallet holds under 0.001 SOL.

> **Tron**
>
> Tron charges bandwidth and energy rather than gas. Each account gets a free daily bandwidth
> allowance and can freeze TRX for more of either, and only the deficit is burned as TRX. A quote
> therefore depends on the vault's current resources and changes over time.
>
> Owner and threshold changes carry a flat 100 TRX system fee. Sending to an account that is
> not yet activated adds about 1 TRX.
>
> > [!WARNING]
> >
> > Separately from any fee, Tron vault activation is refused unless the vault address already
> > holds at least 100 TRX. Fund the vault before calling `activateMultisig`.

> **Bitcoin**
>
> Personal sends are quoted from the current network fee rate against a real coin selection over
> the wallet's UTXOs.
>
> A vault order's fee is decided when the transaction is built and comes out of the vault's own
> UTXOs, so you cannot quote it in advance through `estimateFee`. Read it off the built order
> instead.

> **Cardano**
>
> Fees are computed locally from the Cardano protocol parameters and are small and predictable.
> A vault order costs more than a personal send because every required signature grows the
> transaction.
>
> Every output must clear the 1 ADA minimum. Native-asset transfers need pure-ADA UTXOs to pay
> from, so if all the ADA is locked in UTXOs that also hold tokens, the send is refused with
> *"No pure ADA UTXOs available. Your ADA is locked in UTXOs with tokens, send the tokens first to
> free it."*

> **XRP**
>
> Fees are tiny and computed arithmetically: a multisig transaction on a 3-signer vault costs
> roughly 0.000056 XRP. No RPC call is needed to quote one.
>
> Reserves are not fees. They are XRP locked on the account, not spent:
>
> * Base reserve: 1 XRP.
> * Owner reserve: 0.2 XRP per owned object.
> * An activated vault holds a signer list, so it needs 1.2 XRP minimum. Each open trust line adds
>   another 0.2 XRP; removing the line frees it, but only when the token balance is exactly zero.
>
> Activation is refused below the live required reserve. `sdk.getXrpAccountReserve({ address })`
> returns the current breakdown for a personal wallet.

## Minimum balances

Each chain has a floor below which a wallet cannot transact at all. They are listed with the per-chain
send rules in [Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md#minimum-balances-and-native-currencies).

## Order expiry

Expiry is not a fee, but it decides whether a fee ever gets paid. Three chains time orders out:

| Chain | Lifetime | Set by |
| - | - | - |
| Tron | 23 h 50 min from the transaction fetch | Stamped on the order when the transaction is fetched |
| XRP | 23 h 50 min, also bounded by the ledger sequence | Stamped when the transaction is fetched |
| Cardano | 72 h | Set at order creation |

A lapsed order moves to `expired` and must be recreated. EVM, Solana, and Bitcoin orders do not
expire on a timer.

> **[Multisig Vaults](https://sdk-guide.mintlify.site/sdk-guide/multisig.md)**
>
> Creation and activation, chain by chain.

> **[Orders](https://sdk-guide.mintlify.site/sdk-guide/orders.md)**
>
> The lifecycle these fees attach to.

> **[Transactions](https://sdk-guide.mintlify.site/sdk-guide/transactions.md)**
>
> `estimateFee` for personal-wallet sends.

> **[Errors](https://sdk-guide.mintlify.site/sdk-guide/errors.md)**
>
> `InsufficientBalanceError` and friends.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.