Skip to main content

Advanced Earn Product Overview

ZTDX Earn is an on-chain fixed-income product built on smart contracts. Users subscribe USDT during the subscription window, lock funds until maturity, and then claim principal plus interest.

Key Features

  • Token: USDT (6 decimal precision)
  • Credential: ERC-1155 Soulbound NFT (non-transferable)
  • Interest Calculation: Interest = Principal × (Annual Rate / 365) × Lock Duration in Days

Base URL

EnvironmentREST API
Mainnethttps://api.ztdx.io
Testnethttps://api-sepolia.ztdx.io

Product Lifecycle

Created ──▶ Subscribing ──▶ Active ──▶ Settled ──▶ Ended
│ │
└──▶ Cancelled ◀───────────┘
StatusDescription
createdProduct created, subscription not yet open
subscribingSubscription window open, users can subscribe
activeSubscription closed, product running, funds locked
settledMatured and settled, users can claim principal + interest
endedAll users have completed claiming
cancelledEmergency cancellation (principal refundable)

Status transitions are triggered automatically by a background scheduler (polling every 60 seconds):

Trigger ConditionTransition
subscribe_start_time reachedcreatedsubscribing
subscribe_end_time reachedsubscribingactive
settle_time reachedactivesettled
All users have claimedsettledended

NFT Status

Each subscription is represented by a Soulbound NFT with the following lifecycle:

StatusDescription
createdRecord created, awaiting on-chain confirmation
activeNFT minted, product running
maturedProduct settled, ready to claim
redeemedNFT burned, principal + interest claimed

Amount Precision

Important

Product fields (total_quota, min_amount, etc.) use Wei format (USDT × 10⁶), while subscription request amount uses human-readable USDT.

ContextUnitExample
Product quota/limit fieldsWei (USDT × 10⁶)"100000000" = 100 USDT
Annual rate field (annual_rate)Percentage string"900.00%"
Rate bps fieldsBasis points (1% = 100 bps)90000 = 900%
Subscribe request amountHuman-readable USDT"100" = 100 USDT
Duration field duration_secondsSeconds300 = 5 minutes

Conversion Examples

// Wei → USDT display
const usdtAmount = (weiAmount / 1_000_000).toFixed(2);
// "100000000" → "100.00"
// "500000000" → "500.00"

// bps → Percentage display
const annualRatePercent = (annual_rate_bps / 100).toFixed(2) + '%';
// 90000 → "900.00%"
// 5000 → "50.00%"

EIP-712 Signature

Subscription requires an EIP-712 signature from the backend, used for on-chain verification.

Domain Separator:

FieldValue
nameZTDX Earn
version1
chainIdNetwork-dependent (e.g., 421614 for Arbitrum Sepolia)
verifyingContractEarn contract address

Subscribe Struct:

Subscribe(address user, uint256 productId, uint256 amount, uint256 deadline)
  • Signature validity: 30 minutes
  • Each signature can only be used once (anti-replay)

Public Endpoints

MethodPathDescription
GET/earn/domainGet EIP-712 domain info
GET/earn/productsList products (with filtering)
GET/earn/products/:idGet product details
GET/earn/performanceHistorical performance data

User Endpoints (JWT Required)

MethodPathDescription
GET/earn/subscriptionsMy subscription records
POST/earn/subscribe/preparePrepare subscription (get signature)

Smart Contract ABI

// Subscribe (requires EIP-712 signature)
function subscribe(
uint256 productId,
uint256 amount, // Wei format
uint256 deadline,
bytes calldata signature
) external;

// Claim principal + interest (after settlement)
function claim(uint256 productId) external;

// Emergency refund (when product is cancelled)
function emergencyClaim(uint256 productId) external;

// Query user subscription
function getSubscription(uint256 productId, address user)
external view
returns (
uint256 amount,
uint256 expectedReturn,
uint256 actualReturn,
uint256 subscribedAt,
bool claimed
);

// Query product config
function getProductConfig(uint256 productId)
external view
returns (
string memory name,
uint256 annualRateBps,
uint256 durationDays,
uint256 totalQuota,
uint256 minAmount,
uint256 maxAmountPerUser
);

// Query product timing
function getProductTiming(uint256 productId)
external view
returns (
uint256 subscribeStartTime,
uint256 subscribeEndTime,
uint256 settleTime,
uint256 actualSettleTime
);

Contract Addresses (Testnet)

NetworkContractAddress
Arbitrum SepoliaZTDX Earn v30x436dcB8a2478D636a6cC678AE7A2E4c5449cB3ba
Arbitrum SepoliaUSDT0xb8486A01a7EaAB9e34553E7B323C9C1FAE1fA7B6

Notes

  • On-chain event listener polls every ~12 seconds; subscription records may have a 12–30 second delay
  • Earn NFTs are ERC-1155 Soulbound — non-transferable and non-delegable
  • NFTs are automatically burned upon claiming (redeemed status)