Skip to main content
EarnUSDT is a yield‑bearing version of USDT backed by the Super Vault. Users deposit USDT and receive EarnUSDT (vault shares). As yield is generated, the value of each share increases. There are two main ways to integrate EarnUSDT:
  1. Contract integration – you deploy your own smart contract and that contract deposits into SuperEarn.
  2. Frontend‑only integration – your UI calls SuperEarn directly from the user’s wallet.
The on-chain write flow (deposit / redeem) is slightly different between 1 and 2, but the read side depends on the currently supported public API surface. Confirm the active endpoint contract with the team before integrating.

Common building blocks

You will need three addresses (See smart contracts page):
  • SUPEREARN_ROUTER – SuperEarn router (ISuperEarnRouter).
  • SUPER_VAULT – the EarnUSDT Super Vault shares.
  • USDT – Kaia USDT token.
Key router functions (simplified):
  • deposit(address superVault, uint256 amount, uint256 minSharesOut) returns (uint256 shares)
  • deposit(address superVault, uint256 amount, address receiver, uint256 minSharesOut) returns (uint256 shares)
  • depositWithPermit(...) returns (uint256 shares)
  • previewDeposit(address superVault, uint256 amount) view returns (uint256 expectedShares)
  • previewRedeem(address superVault, uint256 shares) view returns (uint256 expectedAssets)
  • redeem(address superVault, uint256 shares, uint256 minAssetsOut) returns (uint256 requestId)
  • redeem(address superVault, uint256 shares, address receiver, uint256 minAssetsOut) returns (uint256 requestId)
Internally, the Super Vault holds CooldownVault shares and the actual cooldown logic lives in the CooldownVault). All user / integrator deposits and withdrawals must go through SuperEarnRouter; CooldownVault deposits/redeems are restricted to protocol contracts. Claims are permissionless but normally handled by protocol keepers to batch requests.
You can get the CooldownVault address from the Super Vault:
address cooldownVault = IVault(SUPER_VAULT).token();

1. Contract integration (your own wrapper contract)

When to use
  • Centralized service, CEX, wallet, or protocol that:
    • Holds users’ funds in its own contract, and
    • Wants to manage EarnUSDT on behalf of users.
Your contract holds the EarnUSDT shares, does accounting, and talks to SuperEarnRouter. Assume:
ISuperEarnRouter constant ROUTER = ISuperEarnRouter(SUPEREARN_ROUTER);
IERC20            constant USDT   = IERC20(USDT_ADDRESS);
IERC20            constant SHARES = IERC20(SUPER_VAULT); // EarnUSDT shares

1-1. Deposit (USDT → EarnUSDT) via your contract

High‑level flow
  1. User sends USDT into your contract (e.g. via transferFrom).
  2. Your contract approves SuperEarnRouter to spend USDT.
  3. Your contract calls ROUTER.deposit(SUPER_VAULT, amount, receiver, minSharesOut).
  4. EarnUSDT shares are minted to receiver (usually your contract).
  5. You update your internal accounting.
Example Solidity
function depositIntoSuperVault(uint256 amountUSDT, address user) external {
    // 1. Pull USDT from the user into THIS contract
    USDT.transferFrom(user, address(this), amountUSDT);

    // 2. Approve SuperEarnRouter
    USDT.approve(address(ROUTER), amountUSDT);

    // 3. Quote and apply a slippage policy before depositing
    uint256 expectedShares = ROUTER.previewDeposit(SUPER_VAULT, amountUSDT);
    uint256 minSharesOut = expectedShares * 99 / 100;

    // 4. Deposit into Super Vault through SuperEarnRouter
    //    - receiver = this contract (wrapper)
    uint256 shares = ROUTER.deposit(
        SUPER_VAULT,
        amountUSDT,
        address(this),
        minSharesOut
    );

    // 5. Record user's shares in your own storage
    userShares[user] += shares;
}
If you want to show the user an estimate before depositing, call:
uint256 expectedShares = ROUTER.previewDeposit(SUPER_VAULT, amountUSDT);

1-2. Withdraw / redeem (EarnUSDT → USDT) via your contract

Redemption is a 2‑step process under the hood (redeem request → claim after cooldown), but your contract usually only needs to start the request. A keeper / bot normally handles claim. High‑level flow
  1. You reduce the user’s internal share balance.
  2. Your contract approves SuperEarnRouter to spend its EarnUSDT shares.
  3. Call ROUTER.previewRedeem for an estimate (optional).
  4. Call ROUTER.redeem(SUPER_VAULT, shares, receiver, minAssetsOut).
  5. You get a requestId for the pending withdrawal.
  6. After cooldown, USDT is delivered to receiver when the request is claimed.
