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
- CLI walkthrough
- Why three address args?
- Partial redeem
- Verifying the recipe
- Common pitfalls
- Related
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 asowner, but you can route to a treasury or a different wallet.owner— whose shares are burned. Must equal the signer, unlessownerhas approved the signer viaapyUSD.approve(the share token has its own ERC-20 surface). Most consumers pass their own address for bothownerandreceiver.
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 thanapyUSD.balanceOf(owner)returns. Trim to the actual balance.InsufficientAllowanceonredeem— the apyUSD share token has its own allowance surface. Ifsigner ≠ owner, you needapyUSD.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 sameassetsyou put in, modulo rounding. That’s correct, not a bug.
Related
- Approve and deposit — the inverse path.
apyUSD redeem,apyUSD preview-redeem,apyUSD balance-of— CLI reference.- apyUSD module — full SDK reference.