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 seconds5) 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 JsonRpcSigner6) 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
depositStatusandwithdrawStatus. - 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
BrowserProviderand callgetSigner(). - A revert on
delayWithdrawCompleteusually indicates maturity has not passed ormaxLossconstraints 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.
Updated 2 months ago
What’s Next