Example Solidity
function requestWithdrawFromSuperVault(
    uint256 shares,
    address user,
    address receiver
) external returns (uint256 requestId) {
    // 1. Update your internal accounting
    require(userShares[user] >= shares, "insufficient shares");
    userShares[user] -= shares;

    // 2. Approve router to move shares from THIS contract
    SHARES.approve(address(ROUTER), shares);

    // 3. Preview how much USDT the user will get
    uint256 expectedAssets = ROUTER.previewRedeem(SUPER_VAULT, shares);
    uint256 minAssetsOut = expectedAssets * 99 / 100;

    // 4. Create redemption request
    //    - receiver = address that should receive USDT after cooldown
    requestId = ROUTER.redeem(
        SUPER_VAULT,
        shares,
        receiver,
        minAssetsOut
    );

    // 5. Store requestId if you want to show pending withdrawals
    userRedeemRequests[user].push(requestId);
}
Claims on CooldownVault are handled by protocol keepers; integrators should not call deposit, redeem, or claim on CooldownVault directly. Read access (e.g. redeemRequests) is fine for monitoring.

1-3. Read data via the supported public API (for contract integration)

See the Data access page for current read-layer guidance.
For reads, do not assume older GraphQL contracts are still the supported public interface. Confirm the active public data surface with the team before shipping an integration. For contract integration, the important read-side identity is still your wrapper contract address. In practice, you will usually need:
  • vault discovery for the EarnUSDT Super Vault
  • wrapper-level position and redemption status views
  • optional balance and earnings history scoped to that vault

2. Frontend‑only integration (direct from user wallet)

When to use
  • Non‑custodial dApps / wallets.
  • You want the user’s wallet to hold EarnUSDT shares directly.
  • No custom wrapper contract – just frontend and SuperEarn.
Your frontend calls SuperEarnRouter from the user’s wallet using ethers, viem, wagmi, etc. Assume you have:
  • routerAddress = SUPEREARN_ROUTER
  • superVaultAddress = SUPER_VAULT
  • usdtAddress = USDT

2-1. Deposit (USDT → EarnUSDT) directly from frontend

High‑level UX
  1. User connects wallet.
  2. Frontend asks user to approve USDT for SuperEarnRouter.
  3. Frontend calls deposit(superVault, amount, minSharesOut) from the wallet.
  4. User receives EarnUSDT shares (Super Vault shares) in their wallet.
Example (TypeScript + viem/ethers‑style pseudocode)
const router = {
  address: routerAddress,
  abi: ISuperEarnRouterAbi,
} as const;

async function depositEarnUsdt(amountUSDT: bigint, account: `0x${string}`) {
  // 1. Approve USDT
  await writeContract({
    address: usdtAddress,
    abi: erc20Abi,
    functionName: 'approve',
    args: [routerAddress, amountUSDT],
    account,
  });

  // 2. Preview shares and apply your slippage policy
  const expectedShares = await readContract({
    ...router,
    functionName: 'previewDeposit',
    args: [superVaultAddress, amountUSDT],
  });
  const minSharesOut = expectedShares * 99n / 100n;

  // 3. Deposit
  const txHash = await writeContract({
    ...router,
    functionName: 'deposit',
    args: [superVaultAddress, amountUSDT, minSharesOut],
    account,
  });

  // Track txHash and show confirmation in UI
}
If you support permit, use depositWithPermit to skip the explicit ERC‑20 approval.

2-2. Withdraw / redeem (EarnUSDT → USDT) from frontend

From the user’s point of view:
  1. They pick how much EarnUSDT to redeem.
  2. Your UI shows an estimate using previewRedeem.
  3. You call redeem(superVault, shares, minAssetsOut) from their wallet.
  4. A redemption request is created; after the cooldown, USDT is claimable / delivered.
Example (TypeScript pseudocode)
async function redeemEarnUsdt(shares: bigint, account: `0x${string}`) {
  // 1. Preview expected USDT and apply your slippage policy
  const expectedAssets = await readContract({
    address: routerAddress,
    abi: ISuperEarnRouterAbi,
    functionName: 'previewRedeem',
    args: [superVaultAddress, shares],
  });
  const minAssetsOut = expectedAssets * 99n / 100n;

  // 2. Approve router to spend EarnUSDT shares
  await writeContract({
    address: superVaultAddress,
    abi: erc20Abi,
    functionName: 'approve',
    args: [routerAddress, shares],
    account,
  });

  // 3. Create redeem request
  const requestId = await writeContract({
    address: routerAddress,
    abi: ISuperEarnRouterAbi,
    functionName: 'redeem',
    args: [superVaultAddress, shares, minAssetsOut],
    account,
  });

  // Store requestId in your app state if you want to show “pending withdrawal”
}
A backend keeper normally handles the actual claim on the CooldownVault once the cooldown period ends, so your UI can just track redeemRequests and final balances rather than calling claim itself; end users should not call CooldownVault directly.

