Approve and deposit
Wrap apxUSD into apyUSD. Two transactions:
apxUSD.approve(spender = apyUSD, amount)— authorize the vault to pull funds.apyUSD.deposit({ assets, receiver })— pullassetsapxUSD and mint shares toreceiver.
apyx apxUSD approve --spender 0x38EEb52F0771140d10c4E9A9a72349A329Fe8a6A --amount 1e6
apyx apyUSD deposit --assets 1e6 --receiver $(apyx repl <<<'console.log(account.address)' | tail -1)
- Why two transactions
- SDK script
- CLI walkthrough
- Skip the approve via permit
- Verifying the recipe
- Common pitfalls
- Related
Why two transactions
ERC-4626 vaults pull underlying tokens from the depositor via
transferFrom. ERC-20 requires the holder approves the vault first.
This is the canonical “approve + interact” pattern; apxUSD.permit
collapses it to one tx if you’re willing to sign typed data instead —
see “Skip the approve via permit” below.
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 amount = parseUnits('1', 6); // 1 apxUSD (6-decimal)
// Step 1: approve apyUSD to pull `amount` apxUSD
const approve = await apyx.apxUSD.approve({
spender: apyx.addresses.apyUSD,
amount,
});
await approve.wait();
// Step 2: deposit and mint shares
const deposit = await apyx.apyUSD.deposit({
assets: amount,
receiver: account.address,
});
const receipt = await deposit.wait();
console.log({
hash: deposit.hash,
block: receipt.blockNumber,
shares: await apyx.apyUSD.balanceOf(account.address),
});
CLI walkthrough
$ apyx apxUSD approve \
--spender 0x38EEb52F0771140d10c4E9A9a72349A329Fe8a6A \
--amount 1e6
hash: 0x3c6a…a9e1
waiting for receipt…
status: success
block: 19884112
gas: 48201
$ apyx apyUSD deposit \
--assets 1e6 \
--receiver 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
hash: 0xbc9e…1d44
waiting for receipt…
status: success
block: 19884115
gas: 167822
After both land, your share balance reflects the deposit:
$ apyx apyUSD balance-of --owner 0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf
960304728000000000n
Quote ahead of time with apyx apyUSD preview-deposit:
$ apyx apyUSD preview-deposit --assets 1e6
960304728000000000n
previewDeposit is the on-chain quote at the current block — match
this against balance-of after the deposit lands; they should agree
exactly.
Skip the approve via permit
If your wallet supports EIP-2612 typed-data signing (most do —
MetaMask, Rabby, Coinbase Wallet, Ledger Live), you can sign an
allowance off-chain, hand the signature to anyone, and have them
submit permit + deposit in a single bundle. The holder pays no
gas; the relayer pays gas only for the deposit-side benefit.
The full typed-data construction is outside the scope of this recipe;
viem’s signTypedData documentation walks the EIP-712 domain and
struct hashing. Once you have (v, r, s):
const tx = await apyx.apxUSD.permit({
owner, spender, value, deadline,
v, r, s,
});
await tx.wait();
// then deposit, no separate approve required
const dep = await apyx.apyUSD.deposit({ assets: value, receiver });
await dep.wait();
Verifying the recipe
The two SDK paths exercised here are covered by:
test/e2e/sdk/apxUSD.writes.fork.spec.ts—approve,transfer,permiton apxUSD against an anvil mainnet fork (realtransferFromchain on the next call).test/e2e/sdk/apyUSD.writes.fork.spec.ts—deposit,redeem,mint,withdrawon apyUSD with the apxUSD-funded test account.
CLI parity with these paths is covered by
test/e2e/cli/methods.e2e.spec.ts.
Common pitfalls
The contract function "transferFrom" reverted with insufficient allowance— yourapprovedidn’t land yet, you under-approved, or you’re on the wrong chain. Confirm withapyx apxUSD allowance --owner $YOU --spender $(apyx config show --profile default | jq -r .signer.…).- Approving max isn’t free.
approve(maxUint256)saves a future tx but raises the blast radius if the vault is ever compromised. Approve exactly what you’ll deposit, or grant a working budget on a treasury account. - Stale approval after redeem.
apyUSD.redeemdoesn’t burn your approval — it’s still there for next time. That’s by design.
Related
- apxUSD module, apyUSD module — full SDK reference.
- Redeem — the inverse: apyUSD → apxUSD.
apxUSD approve,apyUSD deposit— CLI reference.