Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,20 @@
]
},
"features/transaction-management/balances",
"features/transaction-management/fiat-on-ramp"
"features/transaction-management/fiat-on-ramp",
{
"group": "Earn",
"pages": [
"features/transaction-management/earn",
"features/transaction-management/earn/vault-catalog",
"features/transaction-management/earn/deploy-wrapper",
"features/transaction-management/earn/deploy-wrapper-2",
"features/transaction-management/earn/deposit",
"features/transaction-management/earn/positions",
"features/transaction-management/earn/end-to-end-example"
],
"tag": "Early Access"
}
]
},
{
Expand Down
123 changes: 123 additions & 0 deletions features/transaction-management/earn.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
title: "Overview"
description: "Deposit into DeFi yield vaults from Turnkey wallets, with onchain fee collection for your organization."
---

Earn lets wallets in your organization deposit into DeFi yield vaults, query position data to track their positions, and withdraw, all through Turnkey activities with the corresponding audit trail and policy controls. You can take your own fee on the yield your users earn, paid onchain to a wallet you control.

<Note>
Earn is currently an Early Access Product. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization.
</Note>

## What is Earn

Earn connects Turnkey wallets to ERC-4626 yield vaults. The vaults are deployed and operated by independent third-party protocols; Turnkey provides the API infrastructure to interact with them but does not operate, manage, or guarantee them. Morpho vaults are supported today and Aave support is coming soon.

Users don't deposit into vaults directly. They deposit into a fee wrapper, a smart contract that holds the position in the underlying vault. A vault may be enabled via either the dashboard or the API. Once you enable a vault, a wrapper is automatically deployed with your fee parameters. The wrapper collects fees as yield accrues. Fees come out of yield only, never principal, and users never sign a separate fee transaction.

A wrapper is deployed once per vault and fee configuration. After that, deposits, withdrawals, and position queries are each a single API call.

<Note>
If your application presents Earn functionality to end users, you should clearly disclose that: (1) yield is generated by independent third-party DeFi protocols and do not claim or imply that yield is generated by your application or by Turnkey; (2) deposited assets are subject to risks including smart-contract risk, liquidity risk, and potential loss of principal; (3) yield is variable and not guaranteed; and (4) the user is directing the deposit action and retaining control of their assets. Depending on your jurisdiction and the nature of the assets involved, additional regulatory disclosures may apply.
</Note>

<Warning>
Vault deposits interact with smart contracts deployed on public blockchains. Deposited assets are subject to smart-contract risk, including the possibility of bugs, exploits, or protocol governance changes that could result in partial or total loss of deposited funds. Turnkey does not audit or guarantee the security of any third-party vault contract.
</Warning>

## How it works

1. Query [`list_earn_vaults`](/features/transaction-management/earn/vault-catalog) for the vaults available for an asset, with live TVL and APY.
2. Run [`earn_deploy_wrapper`](/features/transaction-management/earn/deploy-wrapper) once per vault to enable it for your organization and set your fee. Turnkey pays the deployment gas.
3. Call [`earn_deposit`](/features/transaction-management/earn/deposit) to initiate a transfer of assets from a wallet into the vault.
4. Poll the matching status endpoint until the transaction confirms. Deposits, withdrawals, and deployments all confirm asynchronously.
5. Query [`list_earn_positions`](/features/transaction-management/earn/positions) for a wallet’s current value and lifetime totals.
6. Call [`earn_withdraw`](/features/transaction-management/earn/withdraw) for a partial amount, the yield only, or the full position.

## Supported protocols and chains

| Chain | CAIP-2 | Providers |
| :-- | :-- | :-- |
| Ethereum | `eip155:1` | Morpho (Aave upcoming) |
| Base | `eip155:8453` | Morpho (Aave upcoming) |
| Arbitrum | `eip155:42161` | Morpho (Aave upcoming) |
| Polygon | `eip155:137` | Morpho (Aave upcoming) |

Earn is EVM-only in V1. The vault catalog only includes vaults with at least \$100k TVL.

## Fees

Earn fees are charged only on yield: a percentage of the yield a position earns. Principal is never charged. Two fees apply, both in basis points of gross yield:

- **Your fee (the "Client Fee")**: you set it per wrapper at deploy time (`clientFeeBps`), along with the payout wallet (`clientFeeWallet`, a wallet account owned by your organization). Fees accrue onchain and are released to that wallet when you claim them.
- **Turnkey's fee (The "Turnkey fee")**: resolved automatically when you deploy. The Turnkey Fee is up to 10% of yield (1,000 bps).

Client fees are capped at 4,000 bps (40% of yield). Deployments above this cap are rejected. Both Client fees and Turnkey’s fee are taken from yield as wrapper shares minted to a payment splitter contract deployed alongside the wrapper, which splits them onchain between you and Turnkey.

The net yield your users earn is `grossApy × (1 - totalFeeBps / 10000)`. The [`list_earn_enabled_vaults`](/features/transaction-management/earn/vault-catalog#list-your-enabled-vaults) endpoint returns gross APY, net APY, and your fee rate for every wrapper you've deployed, along with `claimableClientFee`: the fee amount accrued to your organization that is actively claimable. Claim it onchain with the [`claim_earn_fees`](/api-reference/activities/claim-earn-fees) activity; the claimed amount is paid out to your fee wallet.

<Warning>
The fee configuration is fixed per wrapper. To change your fee, deploy a new wrapper for the same vault. Existing positions in the old wrapper remain fully withdrawable, and new deposits go to the new wrapper. See [Deploy a vault wrapper](/features/transaction-management/earn/deploy-wrapper#choose-your-fee-configuration).
</Warning>

**Important:** The Client Fee and the Turnkey Fee are infrastructure fees charged on yield generated by third-party vault protocols. Neither fee constitutes a fee for investment advice, asset management, or discretionary portfolio allocation. Turnkey does not exercise discretion over how vault assets are deployed, allocated, or managed. All allocation decisions are made by the vault's curator or governing protocol.

## API surface

Earn adds five activities:

| Activity | Endpoint | Purpose |
| :-- | :-- | :-- |
| [`ACTIVITY_TYPE_EARN_DEPLOY_WRAPPER`](/api-reference/activities/deploy-earn-wrapper) | `POST /public/v1/submit/earn_deploy_wrapper` | Enable a vault for your org by deploying its fee wrapper |
| [`ACTIVITY_TYPE_EARN_DEPOSIT`](/api-reference/activities/deposit-into-earn-vault) | `POST /public/v1/submit/earn_deposit` | Deposit assets from a wallet into an enabled vault |
| [`ACTIVITY_TYPE_EARN_WITHDRAW`](/api-reference/activities/withdraw-from-earn-vault) | `POST /public/v1/submit/earn_withdraw` | Withdraw assets or exit a position |
| [`ACTIVITY_TYPE_EARN_SET_WRAPPER_STATE`](/api-reference/activities/set-earn-wrapper-state) | `POST /public/v1/submit/earn_set_wrapper_state` | Pause or resume deposits to a wrapper (withdrawals are never blocked) |
| [`ACTIVITY_TYPE_CLAIM_EARN_FEES`](/api-reference/activities/claim-earn-fees) | `POST /public/v1/submit/claim_earn_fees` | Claim your accrued fees for a wrapper |

and seven queries:

| Query | Endpoint | Purpose |
| :-- | :-- | :-- |
| [Vault catalog](/api-reference/queries/get-earn-vault-catalog) | `POST /public/v1/query/list_earn_vaults` | All wrappable vaults for an asset, with live TVL/APY |
| [Enabled vaults](/api-reference/queries/get-earn-enabled-vaults) | `POST /public/v1/query/list_earn_enabled_vaults` | Your org's deployed wrappers (management view) |
| [Positions](/api-reference/queries/get-earn-positions) | `POST /public/v1/query/list_earn_positions` | A wallet's active positions |
| [Deploy status](/api-reference/queries/get-earn-deploy-status) | `POST /public/v1/query/get_earn_deploy_status` | Poll a wrapper deployment |
| [Deposit status](/api-reference/queries/get-earn-deposit-status) | `POST /public/v1/query/get_earn_deposit_status` | Poll a deposit until it lands onchain |
| [Withdraw status](/api-reference/queries/get-earn-withdraw-status) | `POST /public/v1/query/get_earn_withdraw_status` | Poll a withdrawal until it lands onchain |
| [Claim fees status](/api-reference/queries/get-claim-earn-fees-status) | `POST /public/v1/query/get_claim_earn_fees_status` | Poll a claim fee until it lands onchain |

<Note>
Earn requests are stamped and submitted like any other Turnkey request. See [Stamps](/api-reference/overview/stamps) and [Submissions](/api-reference/activities/overview). Full request/response schemas and cURL examples live in the API reference pages linked above.
</Note>

## Explore

<CardGroup cols={2}>
<Card title="Browse the vault catalog" href="/features/transaction-management/earn/vault-catalog">
— Discover vaults and check your enabled wrappers.
</Card>

<Card title="Deploy a vault wrapper" href="/features/transaction-management/earn/deploy-wrapper">
— Enable a vault and set your fee.
</Card>

<Card title="Deposit into a vault" href="/features/transaction-management/earn/deposit">
— One transaction, optionally gas-sponsored.
</Card>

<Card title="Withdraw from a vault" href="/features/transaction-management/earn/withdraw">
— Partial, yield-only, or full exit.
</Card>

<Card title="Track positions" href="/features/transaction-management/earn/positions">
— Current value, lifetime totals, and yield.
</Card>

<Card title="End-to-end example" href="/features/transaction-management/earn/end-to-end-example">
— Deploy, deposit, and withdraw USDC on Base.
</Card>
</CardGroup>

**Important:** Turnkey does not operate, control, or audit the DeFi vaults or underlying protocols accessible through the Earn feature. Vault data, including APY and TVL figures, is sourced from third-party providers and may be inaccurate, delayed, or subject to change without notice. Yield is generated by independent third-party protocols, not by Turnkey. Yield is variable, not guaranteed, and past rates are not indicative of future performance.

Depositing into DeFi vaults involves risk, including the potential loss of deposited assets due to smart-contract vulnerabilities, market conditions, liquidity constraints, or protocol failures. These materials are provided for informational and technical integration purposes only. Nothing in this documentation constitutes investment, financial, legal, or tax advice, or a recommendation or solicitation to engage in any particular transaction. You and your end users are solely responsible for evaluating the suitability and risks of any vault.
66 changes: 66 additions & 0 deletions features/transaction-management/earn/deploy-wrapper-2.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: "Manage wrappers"
description: "Pause deposits, claim your accrued fees, and change or tier your fee by deploying additional wrappers."
sidebarTitle: "Manage wrappers"
mode: "wide"
---

A deployed wrapper is immutable. It points at the same underlying vault, its fee configuration cannot change, and the deploy abdicates the contract's deposit, withdrawal, and transfer gates, so no one, including Turnkey, can ever restrict user access to funds.

What you control after deployment:

- whether new deposits are accepted
- when you claim accrued fees
- which wrapper each user deposits into.

Management operations are parent-organization only; sub-organization wallets can deposit and withdraw but cannot manage wrappers.

<Note>
Earn is currently an Early Access Product. [Contact us](https://www.turnkey.com/contact-us) to enable it for your organization.
</Note>

## Pause or resume deposits

[`earn_set_wrapper_state`](/api-reference/activities/set-earn-wrapper-state) sets `depositsDisabled` on a wrapper. While true, earn\_deposit against that wrapper is rejected. Withdrawals, position queries, and fee claims are unaffected, so a paused wrapper can never trap funds. Set it back to false to resume deposits.

Pausing gates deposits submitted through the Turnkey API; it is not an onchain lock. The wrapper remains a permissionless ERC-4626 contract that anyone can interact with directly onchain.

Read the current state from `depositsDisabled` on [`list_earn_enabled_vaults`](/api-reference/queries/get-earn-enabled-vaults), or on each row of [`list_earn_positions`](/api-reference/queries/get-earn-positions) so your UI can hide the deposit button for paused wrappers.

Common uses: stopping new deposits into a vault that looks compromised while users exit at their own pace, and winding down a wrapper after replacing it with a new fee configuration.

## Claim your fees

Fees accrue onchain as wrapper shares held by the wrapper's payment splitter. They accumulate without any action from you and never expire. Claiming converts them into the underlying asset.

1. Check the claimable amount. `claimableClientFee` on [`list_earn_enabled_vaults`](/api-reference/queries/get-earn-enabled-vaults) is the releasable fee for that wrapper, denominated in the underlying asset with USD and asset display values. This field and `clientFeeWallet` are only returned to parent-organization callers.
2. Submit [`claim_earn_fees`](/api-reference/activities/claim-earn-fees) with the `wrapperAddress`. One transaction releases your fee shares from the splitter to your `clientFeeWallet` and redeems them into the underlying asset, so the payout arrives as the asset (for example USDC), not as vault shares.
3. Poll [`get_claim_earn_fees_status`](/api-reference/queries/get-claim-earn-fees-status) with the returned `claimRequestId`. As with every Earn transaction, `COMPLETED` on the activity means submitted, not confirmed onchain.

Details:

- The claim releases exactly what is claimable. If your fee wallet also holds a deposit position in the same wrapper, the claim never touches that principal.
- The claim signs from your `clientFeeWallet`, so your organization's policies apply to it.
- Claim transactions require gas sponsorship. Gas is covered at transaction time and billed on your next invoice.
- Claiming with nothing accrued fails with an explicit error.
- Fees are per wrapper. If you run more than one wrapper for a vault, each has its own splitter and claimable balance; iterate over every wrapper in [`list_earn_enabled_vaults`](/api-reference/queries/get-earn-enabled-vaults).

## Change or tier your fee

A wrapper is unique to its vault and fee configuration (`clientFeeBps` and `clientFeeWallet`). To change your fee or payout wallet, or tier fees for the same underlying vault, deploy a new wrapper for the same vault. The two wrappers coexist permanently: neither invalidates the other, each has its own configuration and claimable balance, and each is an independent depositor in the underlying vault. This one mechanic covers both replacing your fee and offering different rates to different audiences.

To replace your fee:

1. Deploy the new wrapper with the new `clientFeeBps` and/or `clientFeeWallet`. You get a new `wrapperAddress` and `splitterAddress`, and Turnkey pays the gas as with the first deployment.
2. Point new deposits at the new `wrapperAddress`. Deposits are addressed by wrapper, not by vault.
3. Pause the old wrapper so nothing else lands in it. Withdrawals stay open.
4. Optionally migrate existing positions. There is no in-place migration: [`earn_withdraw`](/features/transaction-management/earn/withdraw) the full position (`MAX`) from the old wrapper, then [`earn_deposit`](/features/transaction-management/earn/deposit) the proceeds into the new one, per wallet. Both legs can be gas-sponsored.
5. Keep claiming from the old wrapper. Balances left behind keep accruing fees at the old rate to the old `clientFeeWallet`.

Things to keep in mind:

- Fee changes are forward-only. Yield already accrued settles at the old rate, and a position keeps paying its wrapper's rate until it is migrated.
- Position history is per wrapper. [`list_earn_positions`](/features/transaction-management/earn/positions) returns one row per wrapper, so a wallet with balances in two wrappers for the same vault shows two rows; aggregate by `vaultAddress` if your UI shows a single balance. A `MAX` exit zeroes a row's totals, and the row disappears once the share balance is zero.
- The catalog's `enabled` flag is per vault, not per wrapper. It is true once any wrapper exists and won't tell you which is current. Use `wrapperAddress` from [`list_earn_enabled_vaults`](/api-reference/queries/get-earn-enabled-vaults) or your own stored mapping as the source of truth.
- The fee caps apply to every deployment.
- Redeploying with identical parameters is idempotent: it re-derives the same addresses and skips the broadcast, so retries never create duplicates.
Loading