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

# Transaction signing

> Decode, validate, sign, and submit an action's transactions in the right order for the chain you're on.

Every action (`POST /v1/actions/enter`, `/exit`, or `/manage`) returns `transactions[]`, each with an `unsignedTransaction` you sign and submit yourself. We construct. You sign; yield.xyz never holds keys and never signs on your behalf. This guide covers the parts of that contract that are the same across every yield: how to decode `unsignedTransaction` for the chain family you're on, when to validate it before signing, and which of the two submission paths to use. For the request, sort, and poll loop itself, see [Enter and exit flows](/guides/earn/enter-exit-flows).

## Prerequisites

* An action's `transactions[]`, already sorted by `stepIndex` with terminal transactions (`CONFIRMED`, `SKIPPED`, `FAILED`) filtered out. See [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position).
* A signer for the target chain: a raw key for local testing, an embedded/MPC wallet provider (see [Embedded wallets](/guides/integrations/index)), or a custody or HSM backend (see [Custody and signing flows](/guides/enterprise/custody-signing-flows)).

<Note>
  We construct. You sign. Inspect every `unsignedTransaction` before signing it, and never modify its values, whatever library you use to parse or re-key it.
</Note>

## Decode `unsignedTransaction` by chain family

`unsignedTransaction` is a `string` on most chains, but its encoding and structure are chain-family specific. Decode it before signing:

| Chain family | Encoding | Guide |
| - | - | - |
| EVM (Ethereum, Base, Arbitrum, Optimism, Polygon, and other EVM networks) | JSON string, `JSON.parse` before signing | [EVM signing](/guides/transaction-signing/evm) |
| Solana | Serialized transaction bytes, hex or base64 depending on the yield, legacy or versioned (v0) | [Solana signing](/guides/transaction-signing/solana) |
| Cosmos SDK chains | Hex-encoded protobuf `SignDoc` bytes, direct (not amino) signing | [Cosmos signing](/guides/transaction-signing/cosmos) |
| Tron | JSON string of a TronGrid transaction object | [Tron signing](/guides/transaction-signing/tron) |

Other supported chain families return `unsignedTransaction` in their own native encoding; the sign, submit, and poll pattern on this page still applies, decoded with that chain's own SDK.

## Validate before signing (optional)

Before signing, you can run the same validation engine that powers yield.xyz's optional server-side Defense Mode, so a malformed or tampered `unsignedTransaction` is rejected in your own process rather than after a signature exists:

```typescript theme={null}
import { Shield } from "@yieldxyz/shield";

const shield = new Shield();

for (const tx of transactions) {
  const result = shield.validate({
    unsignedTransaction: tx.unsignedTransaction,
    yieldId,
    userAddress,
    args, // the same arguments you sent to the action call
  });

  if (!result.isValid) throw new Error(`Invalid transaction: ${result.reason}`);
}
```

`@yieldxyz/shield` also ships as a standalone binary for non-Node stacks. See [Shield integration](/documentation/platform/shield/integration) for installation and the binary path; this call is the only step relevant to signing.

## Sign and choose a submission path

Sign the decoded transaction with your chain-specific signer (see the per-family guides), then submit through exactly one of two paths for that transaction, never both:

| Path | Call | Use when |
| - | - | - |
| Hand off the signed transaction | `POST /v1/transactions/{transactionId}/submit` with `{ "signedTransaction": ... }` | Your signer has no broadcast path of its own, or you'd rather let yield.xyz's infrastructure broadcast and track it |
| Broadcast yourself | `PUT /v1/transactions/{transactionId}/submit-hash` with `{ "hash": ... }` | You already broadcast through your own RPC or custody provider and only need to report the result |

Both paths are idempotent for a repeat of the exact same signed payload or hash. Submitting a different signed transaction or hash for a transaction that is already at a terminal status returns `412 Precondition Failed`; treat that as already settled and fetch the transaction instead of retrying.

## Wait for confirmation, unless the action is batched

Sign and submit `transactions[]` in `stepIndex` order, waiting for each to reach a terminal transaction status before the next, unless the action's `executionPattern` is `batch`. A batched action returns a single `BATCH` transaction whose `unsignedTransaction` is a JSON array of calls (`{ to, data, value }` per call, one entry per step that a non-batched action would otherwise return as separate transactions) for a smart account or wallet that supports atomic multi-call execution, such as EIP-5792 `wallet_sendCalls`. There is only one transaction to sign and submit, so there is no sequencing to wait on. See [Enter and exit flows](/guides/earn/enter-exit-flows#enter-a-position) for the full branch on `executionPattern`.

## Next steps

<Columns cols={2}>
  <Card title="EVM signing" icon="cube" href="/guides/transaction-signing/evm">
    Parse and sign the JSON transaction with ethers.js or viem.
  </Card>

  <Card title="Solana signing" icon="sun" href="/guides/transaction-signing/solana">
    Detect legacy vs versioned encoding and sign with @solana/web3.js.
  </Card>

  <Card title="Embedded wallets" icon="wallet" href="/guides/integrations/index">
    Wire an MPC or embedded wallet provider into the same sign and submit loop.
  </Card>

  <Card title="Custody and signing flows" icon="building-columns" href="/guides/enterprise/custody-signing-flows">
    Run this contract from a custody or HSM backend instead of a client signer.
  </Card>
</Columns>


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