@chipi-stack/backend
Server-side SDK for Chipi Pay. Handles wallet creation, transactions, SKU purchases, and exchanges.
Install
npm install @chipi-stack/backend
Quick Start
import { ChipiSDK } from "@chipi-stack/backend";
const sdk = new ChipiSDK({
apiPublicKey: process.env.CHIPI_API_PUBLIC_KEY!,
apiSecretKey: process.env.CHIPI_API_SECRET_KEY!,
});
const bearerToken = "your-bearer-token"; // From your auth provider (e.g., Clerk)
// Create a wallet
const wallet = await sdk.createWallet({
params: {
externalUserId: "user-123",
chain: "STARKNET",
encryptKey: "user-pin-or-passkey",
},
bearerToken,
});
// Transfer USDC
const tx = await sdk.transfer({
params: {
wallet: wallet.wallet,
token: "USDC",
recipient: "0x...",
amount: 10,
encryptKey: "user-pin-or-passkey",
},
bearerToken,
});
Confirming a transaction
Every gasless write returns a transaction hash and nothing else, so
waitForTransaction answers the two questions that follow: did it succeed, and
what did it emit.
import { waitForTransaction } from "@chipi-stack/backend";
const receipt = await waitForTransaction(tx);
if (!receipt.success) {
throw new Error(receipt.revertReason ?? "reverted on chain");
}
// Raw events, decode against your own ABI.
const transferred = receipt.events.filter((e) => e.from_address === USDC);
A revert comes back as a result (success: false plus revertReason), not
a throw, because a revert is a normal outcome you branch on. Only "we do not
know yet" throws, meaning a timeout or transport failure, where retrying is the
correct response.
Options: nodeUrl, retryIntervalMs (default 3000) and timeoutMs
(default 300000). nodeUrl is your preferred endpoint rather than the only one
tried, since reading a receipt is idempotent and falls through the RPC chain.
Treasuries
sdk.treasury drives a SHHH multisig from your own server. All three below need
a sk_, so keep them server-side.
// Discover a treasury instead of pinning its id in an env var.
const { items } = await sdk.treasury.list();
const ops = items.find((t) => t.name === "Ops Treasury");
// Require more signatures to borrow than to repay.
await sdk.treasury.setPolicy(ops.id, { sensitiveQuorum: 3 });
const policy = await sdk.treasury.getPolicy(ops.id);
// A transport bound to one treasury + owner, wireable into chipi-react's hooks.
const transport = sdk.treasury.forTreasury({ treasuryId: ops.id, ownerPubkey });
A quorum can only raise requiredApprovals above the wallet's on-chain
threshold. The chain is the hard floor, so a quorum below it produces proposals
that reach approved and then revert at execute.
Bramble and other Starknet wallets
Users who already have Bramble, Braavos or Ready can use it with Chipi. Their key never leaves that wallet.
Sign in with Starknet (14.16.0+): Bramble signs a SNIP-12 message with a one-time nonce, and the account checks it.
import { createStarknetSignInTypedData, verifyStarknetSignIn } from "@chipi-stack/backend";
import { RpcProvider } from "starknet";
const now = Math.floor(Date.now() / 1000);
const typedData = createStarknetSignInTypedData({
domain: "app.example.com",
uri: "https://app.example.com",
address: brambleAddress, // from wallet_requestAccounts
nonce,
issuedAt: now,
expirationTime: now + 600,
});
// The browser asks Bramble: wallet_signTypedData(typedData) → signature
const result = await verifyStarknetSignIn({
typedData,
signature,
provider: new RpcProvider({ nodeUrl }),
expected: { domain: "app.example.com", uri: "https://app.example.com", nonce },
});
if (result.ok) console.log("signed in as", result.address);
Trusted wallets (14.17.0+): Bramble can be a co-owner or a recovery guardian of a Chipi wallet, through the STARK_SNIP12 signer kind. Once added as a co-owner, Bramble approves transactions with wallet_signTypedData and Chipi relays them gaslessly:
const txHash = await sdk.executeWithExternalSigner({
params: {
walletAddress: chipiWallet.publicKey,
ownerIndex: brambleOwnerId,
calls,
signTypedData: async (typedData) =>
(await bramble.request({ type: "wallet_signTypedData", params: typedData })) as string[],
// Checked off-chain before relaying (key from resolveStarkSnip12Signer).
signer: { address: brambleAddress, starkPubkey },
},
bearerToken,
});
The STARK_SNIP12 verifier class is declared on mainnet; isShhhVerifierDeclared checks it on any network. Guides: Sign in with Starknet and Trusted wallets.
Cross-chain USDC (Circle CCTP V2)
Native USDC comes into a Chipi wallet from Base, Arbitrum, Optimism, Polygon or Ethereum, and goes back out, with Circle burning on one chain and minting on the other. Chipi pays the Starknet mint; the user picks free (15–20 minutes from an L2) or fast (Circle's fee, about a minute). The React hooks (useCrossChainDeposit, useCrossChainWithdraw) do the whole flow; the pieces are here for servers and custom flows.
import { ChipiServerSDK, CCTP_EVM_CHAINS, cctpQuote, formatCctpEta } from "@chipi-stack/backend";
export async function depositFromBase(sdk: ChipiServerSDK, burnTxHash: string) {
const base = CCTP_EVM_CHAINS.find((c) => c.key === "base")!;
const quotes = await cctpQuote({ sourceDomain: base.domain, destinationDomain: 25, amount: "25" });
console.log(`free in ${formatCctpEta(quotes.standard.eta)}, or ${quotes.fast.feeUsdc} USDC fee for ${formatCctpEta(quotes.fast.eta)}`);
// Once the user's EVM wallet burned (depositFromEvm in the browser), register the burn:
// Chipi mints on Starknet even if the user's page is gone.
await sdk.registerCctpDeposit({
burnTxHash,
sourceDomain: base.domain,
mintRecipient: wallet.address,
amountUsdc: quotes.standard.amountUsdc,
});
return sdk.getCctpDepositStatus(burnTxHash); // PENDING | MINTED | FAILED | UNREGISTERED
}
Solana is a separate entry point, @chipi-stack/backend/solana, which needs @solana/web3.js (optional peer dependency): depositFromSolana burns through the owner's wallet and waits for the burn to land, buildCctpWithdrawToSolanaCalls names the owner's USDC token account (the only account receiveMessage mints to), solanaUsdcAtaExists and isSolanaWalletAddress gate the recipient.
Guide: Cross-chain USDC.
What you can ship
- Wallet-as-a-service APIs — provision and manage wallets for your users from your backend
- SKU marketplace (airtime, bill pay, gift cards) — integrate real-world purchases with crypto rails
- Crypto remittance backends — build cross-border payment services with stablecoin settlement
- Gasless payment processing for fintechs — accept and send USDC without your users paying gas
Have an idea? Tell us what you want to build
Documentation
Full docs at docs.chipipay.com
License
MIT