@bbuilders/djeon402-sdk-node
v2.0.1
Published
DJEON402 SDK for Node.js and Backend applications
Maintainers
Readme
@bbuilders/djeon402-sdk-node
Official Node.js SDK for DJEON402 token - designed for backend and server-side applications.
What's New in 2.0
Breaking changes:
- Token decimals are 6 (USDC/x402 convention) — USD/amount conversions use
parseTokenAmount(no more float precision loss) kyc.checkDailyLimitnow returns the real allow/deny boolean (simulates first, records on-chain only when allowed; caller must be KYC admin or approved provider)- Custom chain ids now get a proper chain definition (previously any non-Base chain was signed as Foundry/31337)
New:
kyc.verifyaccepts an optionalkycHash; when omitted it is derived deterministically fromdocuments(no more random placeholder hash)
Overview
@bbuilders/djeon402-sdk-node provides a comprehensive, type-safe interface for interacting with DJEON402 smart contracts from Node.js environments. It handles all the complexity of blockchain interactions, signing, and transaction management.
Key Features:
- Full ERC-20 token operations (transfer, approve, mint, burn)
- Admin functions (roles, blacklist, pause/unpause)
- KYC management (verify, check status, limits)
- x402/EIP-3009 gasless transactions
- Built on viem for type safety and performance
- Server-side signing with private keys
- Support for Foundry (Anvil), Base, and custom EVM networks
Units
The token uses 6 decimals (USDC/x402 convention): $1 = 1_000_000 base units.
Human-readable amount params (e.g. '100.5') are converted internally via
parseTokenAmount — pass token units, not base units.
Installation
npm install @bbuilders/djeon402-sdk-node
# or
pnpm add @bbuilders/djeon402-sdk-node
# or
yarn add @bbuilders/djeon402-sdk-nodeQuick Start
import { Djeon402NodeSDK } from '@bbuilders/djeon402-sdk-node';
// Configuration constants
const DJEON402_CONTRACT = '0x....';
const KYC_REGISTRY = '0x....';
const RPC_URL = 'http://localhost:8545'; // Anvil local node
const CHAIN_ID = 31337; // Foundry
// Test account addresses
const TEST_ACCOUNT = '0x.....';
const RECIPIENT_ADDRESS = '0x....';
// Initialize SDK
const sdk = new Djeon402NodeSDK({
rpcUrl: RPC_URL,
contractAddress: DJEON402_CONTRACT,
kycRegistryAddress: KYC_REGISTRY,
chainId: CHAIN_ID,
});
// Get token info
const info = await sdk.token.getInfo();
console.log(`Token: ${info.name} (${info.symbol})`);
console.log(`Total Supply: ${info.totalSupply}`);
// Check balance
const balance = await sdk.token.getBalance(TEST_ACCOUNT);
console.log(`Balance: ${balance.balance} DJEON`);
// Transfer tokens
const result = await sdk.token.transfer({
privateKey: process.env.PRIVATE_KEY!,
to: RECIPIENT_ADDRESS,
amount: '100.5',
});
console.log(`Transfer successful! Hash: ${result.hash}`);Configuration
Constructor Options
interface SDKConfig {
rpcUrl: string; // Blockchain RPC endpoint
contractAddress: Address; // DJEON402 token contract address
kycRegistryAddress?: Address; // Optional KYC registry address
chainId: number; // 31337 (Foundry) or 8453 (Base)
}Supported Networks
- Foundry (Anvil):
chainId: 31337 - Base:
chainId: 8453 - Other EVM chains: any other
chainIdgets a minimal chain definition built fromrpcUrl, so transactions are signed with the correct chain id
API Reference
Token Module (sdk.token)
getInfo()
Get token information.
const info = await sdk.token.getInfo();
// Returns: { name, symbol, decimals, totalSupply, paused, contractAddress }getBalance(address)
Get token balance for an address.
const balance = await sdk.token.getBalance('0x742d35...');
// Returns: { address, balance, balanceRaw }getAllowance(owner, spender)
Get allowance amount.
const allowance = await sdk.token.getAllowance(
'0xOwner...',
'0xSpender...'
);
// Returns: { owner, spender, allowance, allowanceRaw }transfer({ privateKey, to, amount })
Transfer tokens.
const result = await sdk.token.transfer({
privateKey: '0xYourPrivateKey',
to: '0xRecipient...',
amount: '100.5', // Human-readable amount
});
// Returns: { success, hash, blockNumber }approve({ privateKey, spender, amount })
Approve spending allowance.
const result = await sdk.token.approve({
privateKey: '0xYourPrivateKey',
spender: '0xSpender...',
amount: '1000',
});mint({ privateKey, to, amount }) (Admin only)
Mint new tokens.
const result = await sdk.token.mint({
privateKey: '0xAdminPrivateKey',
to: '0xRecipient...',
amount: '500',
});burn({ privateKey, amount }) (Admin only)
Burn tokens.
const result = await sdk.token.burn({
privateKey: '0xAdminPrivateKey',
amount: '100',
});Admin Module (sdk.admin)
getRoles()
Get all role identifiers.
const roles = await sdk.admin.getRoles();
// Returns: { DEFAULT_ADMIN_ROLE, MINTER_ROLE, BURNER_ROLE, PAUSER_ROLE, BLACKLISTER_ROLE }hasRole(role, account)
Check if account has a role.
const result = await sdk.admin.hasRole(
'0xRoleHash...',
'0xAccount...'
);
// Returns: { role, address, hasRole }isBlacklisted(address)
Check if address is blacklisted.
const result = await sdk.admin.isBlacklisted('0xAddress...');
// Returns: { address, isBlacklisted }blacklist({ privateKey, account })
Add address to blacklist.
const result = await sdk.admin.blacklist({
privateKey: '0xAdminPrivateKey',
account: '0xBadActor...',
});unBlacklist({ privateKey, account })
Remove address from blacklist.
const result = await sdk.admin.unBlacklist({
privateKey: '0xAdminPrivateKey',
account: '0xAccount...',
});pause(privateKey) / unpause(privateKey)
Pause/unpause the contract.
await sdk.admin.pause('0xAdminPrivateKey');
await sdk.admin.unpause('0xAdminPrivateKey');KYC Module (sdk.kyc)
getData(address)
Get KYC data for a user.
const kyc = await sdk.kyc.getData('0xUser...');
// Returns: { level, levelName, expiryDate, kycHash, isActive, dailyLimit, dailySpent, ... }verify({ adminPrivateKey, userAddress, level, expiryDate?, documents, kycHash? })
Verify user's KYC. When kycHash is omitted it is derived deterministically
(keccak256) from documents; pass your own IPFS hash for real deployments.
const result = await sdk.kyc.verify({
adminPrivateKey: '0xAdminPrivateKey',
userAddress: '0xUser...',
level: 2, // 0: None, 1: Tier1, 2: Tier2, 3: Tier3, 4: Tier4
expiryDate: Math.floor(Date.now() / 1000) + 365 * 24 * 60 * 60, // 1 year
documents: [
{ filename: 'passport.pdf', content: 'base64data...' },
{ filename: 'proof.pdf', content: 'base64data...' },
],
kycHash: 'QmYourIpfsHash...', // optional
});updateKYC({ adminPrivateKey, userAddress, level, expiryDate? }) / revokeKYC({ adminPrivateKey, userAddress })
Update or revoke an existing KYC record (admin only).
isKYCValid(address)
Check whether a user's KYC is active, non-None, and not expired.
const valid = await sdk.kyc.isKYCValid('0xUser...'); // booleancheckDailyLimit({ adminPrivateKey, userAddress, amountUSD })
Check whether an amount fits the user's daily limit and record the spend.
This contract function is state-mutating, so the SDK simulates first and
returns the real allow/deny boolean; the transaction is only sent when the
spend is allowed. The caller must be the KYC admin or an approved provider.
For a read-only check use getRemainingDailyLimit.
const allowed = await sdk.kyc.checkDailyLimit({
adminPrivateKey: '0xAdminPrivateKey',
userAddress: '0xUser...',
amountUSD: '250',
}); // booleangetRemainingDailyLimit(address)
Read-only remaining daily limit.
const remaining = await sdk.kyc.getRemainingDailyLimit('0xUser...');
// Returns: bigint in base units (type(uint256).max when unlimited)setDailyLimit({ adminPrivateKey, userAddress, limitUSD })
Set a custom per-user daily limit (admin only).
setLevelLimit({ adminPrivateKey, level, limitUSD })
Set default daily limit for a KYC level (admin only).
// Change Tier2 daily limit to $20,000
const result = await sdk.kyc.setLevelLimit({
adminPrivateKey: '0xAdminPrivateKey',
level: 2,
limitUSD: '20000',
});getLevelLimit(level)
Get default daily limit for a KYC level.
const limit = await sdk.kyc.getLevelLimit(2); // Tier2
// Returns: bigint (e.g. 10000000000n for $10,000 with 6 decimals)x402 Module (sdk.x402)
EIP-3009 gasless transactions - allows users to authorize transfers without paying gas.
generateNonce()
Generate a unique nonce for authorization.
const nonce = sdk.x402.generateNonce();
// Returns: '0x........'getDomainSeparator()
Get EIP-712 domain separator. Signing methods read this from the contract, so signatures stay valid for any deploy-time token name — never hardcode the domain name.
const separator = await sdk.x402.getDomainSeparator();getAuthorizationState(authorizer, nonce)
Check if a nonce has been used.
const isUsed = await sdk.x402.getAuthorizationState('0xAuthorizer...', '0xNonce...');
// Returns: booleansignTransfer({ privateKey, from, to, amount, ... })
Sign a transfer authorization (payer side).
const signature = await sdk.x402.signTransfer({
privateKey: '0xPayerPrivateKey',
from: '0xPayer...',
to: '0xRecipient...',
amount: '50',
validAfter: BigInt(Math.floor(Date.now() / 1000)),
validBefore: BigInt(Math.floor(Date.now() / 1000) + 3600),
nonce: sdk.x402.generateNonce(),
});
// Returns: { from, to, value, validAfter, validBefore, nonce, v, r, s }executeTransfer({ executorPrivateKey, authorization })
Execute a signed transfer authorization (executor/relayer side).
const result = await sdk.x402.executeTransfer({
executorPrivateKey: '0xExecutorPrivateKey',
authorization: signature,
});
// Returns: { success, hash, blockNumber }signReceive({ privateKey, from, to, amount, ... })
Sign a receive authorization (payee side). The sender signs to authorize the receiver to pull funds.
const signature = await sdk.x402.signReceive({
privateKey: '0xSenderPrivateKey',
from: '0xSender...',
to: '0xReceiver...',
amount: '50',
});
// Returns: { from, to, value, validAfter, validBefore, nonce, v, r, s }executeReceive({ receiverPrivateKey, authorization })
Execute a signed receive authorization. The caller must be the receiver (to address).
const result = await sdk.x402.executeReceive({
receiverPrivateKey: '0xReceiverPrivateKey',
authorization: signature,
});
// Returns: { success, hash, blockNumber }cancelAuthorization({ privateKey, authorizer, nonce })
Cancel an authorization by marking the nonce as used.
const result = await sdk.x402.cancelAuthorization({
privateKey: '0xAuthorizerPrivateKey',
authorizer: '0xAuthorizer...',
nonce: '0xNonce...',
});
// Returns: { success, hash, blockNumber }Complete x402 Payment Flow
// Step 1: User signs authorization (no gas needed)
const authorization = await sdk.x402.signTransfer({
privateKey: '0xUserPrivateKey',
from: '0xUser...',
to: '0xMerchant...',
amount: '99.99',
});
// Step 2: Relayer/backend executes the transfer (pays gas)
const result = await sdk.x402.executeTransfer({
executorPrivateKey: '0xRelayerPrivateKey',
authorization,
});
console.log(`Payment processed! Hash: ${result.hash}`);Receive Flow (Payee-Initiated)
// Step 1: Sender signs receive authorization
const authorization = await sdk.x402.signReceive({
privateKey: '0xSenderPrivateKey',
from: '0xSender...',
to: '0xReceiver...',
amount: '50',
});
// Step 2: Receiver executes (pulls funds)
const result = await sdk.x402.executeReceive({
receiverPrivateKey: '0xReceiverPrivateKey',
authorization,
});Direct Blockchain Access
Access the underlying viem public client for custom operations:
// Get current block number
const blockNumber = await sdk.publicClient.getBlockNumber();
// Get ETH balance
const ethBalance = await sdk.publicClient.getBalance({
address: '0x......',
});
// Read custom contract method
const result = await sdk.publicClient.readContract({
address: sdk.contractAddress,
abi: [...],
functionName: 'customMethod',
args: [...],
});Error Handling
All SDK methods may throw errors. Always wrap calls in try-catch blocks:
try {
const result = await sdk.token.transfer({
privateKey: '0x...',
to: '0x...',
amount: '100',
});
console.log('Success:', result.hash);
} catch (error) {
if (error instanceof Error) {
console.error('Transfer failed:', error.message);
}
}Examples
Backend API Integration
import { Djeon402NodeSDK } from '@bbuilders/djeon402-sdk-node';
import express from 'express';
// Configuration from environment variables
const RPC_URL = process.env.RPC_URL!;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS as `0x${string}`;
const CHAIN_ID = parseInt(process.env.CHAIN_ID || '31337');
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
const sdk = new Djeon402NodeSDK({
rpcUrl: RPC_URL,
contractAddress: CONTRACT_ADDRESS,
chainId: CHAIN_ID,
});
const app = express();
app.post('/api/transfer', async (req, res) => {
try {
const { to, amount } = req.body;
const result = await sdk.token.transfer({
privateKey: PRIVATE_KEY,
to,
amount,
});
res.json({ success: true, hash: result.hash });
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
app.get('/api/balance/:address', async (req, res) => {
try {
const balance = await sdk.token.getBalance(req.params.address as `0x${string}`);
res.json(balance);
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
app.listen(3000);Automated KYC Verification
async function verifyUserKYC(userAddress: string, documents: any[]) {
// Configuration constants
const RPC_URL = process.env.RPC_URL!;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS as `0x${string}`;
const CHAIN_ID = parseInt(process.env.CHAIN_ID || '31337');
const ADMIN_PRIVATE_KEY = process.env.ADMIN_KEY as `0x${string}`;
// KYC settings
const KYC_LEVEL = 2; // Tier2
const EXPIRY_DAYS = 365;
const EXPIRY_TIMESTAMP = Math.floor(Date.now() / 1000) + EXPIRY_DAYS * 24 * 60 * 60;
const sdk = new Djeon402NodeSDK({
rpcUrl: RPC_URL,
contractAddress: CONTRACT_ADDRESS,
chainId: CHAIN_ID,
});
// Verify user with Tier2 access
const result = await sdk.kyc.verify({
adminPrivateKey: ADMIN_PRIVATE_KEY,
userAddress: userAddress as `0x${string}`,
level: KYC_LEVEL,
expiryDate: EXPIRY_TIMESTAMP,
documents,
});
// Check KYC data
const kycData = await sdk.kyc.getData(userAddress as `0x${string}`);
console.log(`User verified: ${kycData.levelName}`);
console.log(`Daily limit: ${kycData.dailyLimit} USD`);
return result;
}Gasless Payment Relayer
import { Djeon402NodeSDK } from '@bbuilders/djeon402-sdk-node';
// Configuration constants
const RPC_URL = process.env.RPC_URL!;
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS as `0x${string}`;
const CHAIN_ID = parseInt(process.env.CHAIN_ID || '31337');
const RELAYER_PRIVATE_KEY = process.env.RELAYER_KEY as `0x${string}`;
const sdk = new Djeon402NodeSDK({
rpcUrl: RPC_URL,
contractAddress: CONTRACT_ADDRESS,
chainId: CHAIN_ID,
});
// User submits signed authorization to your API
app.post('/api/relay-payment', async (req, res) => {
const { authorization } = req.body;
try {
// Your backend pays the gas
const result = await sdk.x402.executeTransfer({
executorPrivateKey: RELAYER_PRIVATE_KEY,
authorization,
});
res.json({
success: true,
hash: result.hash,
message: 'Payment processed without user paying gas',
});
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});TypeScript Support
This SDK is written in TypeScript and provides full type definitions:
import type {
TokenInfo,
BalanceResult,
TransferResult,
KYCData,
TransferAuthorizationSignature,
ReceiveAuthorizationSignature,
CancelAuthorizationParams,
CancelAuthorizationResult,
} from '@bbuilders/djeon402-sdk-node';Security Notes
- Private Keys: Never expose private keys in client-side code. This SDK is designed for server-side use only.
- Environment Variables: Store private keys and sensitive data in environment variables.
- Input Validation: The SDK validates addresses, amounts, and private keys, but you should still validate user input.
- Rate Limiting: Implement rate limiting on your API endpoints to prevent abuse.
Related Packages
- @bbuilders/djeon402-contracts - Smart Contracts (Solidity / Foundry)
- @bbuilders/djeon402-core - Core types and utilities
- @bbuilders/djeon402-sdk-client - Browser/Frontend SDK
License
MIT
