@zebec-network/zebec-card-v2-sdk
v2.6.0
Published
This is an SDK for interacting with ZEBEC Card Program in solana
Readme
Zebec Card V2 SDK
A TypeScript SDK for interacting with the Zebec Card Program on Solana blockchain. This SDK provides a comprehensive interface for creating and managing virtual cards with crypto-to-fiat conversion capabilities.
Features
- Card Creation & Management: Create Silver and Carbon cards with customizable configurations
- Token Swapping: Integrated Jupiter aggregator support for seamless token swaps
- Partner Integration: Support for partner-specific card configurations and custom fees
- Fee Management: Flexible fee structures with tiered pricing and custom token fees
- Daily Limits: Built-in daily purchase limits and tracking
- Type Safety: Full TypeScript support with comprehensive type definitions
Installation
npm install @zebec-network/zebec-card-v2-sdkPrerequisites
- Node.js >= 14
- @coral-xyz/anchor >= 0.31.1
- @solana/web3.js >= 1.98.2
Quick Start
Initialize the Service
import {
ZebecCardV2Service,
createAnchorProvider,
} from "@zebec-network/zebec-card-v2-sdk";
import { Connection, Keypair } from "@solana/web3.js";
// Create connection
const connection = new Connection("https://api.mainnet-beta.solana.com");
// Create provider using the SDK helper (wallet must implement AnchorWallet interface)
const provider = createAnchorProvider(connection, wallet);
// Initialize service
const cardService = ZebecCardV2Service.create(provider, "mainnet-beta");Email Hash
All card creation methods require a 32-byte Buffer email hash. Use hashSHA256ToBuffer from @zebec-network/core-utils:
import { hashSHA256ToBuffer } from "@zebec-network/core-utils";
const emailHash = await hashSHA256ToBuffer("[email protected]");Get Next Card Index
Before creating a card, fetch the next available card counter for the user:
const nextCardCounter = await cardService.getNextCardIndex();Create a Silver Card
const payload = await cardService.createSilverCard({
userAddress: userPublicKey,
nextCardCounter,
amount: "100", // USDC amount
inputMintAddress: usdcMintAddress,
outputMintAddress: usdcMintAddress,
emailHash,
currency: "USD",
});
const signature = await payload.execute({ commitment: "confirmed" });Swap and Create Card
// Fetch a quote from Jupiter (GET request with query parameters)
const queryParams = new URLSearchParams({
inputMint: "So11111111111111111111111111111111111111112", // SOL
outputMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", // USDC
amount: "90000000", // 0.09 SOL in lamports
slippageBps: "10", // 0.1% slippage
swapMode: "ExactIn",
});
const quoteInfo = await fetch(
`https://lite-api.jup.ag/swap/v1/quote?${queryParams}`
).then((res) => res.json());
// Create card with swap
const payload = await cardService.swapAndCreateSilverCard({
quoteInfo,
userAddress: userPublicKey,
nextCardCounter,
emailHash,
currency: "USD",
wrapAndUnwrapWsol: true,
});
await payload.execute({ commitment: "confirmed" });Card Types
Silver Card
One-time use virtual card created with a specific amount.
await cardService.createSilverCard({
userAddress: Address,
nextCardCounter: bigint,
amount: Numeric, // USDC amount as string or number
inputMintAddress: Address, // token being spent
outputMintAddress: Address, // must be USDC
emailHash: Buffer, // 32-byte SHA-256 hash
currency: string, // e.g. "USD"
feePayer?: Address, // optional, defaults to userAddress
});Carbon Card
Reloadable virtual card that can be topped up multiple times.
await cardService.loadCarbonCard({
userAddress: Address,
nextCardCounter: bigint,
amount: Numeric,
inputMintAddress: Address,
outputMintAddress: Address,
emailHash: Buffer,
currency: string,
reloadCardId: string, // identifier for the card being reloaded
feePayer?: Address,
});Configuration Management
Initialize Card Config (Admin)
await cardService.initCardConfig({
zicOwnerAddress?: Address, // optional, defaults to provider.publicKey
cardVaultAddress: Address,
revenueVaultAddress: Address,
commissionVaultAddress: Address,
usdcAddress: Address,
revenueFeePercent: Numeric, // e.g. "1.0"
nativeFeePercent: Numeric, // fee for native SOL swaps
nonNativeFeePercent: Numeric, // fee for non-native token swaps
carbonFeePercent: Numeric, // fee for Carbon card loads
minCardAmount: Numeric, // minimum USDC amount per card
maxCardAmount: Numeric, // maximum USDC amount per card
dailyCardPurchaseLimit: Numeric,
feeTiers: FeeTier[],
});Update Card Config
await cardService.setCardConfig({
zicOwnerAddress?: Address,
newZicOwnerAddress: Address,
cardVaultAddress: Address,
revenueVaultAddress: Address,
commissionVaultAddress: Address,
revenueFeePercent: Numeric,
nativeFeePercent: Numeric,
nonNativeFeePercent: Numeric,
carbonFeePercent: Numeric,
minCardAmount: Numeric,
maxCardAmount: Numeric,
dailyCardPurchaseLimit: Numeric,
feeTiers: FeeTier[],
});Set Custom Token Fees
await cardService.setCustomFees({
tokenFeeList: [
{ tokenAddress: solMintAddress, fee: "1.5" },
{ tokenAddress: usdtMintAddress, fee: "2.0" },
],
});Delete Custom Token Fees
await cardService.deleteCustomFees({
tokenAddressList: [solMintAddress, usdtMintAddress],
});Partner Integration
The SDK supports partner-specific configurations with separate fee structures.
Initialize Partner Config (Admin)
import { ZebecPartnerCardService } from "@zebec-network/zebec-card-v2-sdk";
// partnershipName is bound at construction time
const partnerService = ZebecPartnerCardService.create(
"MyPartner",
provider,
"mainnet-beta",
);
await partnerService.initPartnerCardConfig({
zicOwnerAddress?: Address, // optional, defaults to provider.publicKey
cardVaultAddress: Address,
revenueVaultAddress: Address,
usdcAddress: Address,
carbonFeePercent: Numeric,
minCardAmount: Numeric,
maxCardAmount: Numeric,
dailyCardPurchaseLimit: Numeric,
feeTiers: FeeTier[],
});Update Partner Config
await partnerService.setPartnerCardConfig({
zicOwnerAddress?: Address,
newZicOwnerAddress: Address,
cardVaultAddress: Address,
revenueVaultAddress: Address,
carbonFeePercent: Numeric,
minCardAmount: Numeric,
maxCardAmount: Numeric,
dailyCardPurchaseLimit: Numeric,
feeTiers: FeeTier[],
});Create Partner Silver Card
await partnerService.createPartnerSilverCard({
userAddress: Address,
nextCardCounter: bigint,
amount: Numeric,
inputMintAddress: Address,
outputMintAddress: Address,
emailHash: Buffer,
currency: string,
feePayer?: Address,
});Load Partner Carbon Card
await partnerService.loadPartnerCarbonCard({
userAddress: Address,
nextCardCounter: bigint,
amount: Numeric,
inputMintAddress: Address,
outputMintAddress: Address,
emailHash: Buffer,
currency: string,
reloadCardId: string,
feePayer?: Address,
});Swap and Create Partner Cards
// Swap and create
await partnerService.swapAndCreatePartnerSilverCard({
quoteInfo,
userAddress: Address,
nextCardCounter: bigint,
emailHash: Buffer,
currency: string,
wrapAndUnwrapWsol?: boolean,
feePayer?: Address,
});
// Swap and load
await partnerService.swapAndLoadPartnerCarbonCard({
quoteInfo,
userAddress: Address,
nextCardCounter: bigint,
emailHash: Buffer,
currency: string,
reloadCardId: string,
wrapAndUnwrapWsol?: boolean,
feePayer?: Address,
});Partner Custom Fees
// Set
await partnerService.setPartnerCustomFees({
tokenFeeList: [{ tokenAddress: solMintAddress, fee: "1.5" }],
});
// Delete
await partnerService.deletePartnerCustomFees({
tokenAddressList: [solMintAddress],
});Data Retrieval
Standard Service
// Card configuration
const config = await cardService.getCardConfigInfo();
console.log({
index: config.index,
minAmount: config.providerConfig.minCardAmount,
maxAmount: config.providerConfig.maxCardAmount,
totalCardSold: config.totalCardSold,
dailyCardPurchaseLimit: config.dailyCardPurchaseLimit,
feeTiers: config.providerConfig.feeTiers,
});
// Purchase info
const purchaseInfo = await cardService.getCardPurchaseInfo(cardPurchasePda);
console.log({
amount: purchaseInfo.amount,
buyer: purchaseInfo.buyerAddress,
timestamp: purchaseInfo.purchaseAt,
});
// User daily purchase record
const userRecord = await cardService.getUserPurchaseRecord(userPurchaseRecordPda);
console.log({
dailyTotal: userRecord.totalCardBoughtPerDay,
lastPurchase: new Date(userRecord.lastCardBoughtTimestamp * 1000),
});
// Custom token fees
const fees = await cardService.getCustomTokenFees();
// Next card index
const nextCardCounter = await cardService.getNextCardIndex();Partner Service
// Partner card configuration (uses this.partnershipName internally)
const partnerConfig = await partnerService.getPartnerCardConfigInfo();
// Partner custom fees (uses this.partnershipName internally)
const partnerFees = await partnerService.getPartnerCustomTokenFees();
// Partner user purchase record
const partnerUserRecord = await partnerService.getPartnerUserPurchaseRecord(pda);
// Purchase info (same as standard)
const purchaseInfo = await partnerService.getCardPurchaseInfo(pda);
// Next card index for partner (partnershipName must be passed explicitly)
const nextCardCounter = await partnerService.getNextCardIndex("MyPartner");PDA Derivation
import {
deriveCardConfigPda,
deriveCardPurchasePda,
deriveUserPurchaseRecordPda,
deriveTokenFeeMapPda,
derivePartnerCardConfigPda,
derivePartnerCardPurchasePda,
derivePartnerUserPurchaseRecordPda,
derivePartnerTokenFeeMapPda,
} from "@zebec-network/zebec-card-v2-sdk";
const programId = cardService.program.programId;
// Standard PDAs
const [cardConfigPda] = deriveCardConfigPda(programId);
const [tokenFeeMapPda] = deriveTokenFeeMapPda(programId);
const [purchasePda] = deriveCardPurchasePda(userAddress, nextCardCounter, programId);
const [userRecordPda] = deriveUserPurchaseRecordPda(userAddress, programId);
// Partner PDAs
const [partnerConfigPda] = derivePartnerCardConfigPda(programId, "PartnerName");
const [partnerTokenFeeMapPda] = derivePartnerTokenFeeMapPda(partnerConfigPda, programId);
const [partnerPurchasePda] = derivePartnerCardPurchasePda(userAddress, partnerConfigPda, nextCardCounter, programId);
const [partnerUserRecordPda] = derivePartnerUserPurchaseRecordPda(userAddress, partnerConfigPda, programId);Error Handling
import {
NotEnoughBalanceError,
AmountOutOfRangeError,
DailyCardLimitReachedError,
InvalidUsdcAddressError,
QuoteResponseError,
AssociatedTokenAccountDoesNotExistsError,
} from "@zebec-network/zebec-card-v2-sdk";
try {
await cardService.createSilverCard(params);
} catch (error) {
if (error instanceof NotEnoughBalanceError) {
console.error("Insufficient balance");
} else if (error instanceof AmountOutOfRangeError) {
console.error(`Amount must be between ${error.minRange} and ${error.maxRange}`);
} else if (error instanceof DailyCardLimitReachedError) {
console.error(`Daily limit of ${error.dailyCardLimit} reached`);
} else if (error instanceof InvalidUsdcAddressError) {
console.error(`Invalid USDC mint: ${error.mintAddress}`);
}
}Fee Tiers
Fee tiers allow for progressive pricing based on card amounts:
const feeTiers: FeeTier[] = [
{ minAmount: "0", maxAmount: "100", feePercent: "3.0" },
{ minAmount: "100", maxAmount: "500", feePercent: "2.5" },
{ minAmount: "500", maxAmount: "1000", feePercent: "2.0" },
{ minAmount: "1000", maxAmount: "10000", feePercent: "1.5" },
];Readonly Provider
For read-only operations without wallet signing:
import { createReadonlyProvider } from "@zebec-network/zebec-card-v2-sdk";
const readonlyProvider = createReadonlyProvider(
connection,
userPublicKey, // optional
);
const cardService = ZebecCardV2Service.create(readonlyProvider, "mainnet-beta");
const config = await cardService.getCardConfigInfo();Network Support
The SDK supports Solana mainnet-beta and devnet. The network argument must match the cluster of the connection:
// Mainnet
const mainnetService = ZebecCardV2Service.create(provider, "mainnet-beta");
// Devnet
const devnetService = ZebecCardV2Service.create(devnetProvider, "devnet");Transaction Building
All transaction-creating methods return a TransactionPayload object:
const payload = await cardService.createSilverCard(params);
// Execute and get signature
const signature = await payload.execute({ commitment: "confirmed" });
// Simulate without submitting
const result = await payload.simulate({ commitment: "confirmed" });
console.log(result.value.logs);
// Get raw versioned transaction for manual handling
const transaction = await payload.getTransaction();Best Practices
- Amount Precision: Always provide amounts as strings to avoid floating-point precision issues
- Email Hash: Use
hashSHA256ToBufferfrom@zebec-network/core-utilsto produce the required 32-byte Buffer - Card Counter: Always call
getNextCardIndex()immediately before creating a card to avoid counter collisions - Error Handling: Implement comprehensive error handling for all transaction operations
- Network Validation: Ensure your connection and service network match — the SDK will throw on mismatch
- Daily Limits: Check
getUserPurchaseRecordbefore creating cards to preventDailyCardLimitReachedError
API Reference
ZebecCardV2Service
Main service class for standard card operations.
Factory:
create(provider, network)— Create a service instance
Card Configuration (Admin):
initCardConfig(params)→Promise<TransactionPayload>setCardConfig(params)→Promise<TransactionPayload>
Card Creation:
createSilverCard(params)→Promise<TransactionPayload>loadCarbonCard(params)→Promise<TransactionPayload>swapAndCreateSilverCard(params)→Promise<TransactionPayload>swapAndLoadCarbonCard(params)→Promise<TransactionPayload>
Fee Management (Admin):
setCustomFees(params)→Promise<TransactionPayload>deleteCustomFees(params)→Promise<TransactionPayload>
Data Retrieval:
getNextCardIndex()→Promise<bigint>getCardConfigInfo()→Promise<CardConfigInfo>getCardPurchaseInfo(pda)→Promise<CardPurchaseInfo>getUserPurchaseRecord(pda)→Promise<UserPurchaseRecordInfo>getCustomTokenFees()→Promise<TokenFeeRecord[]>
ZebecPartnerCardService
Service class for partner-specific card operations.
Factory:
create(partnershipName, provider, network)— Create a service instance;partnershipNameis bound to all subsequent calls
Partner Configuration (Admin):
initPartnerCardConfig(params)→Promise<TransactionPayload>setPartnerCardConfig(params)→Promise<TransactionPayload>
Card Creation:
createPartnerSilverCard(params)→Promise<TransactionPayload>loadPartnerCarbonCard(params)→Promise<TransactionPayload>swapAndCreatePartnerSilverCard(params)→Promise<TransactionPayload>swapAndLoadPartnerCarbonCard(params)→Promise<TransactionPayload>
Fee Management (Admin):
setPartnerCustomFees(params)→Promise<TransactionPayload>deletePartnerCustomFees(params)→Promise<TransactionPayload>
Data Retrieval:
getNextCardIndex(partnershipName)→Promise<bigint>— requires explicitpartnershipNamegetPartnerCardConfigInfo()→Promise<PartnerCardConfigInfo>— usesthis.partnershipNamegetCardPurchaseInfo(pda)→Promise<CardPurchaseInfo>getPartnerUserPurchaseRecord(pda)→Promise<UserPurchaseRecordInfo>getPartnerCustomTokenFees()→Promise<TokenFeeRecord[]>— usesthis.partnershipName
Provider Utilities
import {
createAnchorProvider,
createReadonlyProvider,
} from "@zebec-network/zebec-card-v2-sdk";
// For signing transactions
createAnchorProvider(connection, wallet, options?) → AnchorProvider
// For read-only queries
createReadonlyProvider(connection, walletAddress?) → ReadonlyProviderKey Types
type FeeTier = {
minAmount: Numeric;
maxAmount: Numeric;
feePercent: Numeric;
};
type TokenFeeRecord = {
tokenAddress: Address;
fee: Numeric;
};
type CardConfigInfo = {
address: Address;
index: bigint;
zicOwner: Address;
nativeFeePercent: Numeric;
nonNativeFeePercent: Numeric;
revenueFeePercent: Numeric;
carbonFeePercent: Numeric;
usdcMint: Address;
revenueVault: Address;
commissionVault: Address;
cardVault: Address;
totalCardSold: Numeric;
dailyCardPurchaseLimit: Numeric;
providerConfig: ProviderConfig;
};
type PartnerCardConfigInfo = {
address: Address;
partnerName: string;
index: bigint;
zicOwner: Address;
carbonFeePercent: Numeric;
usdcMint: Address;
revenueVault: Address;
cardVault: Address;
totalCardSold: Numeric;
dailyCardPurchaseLimit: Numeric;
providerConfig: ProviderConfig;
};
type ProviderConfig = {
minCardAmount: Numeric;
maxCardAmount: Numeric;
feeTiers: FeeTier[];
};
type CardPurchaseInfo = {
address: Address;
index: bigint;
buyerAddress: Address;
amount: Numeric;
purchaseAt: number; // Unix timestamp
};
type UserPurchaseRecordInfo = {
address: Address;
owner: Address;
lastCardBoughtTimestamp: number; // Unix timestamp
totalCardBoughtPerDay: Numeric;
};License
MIT
Support
For issues and questions:
- GitHub Issues: Zebec Card V2 SDK
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting pull requests.
