> ## 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.

# Sequence and Trails

> Discover, enter, track and exit yields with Sequence embedded wallets batching each step, and power Trails Earn cross-chain deposits with yield.xyz.

This guide builds a Next.js flow where users discover a yield across DeFi, staking and tokenized real-world assets, enter it, track the position and exit, signing with the Sequence embedded wallet. Sequence wallets are smart wallets, so every step of an action, such as an approval and a deposit, can go out as one atomic transaction with a single confirmation, and gas can be sponsored.

The second part covers Trails, Sequence's intent-based cross-chain rails, and how yield.xyz supplies the discovery, transaction building and position tracking behind Trails Earn.

## Resources

* [Sequence embedded wallet](https://docs.sequence.xyz/sdk/headless-wallet/quickstart)
* [Trails quickstart](https://docs.trails.build/)
* [Enter and exit flows](/guides/earn/enter-exit-flows) and [EVM signing](/guides/transaction-signing/evm)

## Prerequisites

* A yield.xyz API key. See [Projects & API keys](/documentation/get-started/projects-api-keys).
* A Sequence project access key and WaaS config key from Sequence Builder.

## Getting started with Sequence

Install the SDK and sign the user in. Social login, email and guest sign-in are supported, with no seed phrase.

```bash theme={null}
pnpm install @0xsequence/waas
```

```typescript theme={null}
import { SequenceWaaS } from "@0xsequence/waas";

const sequence = new SequenceWaaS({
  projectAccessKey: process.env.NEXT_PUBLIC_SEQUENCE_PROJECT_ACCESS_KEY!,
  waasConfigKey: process.env.NEXT_PUBLIC_SEQUENCE_WAAS_CONFIG_KEY!,
  network: "base",
});

await sequence.signIn({ idToken }, "Yield session");
const address = await sequence.getAddress();
```

A Sequence wallet address is the same across every app on the same Sequence project.

## How yield.xyz fits

yield.xyz gives you one API across 4,000+ yields on 85+ networks:

* **Discover:** `GET /v1/yields` returns yields with their metadata.
* **Act:** `POST /v1/actions/enter`, `/exit` and `/manage` return an ordered list of unsigned transactions.
* **Track:** `POST /v1/yields/{yieldId}/balances` and `POST /v1/yields/balances` return lifecycle-aware balances.

yield.xyz builds transactions and never signs or holds funds. Sequence signs them in the user's wallet. This guide uses EVM networks.

## Keep the API key on your server

Keep your yield.xyz API key on your server. The snippets in this guide call a small proxy route that adds the key, for example a Next.js route handler:

```typescript theme={null}
// app/api/yield/[...path]/route.ts
export async function handler(req: Request, { params }: { params: { path: string[] } }) {
  const url = new URL(req.url);
  const res = await fetch(`https://api.yield.xyz/${params.path.join("/")}${url.search}`, {
    method: req.method,
    headers: { "content-type": "application/json", "x-api-key": process.env.YIELD_API_KEY! },
    body: req.method === "GET" ? undefined : await req.text(),
  });
  return new Response(res.body, { status: res.status, headers: { "content-type": "application/json" } });
}
export { handler as GET, handler as POST, handler as PUT };
```

On the client, one helper calls it:

```typescript theme={null}
export async function yieldApi(path: string, body?: unknown, method = body ? "POST" : "GET") {
  const res = await fetch(`/api/yield${path}`, {
    method,
    headers: { "content-type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
  });
  const json = await res.json();
  if (!res.ok) throw Object.assign(new Error(json.message ?? `yield.xyz ${res.status}`), { status: res.status, body: json });
  return json;
}
```

## Discover a yield

The discovery response already carries what your UI needs, so you don't need a second request per yield.

```typescript theme={null}
const { items } = await yieldApi("/v1/yields?network=base&token=USDC&limit=10");

const selected = items[0];
const yieldId = selected.id;
const name = selected.metadata.name;
const rate = selected.rewardRate.total;                        // current estimated rate
const tvlUsd = selected.statistics?.tvlUsd;
const enterFields = selected.mechanics.arguments.enter.fields;  // what enter needs
const kycRequired = selected.mechanics.requirements?.kycRequired ?? false;
```

Every field on a yield is described in [Yield metadata](/documentation/earn/core-concepts/yield-metadata).

## Find yields to offer

Rather than hardcoding yield IDs, which change as yields are added and retired, query the catalog for what you want to offer:

```bash theme={null}
# Stablecoin and DeFi yields on Base
curl "https://api.yield.xyz/v1/yields?network=base&token=USDC&types=lending,vault" \
  -H "x-api-key: $YIELD_API_KEY"

# Native staking
curl "https://api.yield.xyz/v1/yields?type=staking&limit=100" \
  -H "x-api-key: $YIELD_API_KEY"

# Tokenized real-world assets
curl "https://api.yield.xyz/v1/yields?type=real_world_asset&networks=ethereum,base" \
  -H "x-api-key: $YIELD_API_KEY"
```

Add `prime=true` to return only yields that meet the [Prime](/documentation/earn/core-concepts/yield-metadata) quality bar. For staking, default users to [preferred validators](/documentation/earn/staking/validators/preferred-validators). For DeFi, wrap yields in an [OAV](/documentation/platform/vaults/oav-v3) to charge fees and auto-compound incentives (see [Monetization](#monetization)).

## Enter a yield

Build the action, map its transactions to Sequence's format and send them as one batch. Sequence simulates the batch first and does not broadcast it if it would fail.

```typescript theme={null}
import { SequenceWaaS, isSentTransactionResponse, Transaction } from "@0xsequence/waas";

const TERMINAL = ["CONFIRMED", "FAILED", "SKIPPED"];

async function signAndTrack(action: any) {
  const steps = [...action.transactions]
    .sort((a, b) => a.stepIndex - b.stepIndex)
    .filter((tx) => !TERMINAL.includes(tx.status)); // e.g. an approval already covered
  if (!steps.length) return action.id;

  const parsed = steps.map((tx) => JSON.parse(tx.unsignedTransaction));
  const chainId = parsed[0].chainId;
  const batch: Transaction[] = parsed.map((p) => ({ to: p.to, data: p.data, value: p.value ?? "0" }));

  // Sequence smart wallets need a fee quote unless the contract is sponsored in Sequence Builder
  const { data: fee } = await sequence.feeOptions({ transactions: batch, network: chainId });
  const result = await sequence.sendTransaction({
    chainId,
    transactions: batch,
    ...(fee.feeQuote && fee.feeOptions?.length
      ? { transactionsFeeQuote: fee.feeQuote, transactionsFeeOption: fee.feeOptions[0] }
      : {}),
  });

  if (!isSentTransactionResponse(result)) {
    // Simulation failed: nothing was broadcast and no gas was spent
    throw new Error(`Transaction would fail: ${result.data.error}`);
  }

  // The batch executed every step in one onchain transaction: report its hash for each step
  for (const tx of steps) {
    await yieldApi(`/v1/transactions/${tx.id}/submit-hash`, { hash: result.data.txHash }, "PUT");
  }
  return action.id;
}

const action = await yieldApi("/v1/actions/enter", { yieldId, address, arguments: { amount: "100" } });
await signAndTrack(action);
```

To make the flow gasless for users, sponsor the yield contracts or your users' wallets in Sequence Builder and leave out the fee quote. Show the fee option in your UI if you charge users for gas.

Build the `arguments` from `mechanics.arguments.enter.fields` rather than hardcoding `{ amount }`, so the same code works for yields that also need a `validatorAddress` or another field. Amounts are in token units, for example `"100"` for 100 USDC. See [Enter and exit flows](/guides/earn/enter-exit-flows).

## KYC-gated RWA yields

Tokenized real-world assets, such as US Treasuries, money market funds and private credit, use the same enter and exit flow, so no new signing code is needed. Some issuers require the wallet to be allowlisted first, shown by `mechanics.requirements.kycRequired: true` on the yield.

Check the wallet when it connects, so you can show "Verify identity" instead of "Deposit" for a gated yield:

```typescript theme={null}
const kyc = await yieldApi(`/v1/yields/${yieldId}/kyc/status?address=${address}`);
// kyc.kycStatus: not_required | not_started | pending | approved | rejected
// kyc.authorizeUrl: the issuer's KYC portal, when the issuer provides one
```

Send the user to `authorizeUrl` when the status is `not_started` or `rejected`, ideally in a new tab or after saving the pending deposit so you can resume it, and enter once it is `approved`. If you call enter for a wallet that is not cleared, the API returns `412` with `RwaKycError`, and the message includes the issuer's KYC URL:

```typescript theme={null}
try {
  await signAndTrack(await yieldApi("/v1/actions/enter", { yieldId, address, arguments: { amount: "100000" } }));
} catch (e: any) {
  if (e.status === 412) {
    // Not allowlisted yet: fetch kyc/status and hand the user to authorizeUrl
  } else throw e;
}
```

Read `entryLimits.minimum` before you build the action: many RWA yields have high minimums. Issuer eligibility is in [RWA KYC](/documentation/earn/rwas/kyc) and on each issuer's page under [RWAs](/documentation/earn/rwas/index).

## Read the position balance

Balances are lifecycle-aware: each one has a `type` that says whether funds are `active`, `entering`, `exiting`, `withdrawable`, `claimable` or `locked`.

```typescript theme={null}
const { balances } = await yieldApi(`/v1/yields/${yieldId}/balances`, { address });
for (const b of balances) {
  console.log(b.type, b.amount, b.token.symbol, b.pendingActions?.map((p: any) => p.type));
}
```

For many yields or wallets at once, use `POST /v1/yields/balances` with `{ "queries": [{ "address", "network" }] }`, up to 25 queries per request:

```typescript theme={null}
const all = await yieldApi("/v1/yields/balances", {
  queries: [{ address, network: "base" }], // every yield on Base for this wallet
});
```

See [Tracking balances](/guides/earn/tracking-balances).

## Handle pending actions

A balance can carry `pendingActions`, such as `CLAIM_REWARDS` or `CLAIM_UNSTAKED`. Pass one back to `POST /v1/actions/manage` unchanged, then sign the result the same way as an enter:

```typescript theme={null}
const pending = balances.find((b: any) => b.pendingActions?.length)?.pendingActions[0];
if (pending) {
  const manage = await yieldApi("/v1/actions/manage", {
    yieldId,
    address,
    action: pending.type,
    passthrough: pending.passthrough,
  });
  await signAndTrack(manage);
}
```

## Exit a yield

Exiting is the same pattern as entering:

```typescript theme={null}
const exit = await yieldApi("/v1/actions/exit", {
  yieldId,
  address,
  arguments: { amount: "50" }, // see mechanics.arguments.exit
});
await signAndTrack(exit);
```

Some exits need more than one transaction, for example withdraw and unwrap. Sequence sends them as one batch, with one confirmation.

Not every exit settles at once. Staking unbonds wait out the network's cooldown, and many RWA redemptions settle on a NAV strike or over several days. While that happens the position shows as an `exiting` balance. When it settles it becomes `withdrawable` with a `CLAIM_UNSTAKED` pending action, which you complete through `POST /v1/actions/manage` as above. Poll balances on a sensible cadence and show the in-flight state in your UI.

## Track transaction status

After each broadcast, report the hash with `PUT /v1/transactions/{transactionId}/submit-hash`, then poll `GET /v1/transactions/{transactionId}` (or `GET /v1/actions/{actionId}`) until it reaches a terminal status:

```typescript theme={null}
const tx = await yieldApi(`/v1/transactions/${transactionId}`);
console.log(tx.status); // CONFIRMED, FAILED, ...
```

See [Monitoring and alerting](/guides/earn/monitoring-alerting).

## Integration tips

* Send transactions in `stepIndex` order and skip any already at a terminal status.
* Never change `to`, `data`, `value`, gas or `nonce` before signing.
* Build arguments from `mechanics.arguments.*` for the right fields and units.
* Keep your yield.xyz API key on the server.
* Geo-blocking is applied by the API for your project; see [Geo-blocking](/documentation/advanced-setup/geo-blocking/index).
* Batch every step of an action into one `sendTransaction` call: one confirmation, atomic execution.
* Use `sendTransaction` for the raw transactions yield.xyz returns; `callContract` is for a single ABI call you encode yourself.
* Check `isSentTransactionResponse` before reporting a hash: a failed simulation is never broadcast.

## Power Trails Earn with yield.xyz

Trails Earn lets users deposit into DeFi protocols and RWA vaults from any token on any chain. Trails handles the cross-chain routing, bridging and swapping. yield.xyz supplies the three things Trails Earn needs underneath: which yields to show, the calldata to enter and exit them, and the user's positions afterwards. New yields, protocols and issuers added to yield.xyz become available to Trails without new integration work.

| Trails Earn needs | yield.xyz provides |
| - | - |
| Which vaults and pools to show | `GET /v1/yields`, with rate, TVL, entry limits, provider metadata and KYC requirements |
| Calldata to deposit | `POST /v1/actions/enter`, ordered unsigned transactions |
| Calldata to withdraw | `POST /v1/actions/exit`, including multi-step and async exits |
| User positions | `POST /v1/yields/{yieldId}/balances`, lifecycle-aware |
| What the user can do next | `pendingActions` on each balance, such as claim rewards or claim a settled withdrawal |
| Regulated assets | Tokenized RWAs with a KYC status check |
| Fees | OAVs with configurable deposit, performance and management fees |

### 1. Populate the Earn UI

```typescript theme={null}
const { items: yields } = await yieldApi("/v1/yields?network=base&type=lending");

// Each yield carries what the Earn UI needs:
// metadata.name, metadata.logoURI, providerId   display
// rewardRate.total                              current rate
// statistics.tvlUsd                             TVL
// token                                         deposit token (address, symbol, decimals)
// mechanics.arguments.enter                     required arguments
// mechanics.requirements.kycRequired            whether the wallet must be allowlisted
// mechanics.possibleFeeTakingMechanisms         monetization options
```

### 2. Build the deposit

When the user picks a yield and an amount, build the enter action for the user's address on the destination chain:

```typescript theme={null}
const action = await yieldApi("/v1/actions/enter", {
  yieldId,
  address: userAddress, // the user's wallet on the destination chain
  arguments: { amount: "100" },
});
// Each step's unsignedTransaction carries to, data, value and chainId.
// Trails runs the bridge and swap, then executes these steps on the destination chain:
// `to` maps to Trails' toAddress and `data` to toCalldata.
```

For a KYC-gated RWA yield, check `GET /v1/yields/{yieldId}/kyc/status` for the destination address before the user starts a cross-chain deposit, so they don't bridge funds and then hit a `412` on enter.

### 3. Track positions

```typescript theme={null}
const { balances } = await yieldApi(`/v1/yields/${yieldId}/balances`, { address: userAddress });
// type: active | entering | exiting | withdrawable | claimable | locked
// pendingActions: what the user can do next
```

Trails doesn't need to understand each protocol's balance model, including RWA redemptions that settle over days.

### 4. Build the withdrawal

```typescript theme={null}
const exit = await yieldApi("/v1/actions/exit", { yieldId, address: userAddress, arguments: { amount: "100" } });
// Steps come back in order; for a Sequence wallet they can be batched into one transaction.
// Async exits show as `exiting`, then `withdrawable` with a CLAIM_UNSTAKED pending action.
```

| Without yield.xyz | With yield.xyz |
| - | - |
| One integration per protocol | 4,000+ yields across DeFi, staking and RWAs through one API |
| Calldata encoding per protocol version | Ready-to-sign transactions |
| Balance tracking per protocol | Lifecycle-aware balances with pending actions |
| No fee layer | OAVs with configurable fees |
| No regulated assets | Tokenized RWAs with a KYC status check |

## Monetization

How you earn depends on the yield type. Each yield's `mechanics.possibleFeeTakingMechanisms` says which fees you can configure on it.

* **Staking:** deposit fees, deducted atomically in the same transaction, and a share of validator revenue through yield.xyz's validator agreements. See [Preferred validators](/documentation/earn/staking/validators/preferred-validators).
* **DeFi:** revenue share from some protocols and curators, and user-facing deposit, performance and management fees through [OAVs](/documentation/platform/vaults/oav-v3).
* **RWAs:** issuer fees are set by the issuer and passed through. Where an RWA is wrapped in an OAV, you can add your own fees on top.

OAVs also improve the user experience: incentive tokens are sold into the deposit asset and reinvested automatically, wrapping and swapping are handled for the user, and users only ever see their deposit token. See [Earn monetization](/documentation/earn/monetization) for fee configuration, and [Plans](/documentation/plans-limits/plans-tiers) for commercial terms.

## Next steps

<Columns cols={2}>
  <Card title="Enter and exit flows" icon="arrows-rotate" href="/guides/earn/enter-exit-flows">
    The full request, sign and poll loop.
  </Card>

  <Card title="Yield metadata" icon="table-list" href="/documentation/earn/core-concepts/yield-metadata">
    Every field returned on a yield.
  </Card>

  <Card title="Shield" icon="shield-check" href="/documentation/platform/shield">
    Verify each transaction before Sequence signs it.
  </Card>

  <Card title="Embedded wallets" icon="wallet" href="/guides/integrations/index">
    The same flow with other wallet providers.
  </Card>
</Columns>


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