2-3. Read user data via the supported public API

See the Data access page for current read-layer guidance.
For frontend-only integrations, use the connected wallet as the account identity for whichever supported public API surface the team confirms. The product requirements are unchanged even if the public endpoint shape evolves. Your frontend will still typically need:
  • EarnUSDT vault discovery
  • the user’s current position and pending redemption state
  • optional balance and earnings history scoped to the EarnUSDT vault

3. Direct contract queries

Most integrations should rely on the supported public read API layer, not raw indexing internals. If you need real‑time, raw on‑chain data (e.g. your own indexer, custom monitoring), you can use the view functions on SUPER_VAULT and CooldownVault.

3-1. Get EarnUSDT balances and underlying value

The Super Vault exposes the user’s EarnUSDT share balance:
// EarnUSDT share balance
uint256 shares = IERC20(SUPER_VAULT).balanceOf(account);
To convert shares to underlying USDT equivalent, use price per share on the Super Vault:
uint256 assetsValue = shares * IVault(SUPER_VAULT).pricePerShare() / 10 ** IVault(SUPER_VAULT).decimals();
In UI terms: display balance in USDT = convertToAssets(balanceOf(user)).

3-2. Check cooldown redemption details

When you call ROUTER.redeem(...), the router internally creates a CooldownVault RedeemRequest. You can read its state directly from CooldownVault using the requestId returned by the router.
  1. Resolve the CooldownVault address
address cooldownVault = IVault(SUPER_VAULT).token();
  1. Query a specific redemption request
(
  address receiver,
  uint256 assets,
  uint256 cooldownRequestedTime,
  uint256 cooldownPeriod,
  bool claimed
) = ICooldownVault(cooldownVault).redeemRequests(requestId);
With this you can:
  • Compute claimable time:
uint256 claimableAt = cooldownRequestedTime + cooldownPeriod;
bool isClaimable = block.timestamp >= claimableAt && !claimed;
  • Show “pending / claimable / claimed” states per requestId.

3-3. Check global cooldown configuration

You can read the global cooldown period applied to new redemption requests:
uint256 globalCooldownPeriod = ICooldownVault(cooldownVault).cooldownPeriod();
// e.g. convert to days:
uint256 cooldownDays = globalCooldownPeriod / 86400;
This is useful to:
  • Show “estimated withdrawal time” in UI.
  • Enforce minimum UX expectations (e.g. “cooldown ≈ 7 days”).

3-4. System status: emergency & pause

For safety‑aware integrations you may want to halt actions if the system is paused or in emergency mode.
// Super Vault emergency shutdown (deposits & redeems are blocked)
bool isEmergency = IVault(SUPER_VAULT).emergencyShutdown();

// CooldownVault pause state (cooldown flow disabled)
bool isPaused = ICooldownVault(cooldownVault).paused();
Typical pattern:
  • Block new deposits / redeems in your app when isEmergency || isPaused is true.
  • Keep reads enabled for transparency.

4. Events to watch (monitoring & indexing)

If you run your own indexer or monitoring system, you will mainly care about events from ISuperEarnRouter and CooldownVault.

4-1. Router events (user‑facing actions)

Defined in ISuperEarnRouter:
event Deposited(
    address indexed sender,
    address indexed receiver,
    address indexed yVault,
    uint256 underlyingAmount,
    uint256 yShares
);

event Redeemed(
    address indexed sender,
    address indexed receiver,
    address indexed yVault,
    uint256 yShares,
    uint256 ySharesFilled,
    uint256 requestId,
    uint256 underlyingAmount
);
Usage:
  • Deposited
    • Trigger on successful USDT → EarnUSDT deposits.
    • Index (sender, receiver, yVault, underlyingAmount, yShares) to reconstruct deposit history.
  • Redeemed
    • Trigger when a redeem request is created (EarnUSDT → USDT with cooldown).
    • Use requestId to join with CooldownVault.redeemRequests(requestId) for full status.
    • ySharesFilled lets you detect partial fills in advanced scenarios.

4-2. CooldownVault events (cooldown lifecycle)

Core events from ICooldownVault:
event RedeemRequested(
    address indexed caller,
    address indexed receiver,
    uint256 indexed requestId,
    uint256 assets,
    uint256 shares,
    uint256 requestedTime
);

event Claimed(
    address indexed caller,
    uint256 indexed requestId,
    uint256 assets,
    uint256 claimable
);
(There are additional governance / strategy events such as PredepositRequested, DebtRetrieved, etc., but most EarnUSDT integrators can ignore those.) Usage:
  • RedeemRequested
    • Mirrors router Redeemed, but emitted at the CooldownVault level.
    • Good for verifying that a given requestId exists and carries (assets, shares, requestedTime).
  • Claimed
    • Emitted when a cooldown redemption is actually claimed and USDT is transferred.
    • Use it to mark a pending withdrawal as completed in your own indexer or notifications.
