Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Redeem

Burn apyUSD shares back into apxUSD. The exit path. One transaction — no approval needed because you’re burning your own shares.

apyx apyUSD redeem \
  --shares   1e18 \
  --receiver 0xYOU \
  --owner    0xYOU

SDK script

import { createApyxClient } from '@koed_jang/apyx-sdk';
import { http, parseUnits } from 'viem';
import { mainnet } from 'viem/chains';
import { privateKeyToAccount } from 'viem/accounts';

const account = privateKeyToAccount(process.env.PK as `0x${string}`);
const apyx = createApyxClient({
  chain: mainnet,
  transport: http(process.env.ETH_RPC_URL),
  account,
});

const shares = await apyx.apyUSD.balanceOf(account.address);

const tx = await apyx.apyUSD.redeem({
  shares,
  receiver: account.address,
  owner:    account.address,
});
const receipt = await tx.wait();

console.log({
  hash:    tx.hash,
  block:   receipt.blockNumber,
  apxUSD:  await apyx.apxUSD.balanceOf(account.address),
});

CLI walkthrough

$ apyx apyUSD balance-of --owner 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
960304728000000000n

$ apyx apyUSD redeem \
    --shares   960304728000000000 \
    --receiver 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf \
    --owner    0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
hash: 0x…
waiting for receipt…
status: success
block:  19884120
gas:    158400

Quote in advance with apyx apyUSD preview-redeem:

$ apyx apyUSD preview-redeem --shares 960304728000000000
1000123n

That’s the apxUSD you’ll receive. If the vault has earned yield since your deposit, this number will exceed the apxUSD you originally deposited — that’s the whole point of the vault.

Why three address args?

ERC-4626 redeem takes three:

  • shares — exact share quantity to burn.
  • receiver — who gets the apxUSD. Often the same as owner, but you can route to a treasury or a different wallet.
  • owner — whose shares are burned. Must equal the signer, unless owner has approved the signer via apyUSD.approve (the share token has its own ERC-20 surface). Most consumers pass their own address for both owner and receiver.

The SDK forwards the args verbatim — pass owner = receiver = wallet.address unless you have a specific reason to split them.

Partial redeem

shares doesn’t have to equal your full balance. Burn a slice:

const all = await apyx.apyUSD.balanceOf(account.address);
const half = all / 2n;
await apyx.apyUSD.redeem({ shares: half, receiver: account.address, owner: account.address });

For an “exact apxUSD out” exit, use apyUSD.withdraw instead — same shape but you specify the assets you want and the contract burns whatever shares are needed.

Verifying the recipe

The path is covered by test/e2e/sdk/apyUSD.writes.fork.spec.ts (full deposit → redeem round-trip with a check that the post-redeem apxUSD balance matches previewRedeem).

Common pitfalls

  • ERC4626: redeem more than max — you asked to burn more shares than apyUSD.balanceOf(owner) returns. Trim to the actual balance.
  • InsufficientAllowance on redeem — the apyUSD share token has its own allowance surface. If signer ≠ owner, you need apyUSD.approve(signer, shares) first (called against the share token, not apxUSD).
  • No yield yet — if you redeem in the same block you deposited, previewRedeem(shares) returns the same assets you put in, modulo rounding. That’s correct, not a bug.