useInvestViewModel.tsx.
The Passage SDK is responsible for recording the participation (and verifying the on-chain approval). Your app is responsible for connecting the user’s wallet, collecting their input, and submitting the ERC-20 approve transaction.
Prerequisites
- Completed OAuth with a working
CoinListProvider - An
OfferDetailand theOfferOptionthe user picked (from Display offer details) - A wallet integration. The partner demo uses wagmi + Reown AppKit. Anything that gives you a connected EVM address, balance reads, and
writeContract/waitForTransactionReceiptworks.
Flow at a glance
- idle - user picks the funding asset (
USDC,USDT, etc. fromofferDetail.fundingAssets) and enters an amount. - awaiting_wallet - validate input, ensure the wallet is on Ethereum mainnet, then send the ERC-20
approvetransaction. The wallet popup is open. - confirming_tx - the user signed; wait for
waitForTransactionReceipt. - recording - call
coinlist.createParticipation({ ..., approvalTransactionHash }). The Passage backend reads the on-chain allowance to confirm before recording. - success / error - redirect on success; surface a user-readable message on error.
Step 1: Connect the wallet
The wallet UI is yours. From the SDK’s point of view all you need is a connected EVMaddress and the means to submit transactions on Ethereum mainnet. With wagmi + Reown AppKit that’s a few hooks:
src/lib/providers/WalletConnectProvider.tsx in the partner demo for the AppKit/wagmi setup.
Step 2: Validate and submit the approval transaction
Theapprove call grants the funding contract permission to pull the funding asset (USDC, USDT, …) from the wallet later. It does not move funds. Before showing the wallet popup, sanity-check that the user has enough ETH for gas - otherwise the transaction will fail on-chain after the user signs.
assetContract(...) and fundingContract(...) are partner-side helpers that map a Passage AssetId to the right ERC-20 contract address and the matching Passage funding contract address. See src/types/coinlist.ts in the partner demo.useSwitchChain triggers the wallet’s native “Switch Network” dialog:
Step 3: Record the participation
Once the approval is mined, callcreateParticipation with the tx hash. The hash is what lets Passage verify on-chain that the allowance actually exists before recording the participation.
createParticipation rejects - surface that as a retryable error rather than a hard failure.
Wiring it together
The full state machine plus error handling lives inuseInvestViewModel.tsx. The skeleton is:
Reflecting status back in the UI
After a successfulcreateParticipation, the user lands back on the offer detail page. Use useParticipations(offerId) to fetch the user’s participations for that offer and render their status (pending, completed, failed, remitted, etc.). The participation moves through statuses asynchronously - the SDK doesn’t push; you re-fetch on session refresh or after a user action.
Common questions
Why an approve instead of a transfer?
Why an approve instead of a transfer?
Passage settles the funding move on its own contracts, on its own schedule, after on-chain verification. The user only signs an allowance up to the amount they want to invest; nothing leaves their wallet until the Passage backend later pulls it. This pattern is cancellable (the user can revoke the allowance) and lets Passage batch transfers.
Do I need to use wagmi / Reown AppKit specifically?
Do I need to use wagmi / Reown AppKit specifically?
No - any EVM wallet integration that gives you
address, balance reads, writeContract, and a way to wait for a tx receipt works. The partner demo uses wagmi + Reown AppKit because it’s a maintained, framework-agnostic option. The SDK only sees the resulting tx hash.What if the user is on the wrong chain?
What if the user is on the wrong chain?
Prompt a network switch before calling
writeContract. With wagmi, useSwitchChain triggers the wallet’s native switch dialog. The partner demo bails out via ensureMainnet(currentChainId, switchChain) before submitting the approval.How is gas handled?
How is gas handled?
Gas is paid by the user in ETH, like any other on-chain transaction. The partner demo enforces a minimum ETH balance check (
0.001 ETH) before opening the wallet popup so the user gets a clear error instead of a failed transaction.What about chains other than Ethereum?
What about chains other than Ethereum?
The flow generalises: connect on the right chain, approve the right ERC-20 contract for the matching funding contract, then call
createParticipation with the resulting tx hash. Your account manager will confirm which chains and assets are enabled for your offers.Next step
Display participation status
Use
useParticipations(offerId) or CoinListClient.fetchParticipations() to render the user’s participation status after they invest.