In practice:
  • Track user intent via Deposited / Redeemed on the router.
  • Track cooldown state + completion via RedeemRequested / Claimed on CooldownVault.

5. Interface reference (abridged)

This section summarizes only the pieces of the on‑chain interfaces that most integrators actually need. Strategy‑level interfaces such as IStrategyCooldownAware are intentionally omitted.

5-1. ISuperEarnRouter

Full interface (simplified to the relevant parts):
interface ISuperEarnRouter {
    // EVENTS
    event Deposited(
        address indexed sender,
        address indexed receiver,
        address indexed yVault,
        uint256 underlyingAmount,
        uint256 yShares
    );

    event Redeemed(
        address indexed sender,
        address indexed receiver,
        address indexed yVault,
        uint256 yShares,
        uint256 ySharesFilled,
        uint256 requestId,
        uint256 underlyingAmount
    );

    // VIEW
    function registry() external view returns (address);
    function previewRedeem(address yVault, uint256 yShares) external view returns (uint256);
    function endorsedVault(address token) external view returns (address);

    // DEPOSIT
    function deposit(
        address yVault,
        uint256 amount,
        uint256 minSharesOut
    ) external returns (uint256);

    function deposit(
        address yVault,
        uint256 amount,
        address receiver,
        uint256 minSharesOut
    ) external returns (uint256);

    function depositWithPermit(
        address yVault,
        uint256 amount,
        address receiver,
        uint256 minSharesOut,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external returns (uint256);

    // REDEEM (cooldown‑based)
    function redeem(
        address yVault,
        uint256 yShares,
        uint256 minAssetsOut
    ) external returns (uint256 requestId);

    function redeem(
        address yVault,
        uint256 yShares,
        address receiver,
        uint256 minAssetsOut
    ) external returns (uint256 requestId);
}
Rule of thumb:
  • For writes, you only need deposit, depositWithPermit, redeem.
  • For reads / UX, you mainly need previewRedeem.

5-2. ICooldownVault (high‑level subset)

CooldownVault is a vault with built‑in cooldown and loss‑limit logic. Below are the main functions relevant to EarnUSDT integrators.
interface ICooldownVault {
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    function mint(uint256 shares, address receiver) external returns (uint256 assets);
    function previewDeposit(uint256 assets) external view returns (uint256 shares);
    function previewMint(uint256 shares) external view returns (uint256 assets);

    // Cooldown redemption
    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) external returns (uint256 assets);

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) external returns (uint256 shares);

    // Global cooldown config
    function cooldownPeriod() external view returns (uint256 period);

    // Individual redemption request
    function redeemRequests(uint256 requestId)
        external
        view
        returns (
            address receiver,
            uint256 assets,
            uint256 cooldownRequestedTime,
            uint256 cooldownPeriod,
            bool claimed
        );

    // Claim after cooldown
    function claim(uint256 requestId, uint256 maxLossBps) external returns (uint256 claimable);

    // Status
    function paused() external view returns (bool);
}
As an integrator:
  • Do not call deposit or redeem on CooldownVault — these are restricted to protocol contracts and go through SuperEarnRouter.
  • Claims are permissionless but typically executed by protocol keepers to batch/optimise redemptions.
  • Helpful reads:
    • cooldownPeriod() – to show global cooldown.
    • redeemRequests(requestId) – to inspect a specific withdrawal.

5-3. IVault (Super Vault)

The Super Vault wraps CooldownVault shares. For EarnUSDT integration you typically only need:
interface IVault {
    // Underlying token of the vault (here: CooldownVault)
    function token() external view returns (address);

    // (Plus standard vault view functions, if you need them)
    // e.g. totalAssets(), pricePerShare(), etc.
}
In most cases you use IVault(SUPER_VAULT).token() just to resolve the CooldownVault address.

Summary

For both integration styles:
  • Writes (deposit / redeem) → call SuperEarnRouter:
    • deposit / depositWithPermit for USDT → EarnUSDT
    • previewRedeem + redeem for EarnUSDT → USDT (cooldown‑based)
  • Reads (positions / status):
    • Use the supported public read API layer for portfolio, PnL, and charts.
    • Optionally use direct contract reads for real‑time balances or custom monitoring.
  • Monitoring:
    • Subscribe to Deposited / Redeemed on the router and RedeemRequested / Claimed on CooldownVault.
The only thing that changes between integration styles is which accountAddress you query:
  • Contract integration → your wrapper contract address.
  • Frontend‑only → the user’s wallet address.