npm.io
2.0.1 • Published yesterday

@bbuilders/djeon402-sdk-node

Licence
MIT
Version
2.0.1
Deps
1
Size
311 kB
Vulns
0
Weekly
0

@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.checkDailyLimit now 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.verify accepts an optional kycHash; when omitted it is derived deterministically from documents (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-node

Quick 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 chainId gets a minimal chain definition built from rpcUrl, 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...'); // boolean
checkDailyLimit({ 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',
}); // boolean
getRemainingDailyLimit(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: boolean
signTransfer({ 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.

License

MIT

Keywords