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
| Environment | REST API |
|---|---|
| Mainnet | https://api.ztdx.io |
| Testnet | https://api-sepolia.ztdx.io |
Product Lifecycle
Created ──▶ Subscribing ──▶ Active ──▶ Settled ──▶ Ended
│ │
└──▶ Cancelled ◀───────────┘
| Status | Description |
|---|---|
created | Product created, subscription not yet open |
subscribing | Subscription window open, users can subscribe |
active | Subscription closed, product running, funds locked |
settled | Matured and settled, users can claim principal + interest |
ended | All users have completed claiming |
cancelled | Emergency cancellation (principal refundable) |
Status transitions are triggered automatically by a background scheduler (polling every 60 seconds):
| Trigger Condition | Transition |
|---|---|
subscribe_start_time reached | created → subscribing |
subscribe_end_time reached | subscribing → active |
settle_time reached | active → settled |
| All users have claimed | settled → ended |
NFT Status
Each subscription is represented by a Soulbound NFT with the following lifecycle:
| Status | Description |
|---|---|
created | Record created, awaiting on-chain confirmation |
active | NFT minted, product running |
matured | Product settled, ready to claim |
redeemed | NFT 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.
| Context | Unit | Example |
|---|---|---|
| Product quota/limit fields | Wei (USDT × 10⁶) | "100000000" = 100 USDT |
Annual rate field (annual_rate) | Percentage string | "900.00%" |
| Rate bps fields | Basis points (1% = 100 bps) | 90000 = 900% |
Subscribe request amount | Human-readable USDT | "100" = 100 USDT |
Duration field duration_seconds | Seconds | 300 = 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:
| Field | Value |
|---|---|
name | ZTDX Earn |
version | 1 |
chainId | Network-dependent (e.g., 421614 for Arbitrum Sepolia) |
verifyingContract | Earn 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
| Method | Path | Description |
|---|---|---|
| GET | /earn/domain | Get EIP-712 domain info |
| GET | /earn/products | List products (with filtering) |
| GET | /earn/products/:id | Get product details |
| GET | /earn/performance | Historical performance data |
User Endpoints (JWT Required)
| Method | Path | Description |
|---|---|---|
| GET | /earn/subscriptions | My subscription records |
| POST | /earn/subscribe/prepare | Prepare 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)
| Network | Contract | Address |
|---|---|---|
| Arbitrum Sepolia | ZTDX Earn v3 | 0x436dcB8a2478D636a6cC678AE7A2E4c5449cB3ba |
| Arbitrum Sepolia | USDT | 0xb8486A01a7EaAB9e34553E7B323C9C1FAE1fA7B6 |
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 (
redeemedstatus)