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

Approve and deposit

Wrap apxUSD into apyUSD. Two transactions:

  1. apxUSD.approve(spender = apyUSD, amount) — authorize the vault to pull funds.
  2. apyUSD.deposit({ assets, receiver }) — pull assets apxUSD and mint shares to receiver.
apyx apxUSD approve --spender 0x38EEb52F0771140d10c4E9A9a72349A329Fe8a6A --amount 1e6
apyx apyUSD deposit --assets 1e6 --receiver $(apyx repl <<<'console.log(account.address)' | tail -1)

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:

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 — your approve didn’t land yet, you under-approved, or you’re on the wrong chain. Confirm with apyx 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.redeem doesn’t burn your approval — it’s still there for next time. That’s by design.