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

# Add Earn capabilities

> Implement executeYieldTransaction on a custom WalletProvider to enable Earn deposits, withdrawals, and reward claims.

`executeYieldTransaction` is the method the Dynamic SDK calls when [`depositToYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/deposit-to-yield-vault), [`withdrawFromYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/withdraw-from-yield-vault), or [`claimYieldRewards`](https://docs.dynamic.xyz/docs/javascript/reference/client/claim-yield-rewards) need your wallet to sign and broadcast an [Earn](https://docs.dynamic.xyz/docs/earn/overview.md) vault transaction. If your `WalletProvider` does not implement it, the user can connect, sign messages, and even execute swaps — but calling any Earn action throws a `WalletProviderMethodUnavailableError`.

> [!NOTE]
>
> Earn vault actions run on EVM and Solana. `executeYieldTransaction` is
> implemented by Dynamic's built-in EVM and Solana wallet providers. A custom
> `WalletProvider` only needs this method if it targets EVM or Solana chains,
> and the payload it receives differs between the two, as described below.

## What executeYieldTransaction receives

The method receives a `signingPayload` and a `walletAccount`, and must return `{ transactionHash }`. Unlike [`executeSwapTransaction`](https://docs.dynamic.xyz/docs/javascript/reference/creating-extensions/wallet-provider/add-swap-capabilities), there's no optional `approvalTransactionHash` on the result — only the final transaction hash.

* `signingPayload` — `{ chainId, chainName, evmTransaction, evmApproval?, transactions? }`. EVM vaults carry `evmTransaction`, and Solana vaults carry `transactions` instead (see [Solana payloads](#solana-payloads)). `evmApproval` is present only when the wallet's current ERC-20 allowance for the vault is short of the action's amount, and must be signed and sent **before** `evmTransaction`. Only [`depositToYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/deposit-to-yield-vault) can produce an `evmApproval` — [`withdrawFromYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/withdraw-from-yield-vault) and [`claimYieldRewards`](https://docs.dynamic.xyz/docs/javascript/reference/client/claim-yield-rewards) redeem shares or rewards the wallet already holds, so they never require one.
* `walletAccount` — the connected account that should sign.

## High-level implementation

Implement `executeYieldTransaction` and spread it into your `WalletProvider`:

```typescript theme={"system"}
import type { WalletProvider } from "@dynamic-labs-sdk/client/core";

// The yield part of the provider. Spread it into the object that also holds
// chain, key, metadata, connect and the other provider fields.
const yieldCapability: Pick<WalletProvider, "executeYieldTransaction"> = {
  executeYieldTransaction: async ({ signingPayload, walletAccount, onStepChange }) => {
    const { evmTransaction, evmApproval } = signingPayload;

    if (evmApproval) {
      onStepChange?.('approval');
      // Replace with your wallet's native signing layer.
      await myWalletSdk.signAndSend({ transaction: evmApproval, walletAccount });
    }

    onStepChange?.('transaction');

    const transactionHash = await myWalletSdk.signAndSend({
      transaction: evmTransaction,
      walletAccount,
    });

    return { transactionHash };
  },
};
```

Your wallet's signing layer is responsible for:

1. Signing and sending `evmApproval` first, when present, and calling `onStepChange('approval')` beforehand.
2. Signing and sending `evmTransaction`, and calling `onStepChange('transaction')` beforehand.
3. Ensuring the user is on the correct network (the SDK calls `ensureCorrectActiveNetwork` before `executeYieldTransaction`, but you can validate again).
4. Returning the on-chain transaction hash of `evmTransaction` — not the approval's hash.

## Solana payloads

For a Solana vault, `chainName` is `SOL` and the payload has no `evmTransaction`. It carries a `transactions` array of steps instead, and your provider must sign, send, and confirm them one at a time.

Each step has these fields:

| Field | Type | Description |
| - | - | - |
| `ordinal` | `number` | Zero-based position in the sequence. Execute steps in ascending `ordinal` order. |
| `payload` | `string` | The unsigned transaction, serialized and base64-encoded, with an empty signature slot for the wallet. |
| `label` | `string` (optional) | The provider's name for the step, such as `deposit` or `unstake-from-farm`. |
| `expiryBlockHeight` | `number` (optional) | The block height after which the transaction's blockhash is no longer valid. |

Your signing layer is responsible for:

1. Executing the steps in order, never in parallel, because a later step can depend on state that an earlier step wrote.
2. Waiting for each step to confirm before it starts the next.
3. Refusing to sign a step whose blockhash has expired, so that nothing is signed that can no longer land.
4. Calling `onStepChange('transaction')` and `onTransactionStepChange({ label, ordinal, total })` before each step.
5. Returning the signature of the last step as `transactionHash`.
6. Stopping at the first failure. The earlier steps have already confirmed, so tell the caller which step failed.

The built-in Solana provider does this with `SolanaYieldStepError`, which reports the failed step's `ordinal`, so that callers know how much of the action was applied. Gas sponsorship is opt-in for Solana vault actions: only `sponsored: true` routes the transactions through sponsorship.

## Use the wallet for Earn

With `executeYieldTransaction` implemented, a consumer calls the Earn functions directly — each fetches a signing payload from the Dynamic API, then delegates signing to your provider's `executeYieldTransaction`:

```typescript theme={"system"}
import {
  claimYieldRewards,
  depositToYieldVault,
  withdrawFromYieldVault,
} from "@dynamic-labs-sdk/client";

// Deposit reports 'approval' only for an EVM vault, when the wallet's allowance is short of `amount`.
// For a Solana vault, `onTransactionStepChange` reports each transaction in the sequence.
const { transactionHash: depositHash } = await depositToYieldVault({
  amount: "10.5",
  onStepChange: (step) => console.log(step), // 'approval' | 'transaction'
  vaultId,
  walletAccount,
});

// Withdraw and claim redeem shares or rewards the wallet already holds,
// so neither ever produces an approval step.
const { transactionHash: withdrawHash } = await withdrawFromYieldVault({
  amount: "5",
  vaultId,
  walletAccount,
});

const { transactionHash: claimHash } = await claimYieldRewards({
  vaultId,
  walletAccount,
});
```

`depositToYieldVault`, `withdrawFromYieldVault`, and `claimYieldRewards` all resolve the vault's contract address and asset decimals server-side, then call your provider's `executeYieldTransaction` with the resulting `signingPayload`. The only wallet-specific work is inside `executeYieldTransaction`; everything else is handled by the Dynamic SDK.

## Related

* [Build a WalletProvider](https://docs.dynamic.xyz/docs/javascript/reference/creating-extensions/wallet-provider/build-wallet-provider.md)
* [Add swap capabilities](https://docs.dynamic.xyz/docs/javascript/reference/creating-extensions/wallet-provider/add-swap-capabilities.md) - The equivalent method for swaps and Flow
* [`depositToYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/deposit-to-yield-vault)
* [`withdrawFromYieldVault`](https://docs.dynamic.xyz/docs/javascript/reference/client/withdraw-from-yield-vault)
* [`claimYieldRewards`](https://docs.dynamic.xyz/docs/javascript/reference/client/claim-yield-rewards)


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