UNPKG

@zebec-network/evm-card-sdk

Version:
632 lines (453 loc) 18.5 kB
# EVM Zebec Card SDK An SDK for interacting with Zebec Instant Card EVM contracts. ## Installation ```bash npm install @zebec-network/evm-card-sdk yarn add @zebec-network/evm-card-sdk ``` ## Supported Chains | Chain | Chain ID | Enum value | | --------------- | -------- | ------------------------------- | | Ethereum | 1 | `SupportedChain.Mainnet` | | Sepolia | 11155111 | `SupportedChain.Sepolia` | | Base | 8453 | `SupportedChain.Base` | | BSC | 56 | `SupportedChain.Bsc` | | BSC Testnet | 97 | `SupportedChain.BscTestnet` | | Odyssey | 153153 | `SupportedChain.Odyssey` | | Odyssey Testnet | 131313 | `SupportedChain.OdysseyTestnet` | | Polygon | 137 | `SupportedChain.Polygon` | | Polygon Amoy | 80002 | `SupportedChain.PolygonAmoy` | The `SupportedChain` enum and `parseSupportedChain(chainId)` helper are exported for use with chain IDs. `parseSupportedChain` throws if the chain is unsupported handy for validating user input before constructing the service. ```ts import { parseSupportedChain, ODYSSEY_CHAIN_IDS } from "@zebec-network/evm-card-sdk"; const chain = parseSupportedChain(11155111); // SupportedChain.Sepolia const isOdyssey = ODYSSEY_CHAIN_IDS.includes(chain); ``` --- ## Quick Start ```ts import { ZebecCardService, SupportedChain } from "@zebec-network/evm-card-sdk"; import { ethers } from "ethers"; const provider = new ethers.BrowserProvider(window.ethereum); const signer = await provider.getSigner(); const service = new ZebecCardService(signer, SupportedChain.Sepolia); ``` > **Odyssey chains** (`OdysseyTestnet`, `Odyssey`) use a different contract (`OdysseyZebecCard`) and support a different set of methods. The service throws `Method not supported for this chain` when you call a method that is not available on the current chain. See chain-specific notes in each method below. ### Two ways to buy a card The SDK supports two purchase flows: 1. **Direct purchase** (`buyCardDirect`) single transaction; USDC (or any supported token via `swapAndBuyCardDirect` / `swapAndBuyCardOdyssey`) is pulled from the user's wallet at purchase time. Available on **all chains**. 2. **Vault-based purchase** (`depositUsdc` `buyCard`) user first tops up an on-contract USDC vault, then buys cards from that balance. `withdraw` returns unused vault balance. **Non-Odyssey chains only.** ### Gas overrides Every write method accepts an optional `overrides?: ethers.Overrides`. The SDK applies `DEFAULT_GAS_LIMIT = 3_000_000` when `overrides.gasLimit` is not provided. --- ## API Reference ### `ZebecCardService` #### Constructor ```ts new ZebecCardService(signer: ethers.Signer, chainId: number) ``` | Parameter | Type | Description | | --------- | --------------- | ------------------------------------ | | `signer` | `ethers.Signer` | Ethers signer from your wallet | | `chainId` | `number` | One of the supported chain IDs above | **Public properties:** | Property | Type | Description | | ----------- | ------------------------------- | ---------------------------------- | | `zebecCard` | `ZebecCard \| OdysseyZebecCard` | Main Zebec Card contract | | `usdcToken` | `Token` | USDC ERC-20 contract | | `weth` | `Weth` | WETH contract | | `signer` | `ethers.Signer` | Signer passed to the constructor | | `chainId` | `number` | Chain ID passed to the constructor | #### Method availability | Method | Non-Odyssey | Odyssey | Notes | | --------------------------------------- | :---------: | :-----: | ---------------------------------------- | | `approve` / `wrapEth` | | | Token utilities | | `depositUsdc` | | | Vault top-up | | `withdraw` | | | Vault withdrawal | | `buyCard` | | | From vault balance | | `buyCardDirect` | | | From wallet (no vault) | | `swapAndDeposit` | | | 1inch swap vault | | `swapAndBuyCardDirect` | | | 1inch swap card | | `swapAndBuyCardOdyssey` | | | Native ETH swap card | | `setReloadableFee` / `getReloadableFee` | | | Carbon (reloadable) card fee | | `setFee` | | | Per-tier fee on Odyssey | | `getMinimumUsdcAmount` | | | Computes min USDC for a given ETH amount | | All other admin/query | | | | --- ### Token Utilities #### `approve` Approves a token spender. Only submits a transaction if the current allowance is less than the requested amount. Returns `null` if no approval is needed. ```ts const tx = await service.approve({ token: tokenAddress, // ERC-20 token address spender: spenderAddress, amount: "1000", // Human-readable amount (e.g. USDC units) }); if (tx) { const receipt = await tx.wait(); console.log("approval hash:", receipt?.hash); } ``` #### `wrapEth` Wraps native ETH into WETH. ```ts const tx = await service.wrapEth({ amount: "0.001" }); const receipt = await tx.wait(); console.log("txHash:", receipt?.hash); ``` --- ### Vault Flow (Non-Odyssey only) `ZebecCard` keeps a per-user USDC balance ("card vault") on-contract. Top it up with `depositUsdc`, buy cards from it with `buyCard`, and withdraw any leftover with `withdraw`. None of these methods are available on Odyssey chains they throw `Method not supported for this chain`. #### `depositUsdc` Deposits USDC from the user's wallet into their card vault. Requires the `ZebecCard` contract to be approved as an USDC spender first. ```ts const token = await service.usdcToken.getAddress(); const spender = await service.zebecCard.getAddress(); const amount = "1000"; const approval = await service.approve({ token, spender, amount }); if (approval) await approval.wait(); const tx = await service.depositUsdc({ amount }); await tx.wait(); ``` #### `withdraw` Withdraws USDC from the user's card vault back to their wallet. ```ts const tx = await service.withdraw({ amount: "5.0" }); await tx.wait(); ``` #### `buyCard` Buys a card by debiting the user's vault balance. The SDK validates locally before submitting: - email format (via the `isEmailValid` helper) - vault balance requested amount - amount within `minCardAmount` / `maxCardAmount` from `cardConfig` - daily purchase total `dailyCardBuyLimit` ```ts const tx = await service.buyCard({ amount: "199", cardType: "silver", buyerEmail: "user@example.com", }); await tx.wait(); ``` #### `swapAndDeposit` Swaps a source token to USDC via the 1inch aggregator and deposits the result into the user's vault in a single transaction. Use [`fetchSwapData`](#fetching-swap-quote) below to obtain `swapData`. The source token must be approved both for the 1inch router (so the aggregator can pull it) and for the `ZebecCard` contract. ```ts const tx = await service.swapAndDeposit({ swapData }); await tx.wait(); ``` --- ### Card Purchase #### `buyCardDirect` Buys a card in a single transaction USDC is pulled directly from the user's wallet (no prior vault deposit needed). Requires approval of the `ZebecCard` contract to spend USDC. Works on **all supported chains**. The SDK validates email format, amount range, and daily purchase limit before submitting. ```ts const token = await service.usdcToken.getAddress(); const spender = await service.zebecCard.getAddress(); const amount = "199"; const approval = await service.approve({ token, spender, amount }); if (approval) { await approval.wait(); } const tx = await service.buyCardDirect({ amount, cardType: "carbon", buyerEmail: "user@example.com", }); const receipt = await tx.wait(); console.log("txhash:", receipt?.hash); ``` Card type mapping (handled internally pass `"silver"` or `"carbon"`): | `cardType` value | Contract value | | ---------------- | ------------------ | | `"silver"` | `"non_reloadable"` | | `"carbon"` | `"reloadable"` | --- ### Swap & Buy These methods allow users to pay with tokens other than USDC. The contract handles the swap to USDC internally. > **Non-Odyssey chains** use the 1inch aggregator. **Odyssey chains** use a native ETH swap path. #### Fetching Swap Quote Fetch swap data from the Zebec backend before calling swap methods: ```ts const urlParams = new URLSearchParams({ src, // source token address dst, // destination token address (USDC) from, // user wallet address origin, // user wallet address amount, // amount in source token smallest unit slippage: "5", compatibility: "true", chainId: chainId.toString(), receiver, // ZebecCard contract address disableEstimate: "true", }); const url = `https://api.card.zebec.io/swap/get1inchswapquotes?${urlParams}`; const swapData = await fetch(url, { headers: { Accept: "application/json", "Content-Type": "application/json; charset=utf-8" }, }).then((r) => r.json()); ``` #### `swapAndBuyCardDirect` Swaps a source token to USDC and buys a card in one transaction. **Non-Odyssey chains only.** Requires approval of the `ZebecCard` contract to spend the source token. ```ts const approval = await service.approve({ token: srcTokenAddress, spender: await service.zebecCard.getAddress(), amount: srcAmount, }); if (approval) await approval.wait(); const tx = await service.swapAndBuyCardDirect({ swapData, cardType: "carbon", buyerEmail: "user@example.com", }); const receipt = await tx.wait(); console.log("txhash:", receipt?.hash); ``` #### `swapAndBuyCardOdyssey` Swaps native ETH to USDC and buys a card in one transaction. **Odyssey chains only.** ```ts const tx = await service.swapAndBuyCardOdyssey({ cardType: "silver", buyerEmail: "user@example.com", ether: "1265", // Amount of native ETH (in smallest unit) slippage: 1, // Slippage tolerance in percent }); const receipt = await tx.wait(); console.log("txhash:", receipt?.hash); ``` --- ### Query Methods #### `getUserBalance` Returns the user's USDC vault balance as a human-readable string. ```ts const balance = await service.getUserBalance({ userAddress: signerAddress }); console.log("balance:", balance); ``` #### `getCardPurhcaseOfDay` Returns the user's card purchase info for the current day. ```ts const purchase = await service.getCardPurhcaseOfDay({ userAddress: signerAddress }); console.log("total purchased today:", purchase.totalCardPurchased); console.log("timestamp:", purchase.cardPurchasedTimestamp); ``` Returns a `CardPurchaseOfDay` object: ```ts { totalCardPurchased: string; // Total USDC value purchased today cardPurchasedTimestamp: number; // Unix timestamp of last purchase } ``` #### `getCardConfig` Returns the current contract configuration. ```ts const config = await service.getCardConfig(); console.log(config); ``` Returns a `CardConfig` object: ```ts { nativeFeePercent: string; nonNativeFeePercent: string; revenueFeePercent: string; totalCardSold: bigint; cardVault: string; revenueVault: string; commissionVault: string; usdcAddress: string; minCardAmount: string; maxCardAmount: string; dailyCardPurchaseLimit: string; } ``` #### `getFeeTiers` Returns configured fee tiers. ```ts const tiers = await service.getFeeTiers(); // [{ feePercent: "1.5", minAmount: "0", maxAmount: "500" }, ...] ``` #### `getAdmin` Returns the admin (owner) address of the contract. ```ts const admin = await service.getAdmin(); console.log("admin:", admin); ``` #### `getCustomFee` Returns the custom fee configured for a specific token, as a percentage string. ```ts const fee = await service.getCustomFee({ tokenAddress: "0x..." }); console.log("fee:", fee); // e.g. "2.5" ``` #### `getReloadableFee` Returns the reloadable (carbon) card fee as a percentage string. **Non-Odyssey chains only.** ```ts const fee = await service.getReloadableFee(); console.log("reloadable fee:", fee); ``` #### `getMinimumUsdcAmount` Returns the minimum USDC amount for a given ETH amount with slippage applied. **Odyssey chains only.** ```ts const minUsdc = await service.getMinimumUsdcAmount("1265", 1); console.log("min USDC:", minUsdc); ``` --- ### Admin Methods Admin methods can only be called by the contract owner. Use a `ZebecCardService` instance created with the admin signer. #### `setNativeFee` ```ts await (await service.setNativeFee({ feeInPercent: "1.5" })).wait(); ``` #### `setNonNativeFee` ```ts await (await service.setNonNativeFee({ feeInPercent: "2.5" })).wait(); ``` #### `setRevenueFee` ```ts await (await service.setRevenueFee({ feeInPercent: "5.0" })).wait(); ``` #### `setRevenueVault` ```ts await (await service.setRevenueVault({ vaultAddress: "0x..." })).wait(); ``` #### `setCommissionVault` ```ts await (await service.setCommissionVault({ vaultAddress: "0x..." })).wait(); ``` #### `setCardVault` ```ts await (await service.setCardVault({ vaultAddress: "0x..." })).wait(); ``` #### `setUsdcAddress` ```ts await (await service.setUsdcAddress({ tokenAddress: "0x..." })).wait(); ``` #### `setMinCardAmount` ```ts await (await service.setMinCardAmount({ minCardAmount: "10" })).wait(); ``` #### `setMaxCardAmount` ```ts await (await service.setMaxCardAmount({ maxCardAmount: "1000" })).wait(); ``` #### `setDailyCardPurchaseLimit` ```ts await (await service.setDailyCardPurchaseLimit({ dailyCardPurchaseLimit: "5000" })).wait(); ``` #### `setFee` (Odyssey chains only) Updates the fee for a given amount range, or inserts a new tier if the range doesn't exist. ```ts await (await service.setFee({ minAmount: "0", maxAmount: "500", feePercent: "1.5" })).wait(); ``` #### `setFeeTiers` Replaces all fee tiers. ```ts await ( await service.setFeeTiers({ feeTiers: [ { feePercent: "1.0", minAmount: "0", maxAmount: "200" }, { feePercent: "1.5", minAmount: "200", maxAmount: "500" }, { feePercent: "2.0", minAmount: "500", maxAmount: "9999" }, ], }) ).wait(); ``` #### `setCustomFee` Sets a custom fee percentage for a specific token. ```ts await (await service.setCustomFee({ tokenAddress: "0x...", fee: "3.0" })).wait(); ``` #### `setReloadableFee` (Non-Odyssey chains only) Sets the fee for reloadable (carbon) cards. ```ts await (await service.setReloadableFee({ fee: "1.0" })).wait(); ``` --- ## Using Contract Factories The SDK exports Typechain-generated factory classes for creating contract instances directly. ```ts import { Token__factory, ZebecCard__factory } from "@zebec-network/evm-card-sdk"; import { ethers } from "ethers"; // Create an ERC-20 token contract instance const token = Token__factory.connect(tokenAddress, signer); const balance = await token.balanceOf(walletAddress); // Create a ZebecCard interface for parsing logs function parseLogs(logs: readonly ethers.Log[]) { const iface = ZebecCard__factory.createInterface(); return logs.map((l) => iface.parseLog(l)).filter(Boolean) as ethers.LogDescription[]; } ``` --- ## Parsing Contract Events Use the ZebecCard interface to decode transaction receipt logs. ```ts import { ethers } from "ethers"; import { ZebecCard__factory } from "@zebec-network/evm-card-sdk"; const provider = new ethers.JsonRpcProvider(rpcUrl); function parseLogs(logs: readonly ethers.Log[]) { const iface = ZebecCard__factory.createInterface(); return logs.map((l) => iface.parseLog(l)).filter(Boolean) as ethers.LogDescription[]; } // Deposited event const receipt = await provider.getTransactionReceipt(txHash); const events = parseLogs(receipt!.logs); const deposited = events.find((e) => e.name === "Deposited"); deposited?.args.forEach((arg, i) => console.log(`arg ${i}:`, arg)); // Withdrawn event const withdrawn = events.find((e) => e.name === "Withdrawn"); // CardPurchased event const cardPurchased = events.find((e) => e.name === "CardPurchased"); // Swapped event const swapped = events.find((e) => e.name === "Swapped"); ``` --- ## Exported Constants & Helpers ```ts import { SupportedChain, ODYSSEY_CHAIN_IDS, ZEBEC_CARD_ADDRESS, USDC_ADDRESS, WETH_ADDRESS, ATOKEN_ADDRESS, DEFAULT_GAS_LIMIT, parseSupportedChain, } from "@zebec-network/evm-card-sdk"; // Get ZebecCard contract address for a chain const contractAddress = ZEBEC_CARD_ADDRESS[SupportedChain.Sepolia]; // Parse a raw chain ID to SupportedChain enum (throws if unsupported) const chain = parseSupportedChain(11155111); // SupportedChain.Sepolia // Check if a chain is an Odyssey chain const isOdyssey = ODYSSEY_CHAIN_IDS.includes(chainId); ``` --- ## Exported ABIs Raw ABIs are available for use with other libraries: ```ts import { ZEBEC_CARD_ABI, ERC20_TOKEN_ABI, WETH_ABI, AGGREGATOR_ROUTER_V6_ABI, } from "@zebec-network/evm-card-sdk"; ``` --- ## TypeScript Types ```ts import type { CardType, CardConfig, FeeTier, CardPurchaseOfDay, SwapData, SwapAndBuyCardParams, SwapAndBuyCardParamsOdyssey, } from "@zebec-network/evm-card-sdk"; ``` | Type | Description | | ----------------------------- | ------------------------------------------------ | | `CardType` | `"silver" \| "carbon"` | | `CardConfig` | Full contract configuration object | | `FeeTier` | `{ feePercent, minAmount, maxAmount }` | | `CardPurchaseOfDay` | `{ totalCardPurchased, cardPurchasedTimestamp }` | | `SwapData` | Swap quote data from the Zebec backend | | `SwapAndBuyCardParams` | Parameters for `swapAndBuyCardDirect` | | `SwapAndBuyCardParamsOdyssey` | Parameters for `swapAndBuyCardOdyssey` |