Quick Start Guide

Get your Coinchange Vault integration up and running in 30 minutes.

1) Prerequisites

  • React app (Next.js or CRA)
  • Ethers v6 (signer + provider)
  • Optional: wagmi/viem for wallet connection (see signer bridge in step 5)

Github: https://github.com/Se7en-Seas/boring-vault-ui NPM Package: https://www.npmjs.com/package/boring-vault-ui Arbitrum example with a delayed withdrawer: https://github.com/Se7en-Seas/boring-vault-ui/blob/main/src/examples/v2.tsx

2) Wrap your app with the Vault Provider

The provider centralizes contract addresses, your Ethers provider, and supported tokens.

React + TypeScript (TSX) with Ethers v6

import { BoringVaultV1Provider } from 'boring-vault-ui';
import { JsonRpcProvider } from 'ethers';

export default function AppRoot() {
  const rpcUrl = process.env.NEXT_PUBLIC_RPC;
  const ethersProvider = new JsonRpcProvider(rpcUrl);

  return (
    <BoringVaultV1Provider
      vaultContract="0x..."        // BoringVault
      tellerContract="0x..."       // TellerWithMultiAssetSupport
      accountantContract="0x..."   // AccountantWithRateProviders
      lensContract="0x..."         // Lens (read batching)
      ethersProvider={ethersProvider}
      depositTokens={[
        { address: "0xTokenA...", decimals: 18 },
        { address: "0xTokenB...", decimals: 6 }
      ]}
      baseToken={{ address: "0xBase...", decimals: 18 }}
      vaultDecimals={18}
    >
      {/* your app */}
    </BoringVaultV1Provider>
  );
}

Readiness check

import { useBoringVaultV1 } from 'boring-vault-ui';

const { isBoringV1ContextReady } = useBoringVaultV1();

if (!isBoringV1ContextReady()) {
  return <div>Loading vault context…</div>;
}

3) Core reads (no wallet required)

import { useBoringVaultV1 } from 'boring-vault-ui';

const { fetchTotalAssets, fetchShareValue } = useBoringVaultV1();

const tvlHuman     = await fetchTotalAssets();  // base token units (human-readable)
const shareValueHr = await fetchShareValue();   // 1 share → base token (human-readable)

4) User reads (wallet required)

import { useBoringVaultV1 } from 'boring-vault-ui';

const { fetchUserShares, fetchUserUnlockTime } = useBoringVaultV1();

const userSharesHr = await fetchUserShares("0xUser...");
const unlockTs     = await fetchUserUnlockTime("0xUser...");  // unix seconds

5) Get an Ethers signer (viem → Ethers bridge)

// Example pattern — adapt to your wallet setup.
import { BrowserProvider } from 'ethers';

// viemWalletClient should be created by your app.
const ethersProvider = new BrowserProvider(viemWalletClient.transport);
const signer = await ethersProvider.getSigner();  // Ethers JsonRpcSigner

6) Supply (deposit) flow

The spec’s deposit helper will check allowance, prompt approval if needed, then submit the deposit.

import { useBoringVaultV1 } from 'boring-vault-ui';

const { deposit, depositStatus } = useBoringVaultV1();

// depositAmount is a human-readable decimal string (e.g., "1.25")
// selectedToken is { address, decimals }
await deposit(signer, "1.25", { address: "0xToken...", decimals: 18 });

// depositStatus fields:
const { initiated, loading, success, error, tx_hash } = depositStatus;

7) Delayed withdrawals (T+1 to T+5 business days)

Delayed withdrawals use a request → maturity → completion flow.

7a) Request a delayed withdrawal

import { useBoringVaultV1 } from 'boring-vault-ui';

const { delayWithdraw, withdrawStatus } = useBoringVaultV1();

/*
Arguments (positional):
- signer: Ethers JsonRpcSigner
- shareAmount: human-readable shares to withdraw (string)
- tokenOut: { address, decimals }
- maxLoss: human-readable percent as string (e.g., "1" for 1%)
  If "0", contract default is used.
- thirdPartyClaimer: boolean; true allows another account to complete on behalf of the user
*/

await delayWithdraw(
  signer,
  "10.0",
  { address: "0xTokenOut...", decimals: 18 },
  "1",
  false
);

// withdrawStatus mirrors all withdraw actions (request/cancel/complete):
const { initiated, loading, success, error, tx_hash } = withdrawStatus;

7b) List active withdrawal intents

import { useBoringVaultV1 } from 'boring-vault-ui';

const { delayWithdrawStatuses } = useBoringVaultV1();

/*
Returns an array of entries with:
- allowThirdPartyToComplete (boolean)
- maxLoss (number, percent; 1 = 1%)
- maturity (unix timestamp)
- shares (human-readable)
- exchangeRateAtTimeOfRequest (human-readable)
- token: { address, decimals }
*/

const pending = await delayWithdrawStatuses(signer);

7c) Cancel a withdrawal request

import { useBoringVaultV1 } from 'boring-vault-ui';

const { delayWithdrawCancel, withdrawStatus } = useBoringVaultV1();

await delayWithdrawCancel(signer, { address: "0xTokenOut...", decimals: 18 });

// Use withdrawStatus for UI feedback (initiated/loading/success/error/tx_hash)

7d) Complete a matured withdrawal

import { useBoringVaultV1 } from 'boring-vault-ui';

const { delayWithdrawComplete, withdrawStatus } = useBoringVaultV1();

/*
Important:
Validate the request’s maturity time has passed before calling complete,
otherwise the transaction will revert.
*/

await delayWithdrawComplete(signer, { address: "0xTokenOut...", decimals: 18 });

8) UX guidance

  • Gate actions on isBoringV1ContextReady() to avoid undefined config.
  • Show explicit states for approvals and transactions using depositStatus and withdrawStatus.
  • Respect any share-lock period using fetchUserUnlockTime.
  • For allowlisted vaults, disable supply for non-allowed wallets (you’ll receive the allowlist check to call).
  • Use the provided helpers for decimals/rates; avoid hand-rolled math.

9) Troubleshooting

  • If a viem wallet is connected but no signer is present, construct an Ethers BrowserProvider and call getSigner().
  • A revert on delayWithdrawComplete usually indicates maturity has not passed or maxLoss constraints were exceeded.
  • If ReadMe shows a save error about parsing code, switch blocks to text (as above).

Next

  • See Frontend Integration Reference for props, types, and helper signatures.
  • See SDK Examples for end-to-end, copy-paste components.