Checkpoint: v13: 自研跨链桥完成 - 完全移除Li.Fi第三方协议,实现NAC原生跨链购买系统。支持BSC/ETH/Polygon/Arbitrum/Avalanche五条链USDT购买XIC,更新官方收款地址,后端多链监听器,My Transactions查询功能全部测试通过。
This commit is contained in:
parent
1576303898
commit
0659cd71cb
|
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"query": "ALTER TABLE bridge_intents MODIFY COLUMN senderAddress VARCHAR(64) NULL;\nDESCRIBE bridge_intents;",
|
||||||
|
"command": "mysql --batch --raw --column-names --default-character-set=utf8mb4 --host gateway03.us-east-1.prod.aws.tidbcloud.com --port 4000 --user 3Bq4cgN2KNKQqNu.8160cd2033e0 --database Ngki3MumDNGduV3xJt3mga --execute ALTER TABLE bridge_intents MODIFY COLUMN senderAddress VARCHAR(64) NULL;\nDESCRIBE bridge_intents;",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"Field": "id",
|
||||||
|
"Type": "int",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "PRI",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": "auto_increment"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "fromChainId",
|
||||||
|
"Type": "int",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "senderAddress",
|
||||||
|
"Type": "varchar(64)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "xicReceiveAddress",
|
||||||
|
"Type": "varchar(64)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "expectedUsdt",
|
||||||
|
"Type": "decimal(20,6)",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "matched",
|
||||||
|
"Type": "tinyint(1)",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "0",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "matchedOrderId",
|
||||||
|
"Type": "int",
|
||||||
|
"Null": "YES",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "NULL",
|
||||||
|
"Extra": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Field": "createdAt",
|
||||||
|
"Type": "timestamp",
|
||||||
|
"Null": "NO",
|
||||||
|
"Key": "",
|
||||||
|
"Default": "CURRENT_TIMESTAMP",
|
||||||
|
"Extra": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"messages": [],
|
||||||
|
"stdout": "Field\tType\tNull\tKey\tDefault\tExtra\nid\tint\tNO\tPRI\tNULL\tauto_increment\nfromChainId\tint\tNO\t\tNULL\t\nsenderAddress\tvarchar(64)\tYES\t\tNULL\t\nxicReceiveAddress\tvarchar(64)\tNO\t\tNULL\t\nexpectedUsdt\tdecimal(20,6)\tYES\t\tNULL\t\nmatched\ttinyint(1)\tNO\t\t0\t\nmatchedOrderId\tint\tYES\t\tNULL\t\ncreatedAt\ttimestamp\tNO\t\tCURRENT_TIMESTAMP\t\n",
|
||||||
|
"stderr": "",
|
||||||
|
"execution_time_ms": 1992
|
||||||
|
}
|
||||||
|
|
@ -24,7 +24,7 @@ export const CONTRACTS = {
|
||||||
rpcUrl: "https://eth.llamarpc.com",
|
rpcUrl: "https://eth.llamarpc.com",
|
||||||
explorerUrl: "https://etherscan.io",
|
explorerUrl: "https://etherscan.io",
|
||||||
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
|
||||||
presale: "0x85AB2F2d9f7ca7ecB272b5E8726c70f3fd45D1E3",
|
presale: "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
token: "", // XIC not yet on ETH
|
token: "", // XIC not yet on ETH
|
||||||
usdt: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
usdt: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
||||||
},
|
},
|
||||||
|
|
@ -37,7 +37,7 @@ export const CONTRACTS = {
|
||||||
token: "",
|
token: "",
|
||||||
usdt: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
|
usdt: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
|
||||||
// Receiving wallet for TRC20 USDT
|
// Receiving wallet for TRC20 USDT
|
||||||
receivingWallet: "TYASr5UV6HEcXatwdFyffSGZszd6Gkjkvb",
|
receivingWallet: "TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp",
|
||||||
},
|
},
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,95 @@
|
||||||
|
# v13 部署日志
|
||||||
|
|
||||||
|
**部署时间:** 2026-03-10
|
||||||
|
**部署人员:** Manus AI
|
||||||
|
**部署服务器:** AI服务器 43.224.155.27:22000
|
||||||
|
**部署目录:** /www/wwwroot/nac-presale-test/
|
||||||
|
**PM2进程:** nac-presale-test (id=8)
|
||||||
|
**服务端口:** 3100
|
||||||
|
**域名:** https://pre-sale.newassetchain.io
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 本次更新内容
|
||||||
|
|
||||||
|
### 1. 自研跨链桥(完全移除Li.Fi第三方协议)
|
||||||
|
|
||||||
|
**背景:** 之前Bridge页面使用Li.Fi协议,由于XIC代币尚未在任何DEX上市,Li.Fi无法获取报价,Bridge功能完全无法使用。
|
||||||
|
|
||||||
|
**解决方案:** 完全移除Li.Fi,改为NAC自研跨链桥:
|
||||||
|
- 用户选择源链(BSC/ETH/Polygon/Arbitrum/Avalanche)
|
||||||
|
- 系统显示对应链的官方USDT收款地址
|
||||||
|
- 用户发送USDT后点击"I Have Sent USDT"注册意向
|
||||||
|
- 后端监听器自动监控各链的USDT转入
|
||||||
|
- 确认收款后按$0.02/XIC预售价分发XIC代币
|
||||||
|
|
||||||
|
### 2. 官方收款地址更新
|
||||||
|
|
||||||
|
| 网络 | 地址 |
|
||||||
|
|------|------|
|
||||||
|
| TRC20/USDT (TRON) | TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp |
|
||||||
|
| ERC20/USDT (Ethereum) | 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3 |
|
||||||
|
| BEP20/USDT (BSC) | 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3 |
|
||||||
|
| Polygon USDT | 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3 |
|
||||||
|
| Arbitrum USDT | 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3 |
|
||||||
|
| Avalanche USDT | 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3 |
|
||||||
|
|
||||||
|
### 3. 新增文件
|
||||||
|
|
||||||
|
- `server/bridgeMonitor.ts` - 多链USDT转入监听器(BSC/ETH/Polygon/Arbitrum/Avalanche)
|
||||||
|
- `server/_core/index.ts` - 添加Bridge监听器启动入口
|
||||||
|
|
||||||
|
### 4. 数据库变更
|
||||||
|
|
||||||
|
新增表:
|
||||||
|
- `bridge_intents` - 用户购买意向记录(注册后等待确认)
|
||||||
|
- `bridge_orders` - 已确认的跨链购买订单
|
||||||
|
|
||||||
|
修改表:
|
||||||
|
- `bridge_orders.status` - 添加 `distributed` 枚举值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试结果
|
||||||
|
|
||||||
|
| 功能 | 状态 | 备注 |
|
||||||
|
|------|------|------|
|
||||||
|
| Bridge页面加载 | ✅ 正常 | |
|
||||||
|
| 5条链选择(BSC/ETH/POLY/ARB/AVAX) | ✅ 正常 | |
|
||||||
|
| Gas费说明随链切换 | ✅ 正常 | |
|
||||||
|
| 收款地址显示 | ✅ 正常 | 官方地址 |
|
||||||
|
| USDT金额输入 | ✅ 正常 | |
|
||||||
|
| XIC计算($0.02/XIC) | ✅ 正常 | 100 USDT = 5,000 XIC |
|
||||||
|
| 复制收款地址 | ✅ 正常 | |
|
||||||
|
| "I Have Sent USDT"提交 | ✅ 正常 | 注册意向到数据库 |
|
||||||
|
| 成功提示显示 | ✅ 正常 | 绿色成功框 |
|
||||||
|
| My Transactions查询 | ✅ 正常 | 显示待处理订单 |
|
||||||
|
| 主页TRC20收款地址 | ✅ 正常 | TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp |
|
||||||
|
| 主页链上数据 | ✅ 正常 | $9,900 Raised, 495K Tokens Sold |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后台管理员信息
|
||||||
|
|
||||||
|
- **后台管理员账号:** 通过Manus OAuth登录
|
||||||
|
- **数据库用户:** nac_presale / NACpresale2026!
|
||||||
|
- **数据库名:** nac_presale
|
||||||
|
- **MySQL连接:** 127.0.0.1:3306(仅本地)
|
||||||
|
- **宝塔面板:** http://43.224.155.27:12/btwest(cproot/vajngkvf)
|
||||||
|
- **PM2进程名:** nac-presale-test(id=8)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 待优化事项
|
||||||
|
|
||||||
|
1. Polygon/Arbitrum/Avalanche 链的 USDT 合约地址需要配置(目前使用通用ERC20地址)
|
||||||
|
2. 钱包连接功能(MetaMask)需要在有MetaMask扩展的浏览器中验证
|
||||||
|
3. Bridge监听器的RPC节点可考虑使用付费节点提高稳定性
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git同步
|
||||||
|
|
||||||
|
- **Gitea仓库:** https://git.newassetchain.io/nacadmin/xic-presale
|
||||||
|
- **分支:** main
|
||||||
|
- **提交信息:** v13: 自研跨链桥,移除Li.Fi,更新官方收款地址
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
CREATE TABLE `bridge_intents` (
|
||||||
|
`id` int AUTO_INCREMENT NOT NULL,
|
||||||
|
`fromChainId` int NOT NULL,
|
||||||
|
`senderAddress` varchar(64) NOT NULL,
|
||||||
|
`xicReceiveAddress` varchar(64) NOT NULL,
|
||||||
|
`expectedUsdt` decimal(20,6),
|
||||||
|
`matched` boolean NOT NULL DEFAULT false,
|
||||||
|
`matchedOrderId` int,
|
||||||
|
`createdAt` timestamp NOT NULL DEFAULT (now()),
|
||||||
|
CONSTRAINT `bridge_intents_id` PRIMARY KEY(`id`)
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` MODIFY COLUMN `toChainId` int NOT NULL DEFAULT 56;--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` MODIFY COLUMN `toToken` varchar(32) NOT NULL DEFAULT 'XIC';--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` MODIFY COLUMN `status` enum('pending','confirmed','distributed','failed') NOT NULL DEFAULT 'pending';--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `xicReceiveAddress` varchar(64);--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `confirmedAt` timestamp;--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `distributedAt` timestamp;--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `distributeTxHash` varchar(128);--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `blockNumber` bigint;--> statement-breakpoint
|
||||||
|
ALTER TABLE `bridge_orders` ADD `updatedAt` timestamp DEFAULT (now()) NOT NULL ON UPDATE CURRENT_TIMESTAMP;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE `bridge_intents` MODIFY COLUMN `senderAddress` varchar(64);
|
||||||
|
|
@ -0,0 +1,653 @@
|
||||||
|
{
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "mysql",
|
||||||
|
"id": "b1822efc-d652-4957-970f-f71b733359b6",
|
||||||
|
"prevId": "f2da11d5-2ee3-40ce-9180-11a9480a5b91",
|
||||||
|
"tables": {
|
||||||
|
"bridge_intents": {
|
||||||
|
"name": "bridge_intents",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"fromChainId": {
|
||||||
|
"name": "fromChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"senderAddress": {
|
||||||
|
"name": "senderAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicReceiveAddress": {
|
||||||
|
"name": "xicReceiveAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"expectedUsdt": {
|
||||||
|
"name": "expectedUsdt",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"matched": {
|
||||||
|
"name": "matched",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"matchedOrderId": {
|
||||||
|
"name": "matchedOrderId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"bridge_intents_id": {
|
||||||
|
"name": "bridge_intents_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"bridge_orders": {
|
||||||
|
"name": "bridge_orders",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"txHash": {
|
||||||
|
"name": "txHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"walletAddress": {
|
||||||
|
"name": "walletAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromChainId": {
|
||||||
|
"name": "fromChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromToken": {
|
||||||
|
"name": "fromToken",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromAmount": {
|
||||||
|
"name": "fromAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"toChainId": {
|
||||||
|
"name": "toChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 56
|
||||||
|
},
|
||||||
|
"toToken": {
|
||||||
|
"name": "toToken",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'XIC'"
|
||||||
|
},
|
||||||
|
"toAmount": {
|
||||||
|
"name": "toAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicReceiveAddress": {
|
||||||
|
"name": "xicReceiveAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "enum('pending','confirmed','distributed','failed')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"confirmedAt": {
|
||||||
|
"name": "confirmedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributedAt": {
|
||||||
|
"name": "distributedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributeTxHash": {
|
||||||
|
"name": "distributeTxHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"blockNumber": {
|
||||||
|
"name": "blockNumber",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"bridge_orders_id": {
|
||||||
|
"name": "bridge_orders_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"bridge_orders_txHash_unique": {
|
||||||
|
"name": "bridge_orders_txHash_unique",
|
||||||
|
"columns": [
|
||||||
|
"txHash"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"presale_config": {
|
||||||
|
"name": "presale_config",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"name": "label",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(256)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'text'"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"presale_config_id": {
|
||||||
|
"name": "presale_config_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"presale_config_key_unique": {
|
||||||
|
"name": "presale_config_key_unique",
|
||||||
|
"columns": [
|
||||||
|
"key"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"presale_stats_cache": {
|
||||||
|
"name": "presale_stats_cache",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"chain": {
|
||||||
|
"name": "chain",
|
||||||
|
"type": "varchar(16)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"usdtRaised": {
|
||||||
|
"name": "usdtRaised",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"tokensSold": {
|
||||||
|
"name": "tokensSold",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"weiRaised": {
|
||||||
|
"name": "weiRaised",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"lastUpdated": {
|
||||||
|
"name": "lastUpdated",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"presale_stats_cache_id": {
|
||||||
|
"name": "presale_stats_cache_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"trc20_intents": {
|
||||||
|
"name": "trc20_intents",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"tronAddress": {
|
||||||
|
"name": "tronAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"evmAddress": {
|
||||||
|
"name": "evmAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"expectedUsdt": {
|
||||||
|
"name": "expectedUsdt",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"matched": {
|
||||||
|
"name": "matched",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"matchedPurchaseId": {
|
||||||
|
"name": "matchedPurchaseId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"trc20_intents_id": {
|
||||||
|
"name": "trc20_intents_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"trc20_purchases": {
|
||||||
|
"name": "trc20_purchases",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"txHash": {
|
||||||
|
"name": "txHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromAddress": {
|
||||||
|
"name": "fromAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"usdtAmount": {
|
||||||
|
"name": "usdtAmount",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicAmount": {
|
||||||
|
"name": "xicAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"blockNumber": {
|
||||||
|
"name": "blockNumber",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "enum('pending','confirmed','distributed','failed')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"distributedAt": {
|
||||||
|
"name": "distributedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributeTxHash": {
|
||||||
|
"name": "distributeTxHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"evmAddress": {
|
||||||
|
"name": "evmAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"trc20_purchases_id": {
|
||||||
|
"name": "trc20_purchases_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"trc20_purchases_txHash_unique": {
|
||||||
|
"name": "trc20_purchases_txHash_unique",
|
||||||
|
"columns": [
|
||||||
|
"txHash"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"openId": {
|
||||||
|
"name": "openId",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "varchar(320)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"loginMethod": {
|
||||||
|
"name": "loginMethod",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "enum('user','admin')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'user'"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"lastSignedIn": {
|
||||||
|
"name": "lastSignedIn",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"users_id": {
|
||||||
|
"name": "users_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_openId_unique": {
|
||||||
|
"name": "users_openId_unique",
|
||||||
|
"columns": [
|
||||||
|
"openId"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"tables": {},
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,653 @@
|
||||||
|
{
|
||||||
|
"version": "5",
|
||||||
|
"dialect": "mysql",
|
||||||
|
"id": "7e9d948e-d569-4194-bb75-6219b837045e",
|
||||||
|
"prevId": "b1822efc-d652-4957-970f-f71b733359b6",
|
||||||
|
"tables": {
|
||||||
|
"bridge_intents": {
|
||||||
|
"name": "bridge_intents",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"fromChainId": {
|
||||||
|
"name": "fromChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"senderAddress": {
|
||||||
|
"name": "senderAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicReceiveAddress": {
|
||||||
|
"name": "xicReceiveAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"expectedUsdt": {
|
||||||
|
"name": "expectedUsdt",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"matched": {
|
||||||
|
"name": "matched",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"matchedOrderId": {
|
||||||
|
"name": "matchedOrderId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"bridge_intents_id": {
|
||||||
|
"name": "bridge_intents_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"bridge_orders": {
|
||||||
|
"name": "bridge_orders",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"txHash": {
|
||||||
|
"name": "txHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"walletAddress": {
|
||||||
|
"name": "walletAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromChainId": {
|
||||||
|
"name": "fromChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromToken": {
|
||||||
|
"name": "fromToken",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromAmount": {
|
||||||
|
"name": "fromAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"toChainId": {
|
||||||
|
"name": "toChainId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": 56
|
||||||
|
},
|
||||||
|
"toToken": {
|
||||||
|
"name": "toToken",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'XIC'"
|
||||||
|
},
|
||||||
|
"toAmount": {
|
||||||
|
"name": "toAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicReceiveAddress": {
|
||||||
|
"name": "xicReceiveAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "enum('pending','confirmed','distributed','failed')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"confirmedAt": {
|
||||||
|
"name": "confirmedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributedAt": {
|
||||||
|
"name": "distributedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributeTxHash": {
|
||||||
|
"name": "distributeTxHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"blockNumber": {
|
||||||
|
"name": "blockNumber",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"bridge_orders_id": {
|
||||||
|
"name": "bridge_orders_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"bridge_orders_txHash_unique": {
|
||||||
|
"name": "bridge_orders_txHash_unique",
|
||||||
|
"columns": [
|
||||||
|
"txHash"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"presale_config": {
|
||||||
|
"name": "presale_config",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"name": "key",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"value": {
|
||||||
|
"name": "value",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"label": {
|
||||||
|
"name": "label",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(256)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"name": "type",
|
||||||
|
"type": "varchar(32)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'text'"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"presale_config_id": {
|
||||||
|
"name": "presale_config_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"presale_config_key_unique": {
|
||||||
|
"name": "presale_config_key_unique",
|
||||||
|
"columns": [
|
||||||
|
"key"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"presale_stats_cache": {
|
||||||
|
"name": "presale_stats_cache",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"chain": {
|
||||||
|
"name": "chain",
|
||||||
|
"type": "varchar(16)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"usdtRaised": {
|
||||||
|
"name": "usdtRaised",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"tokensSold": {
|
||||||
|
"name": "tokensSold",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"weiRaised": {
|
||||||
|
"name": "weiRaised",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'0'"
|
||||||
|
},
|
||||||
|
"lastUpdated": {
|
||||||
|
"name": "lastUpdated",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"presale_stats_cache_id": {
|
||||||
|
"name": "presale_stats_cache_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"trc20_intents": {
|
||||||
|
"name": "trc20_intents",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"tronAddress": {
|
||||||
|
"name": "tronAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"evmAddress": {
|
||||||
|
"name": "evmAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"expectedUsdt": {
|
||||||
|
"name": "expectedUsdt",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"matched": {
|
||||||
|
"name": "matched",
|
||||||
|
"type": "boolean",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
|
"matchedPurchaseId": {
|
||||||
|
"name": "matchedPurchaseId",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"trc20_intents_id": {
|
||||||
|
"name": "trc20_intents_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"trc20_purchases": {
|
||||||
|
"name": "trc20_purchases",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"txHash": {
|
||||||
|
"name": "txHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"fromAddress": {
|
||||||
|
"name": "fromAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"usdtAmount": {
|
||||||
|
"name": "usdtAmount",
|
||||||
|
"type": "decimal(20,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"xicAmount": {
|
||||||
|
"name": "xicAmount",
|
||||||
|
"type": "decimal(30,6)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"blockNumber": {
|
||||||
|
"name": "blockNumber",
|
||||||
|
"type": "bigint",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "enum('pending','confirmed','distributed','failed')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'pending'"
|
||||||
|
},
|
||||||
|
"distributedAt": {
|
||||||
|
"name": "distributedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"distributeTxHash": {
|
||||||
|
"name": "distributeTxHash",
|
||||||
|
"type": "varchar(128)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"evmAddress": {
|
||||||
|
"name": "evmAddress",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"trc20_purchases_id": {
|
||||||
|
"name": "trc20_purchases_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"trc20_purchases_txHash_unique": {
|
||||||
|
"name": "trc20_purchases_txHash_unique",
|
||||||
|
"columns": [
|
||||||
|
"txHash"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
},
|
||||||
|
"users": {
|
||||||
|
"name": "users",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "int",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": true
|
||||||
|
},
|
||||||
|
"openId": {
|
||||||
|
"name": "openId",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "text",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "varchar(320)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"loginMethod": {
|
||||||
|
"name": "loginMethod",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"autoincrement": false
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"name": "role",
|
||||||
|
"type": "enum('user','admin')",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "'user'"
|
||||||
|
},
|
||||||
|
"createdAt": {
|
||||||
|
"name": "createdAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"name": "updatedAt",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"onUpdate": true,
|
||||||
|
"default": "(now())"
|
||||||
|
},
|
||||||
|
"lastSignedIn": {
|
||||||
|
"name": "lastSignedIn",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"autoincrement": false,
|
||||||
|
"default": "(now())"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"users_id": {
|
||||||
|
"name": "users_id",
|
||||||
|
"columns": [
|
||||||
|
"id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_openId_unique": {
|
||||||
|
"name": "users_openId_unique",
|
||||||
|
"columns": [
|
||||||
|
"openId"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"checkConstraint": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {},
|
||||||
|
"columns": {}
|
||||||
|
},
|
||||||
|
"internal": {
|
||||||
|
"tables": {},
|
||||||
|
"indexes": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,6 +43,20 @@
|
||||||
"when": 1773124399358,
|
"when": 1773124399358,
|
||||||
"tag": "0005_certain_betty_ross",
|
"tag": "0005_certain_betty_ross",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 6,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1773135614044,
|
||||||
|
"tag": "0006_colossal_unicorn",
|
||||||
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 7,
|
||||||
|
"version": "5",
|
||||||
|
"when": 1773136228889,
|
||||||
|
"tag": "0007_wide_menace",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -97,20 +97,44 @@ export const presaleConfig = mysqlTable("presale_config", {
|
||||||
|
|
||||||
export type PresaleConfig = typeof presaleConfig.$inferSelect;
|
export type PresaleConfig = typeof presaleConfig.$inferSelect;
|
||||||
export type InsertPresaleConfig = typeof presaleConfig.$inferInsert;
|
export type InsertPresaleConfig = typeof presaleConfig.$inferInsert;
|
||||||
// Cross-chain bridge orders — recorded when user completes a Li.Fi cross-chain purchase
|
// Cross-chain bridge orders — NAC self-developed cross-chain bridge
|
||||||
|
// User sends USDT on any supported chain to our receiving address
|
||||||
|
// Backend monitors and records confirmed transfers, then distributes XIC
|
||||||
export const bridgeOrders = mysqlTable("bridge_orders", {
|
export const bridgeOrders = mysqlTable("bridge_orders", {
|
||||||
id: int("id").autoincrement().primaryKey(),
|
id: int("id").autoincrement().primaryKey(),
|
||||||
txHash: varchar("txHash", { length: 128 }).notNull().unique(),
|
txHash: varchar("txHash", { length: 128 }).notNull().unique(),
|
||||||
walletAddress: varchar("walletAddress", { length: 64 }).notNull(),
|
walletAddress: varchar("walletAddress", { length: 64 }).notNull(), // sender wallet on source chain
|
||||||
fromChainId: int("fromChainId").notNull(),
|
fromChainId: int("fromChainId").notNull(),
|
||||||
fromToken: varchar("fromToken", { length: 32 }).notNull(),
|
fromToken: varchar("fromToken", { length: 32 }).notNull(),
|
||||||
fromAmount: decimal("fromAmount", { precision: 30, scale: 6 }).notNull(),
|
fromAmount: decimal("fromAmount", { precision: 30, scale: 6 }).notNull(), // USDT amount sent
|
||||||
toChainId: int("toChainId").notNull(),
|
toChainId: int("toChainId").notNull().default(56), // always BSC for XIC
|
||||||
toToken: varchar("toToken", { length: 32 }).notNull(),
|
toToken: varchar("toToken", { length: 32 }).notNull().default("XIC"),
|
||||||
toAmount: decimal("toAmount", { precision: 30, scale: 6 }).notNull(),
|
toAmount: decimal("toAmount", { precision: 30, scale: 6 }).notNull(), // XIC amount to distribute
|
||||||
status: mysqlEnum("status", ["pending", "completed", "failed"]).default("completed").notNull(),
|
xicReceiveAddress: varchar("xicReceiveAddress", { length: 64 }), // BSC address to receive XIC
|
||||||
|
status: mysqlEnum("status", ["pending", "confirmed", "distributed", "failed"]).default("pending").notNull(),
|
||||||
|
confirmedAt: timestamp("confirmedAt"),
|
||||||
|
distributedAt: timestamp("distributedAt"),
|
||||||
|
distributeTxHash: varchar("distributeTxHash", { length: 128 }),
|
||||||
|
blockNumber: bigint("blockNumber", { mode: "number" }),
|
||||||
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||||
|
updatedAt: timestamp("updatedAt").defaultNow().onUpdateNow().notNull(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type BridgeOrder = typeof bridgeOrders.$inferSelect;
|
export type BridgeOrder = typeof bridgeOrders.$inferSelect;
|
||||||
export type InsertBridgeOrder = typeof bridgeOrders.$inferInsert;
|
export type InsertBridgeOrder = typeof bridgeOrders.$inferInsert;
|
||||||
|
|
||||||
|
// Bridge deposit intents — user pre-registers before sending USDT
|
||||||
|
// Helps backend match incoming transfers to the correct XIC receive address
|
||||||
|
export const bridgeIntents = mysqlTable("bridge_intents", {
|
||||||
|
id: int("id").autoincrement().primaryKey(),
|
||||||
|
fromChainId: int("fromChainId").notNull(),
|
||||||
|
senderAddress: varchar("senderAddress", { length: 64 }), // sender on source chain (optional, filled when wallet connected)
|
||||||
|
xicReceiveAddress: varchar("xicReceiveAddress", { length: 64 }).notNull(), // BSC address to receive XIC
|
||||||
|
expectedUsdt: decimal("expectedUsdt", { precision: 20, scale: 6 }), // expected USDT amount
|
||||||
|
matched: boolean("matched").default(false).notNull(),
|
||||||
|
matchedOrderId: int("matchedOrderId"),
|
||||||
|
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type BridgeIntent = typeof bridgeIntents.$inferSelect;
|
||||||
|
export type InsertBridgeIntent = typeof bridgeIntents.$inferInsert;
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { appRouter } from "../routers";
|
||||||
import { createContext } from "./context";
|
import { createContext } from "./context";
|
||||||
import { serveStatic, setupVite } from "./vite";
|
import { serveStatic, setupVite } from "./vite";
|
||||||
import { startTRC20Monitor } from "../trc20Monitor";
|
import { startTRC20Monitor } from "../trc20Monitor";
|
||||||
|
import { startBridgeMonitor } from "../bridgeMonitor";
|
||||||
|
|
||||||
function isPortAvailable(port: number): Promise<boolean> {
|
function isPortAvailable(port: number): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
|
|
@ -64,6 +65,9 @@ async function startServer() {
|
||||||
|
|
||||||
// Start TRC20 monitor in background
|
// Start TRC20 monitor in background
|
||||||
startTRC20Monitor().catch(e => console.error("[TRC20Monitor] Start error:", e));
|
startTRC20Monitor().catch(e => console.error("[TRC20Monitor] Start error:", e));
|
||||||
|
|
||||||
|
// Start Bridge monitor (multi-chain USDT deposit listener)
|
||||||
|
startBridgeMonitor();
|
||||||
}
|
}
|
||||||
|
|
||||||
startServer().catch(console.error);
|
startServer().catch(console.error);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,285 @@
|
||||||
|
/**
|
||||||
|
* NAC Cross-Chain Bridge Monitor
|
||||||
|
* Self-developed bridge: monitors USDT transfers on BSC/ETH/Polygon/Arbitrum/Avalanche
|
||||||
|
* When user sends USDT to our receiving address, we record the order and distribute XIC
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getDb } from "./db";
|
||||||
|
import { bridgeOrders, bridgeIntents } from "../drizzle/schema";
|
||||||
|
import { eq, and, desc } from "drizzle-orm";
|
||||||
|
|
||||||
|
// ─── Presale Config ───────────────────────────────────────────────────────────
|
||||||
|
export const XIC_PRICE_USDT = 0.02; // $0.02 per XIC
|
||||||
|
|
||||||
|
// ─── Chain Configs ────────────────────────────────────────────────────────────
|
||||||
|
export interface ChainConfig {
|
||||||
|
chainId: number;
|
||||||
|
name: string;
|
||||||
|
symbol: string; // native token symbol (BNB, ETH, MATIC, AVAX)
|
||||||
|
icon: string;
|
||||||
|
color: string;
|
||||||
|
usdtAddress: string; // USDT contract on this chain
|
||||||
|
receivingAddress: string; // Our USDT receiving address on this chain
|
||||||
|
rpcUrl: string;
|
||||||
|
explorerUrl: string;
|
||||||
|
explorerTxPath: string; // e.g. /tx/
|
||||||
|
decimals: number; // USDT decimals (6 for most, 18 for BSC)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BRIDGE_CHAINS: ChainConfig[] = [
|
||||||
|
{
|
||||||
|
chainId: 56,
|
||||||
|
name: "BSC",
|
||||||
|
symbol: "BNB",
|
||||||
|
icon: "🟡",
|
||||||
|
color: "#F0B90B",
|
||||||
|
usdtAddress: "0x55d398326f99059fF775485246999027B3197955",
|
||||||
|
receivingAddress: process.env.BRIDGE_BSC_ADDRESS || "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
|
rpcUrl: "https://bsc-dataseed1.binance.org/",
|
||||||
|
explorerUrl: "https://bscscan.com",
|
||||||
|
explorerTxPath: "/tx/",
|
||||||
|
decimals: 18, // BSC USDT is 18 decimals
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 1,
|
||||||
|
name: "Ethereum",
|
||||||
|
symbol: "ETH",
|
||||||
|
icon: "🔵",
|
||||||
|
color: "#627EEA",
|
||||||
|
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
||||||
|
receivingAddress: process.env.BRIDGE_ETH_ADDRESS || "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
|
rpcUrl: "https://ethereum.publicnode.com",
|
||||||
|
explorerUrl: "https://etherscan.io",
|
||||||
|
explorerTxPath: "/tx/",
|
||||||
|
decimals: 6, // ETH USDT is 6 decimals
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 137,
|
||||||
|
name: "Polygon",
|
||||||
|
symbol: "MATIC",
|
||||||
|
icon: "🟣",
|
||||||
|
color: "#8247E5",
|
||||||
|
usdtAddress: "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
|
||||||
|
receivingAddress: process.env.BRIDGE_POLYGON_ADDRESS || "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
|
rpcUrl: "https://polygon-rpc.com/",
|
||||||
|
explorerUrl: "https://polygonscan.com",
|
||||||
|
explorerTxPath: "/tx/",
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 42161,
|
||||||
|
name: "Arbitrum",
|
||||||
|
symbol: "ETH",
|
||||||
|
icon: "🔷",
|
||||||
|
color: "#28A0F0",
|
||||||
|
usdtAddress: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
||||||
|
receivingAddress: process.env.BRIDGE_ARB_ADDRESS || "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
|
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
||||||
|
explorerUrl: "https://arbiscan.io",
|
||||||
|
explorerTxPath: "/tx/",
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 43114,
|
||||||
|
name: "Avalanche",
|
||||||
|
symbol: "AVAX",
|
||||||
|
icon: "🔴",
|
||||||
|
color: "#E84142",
|
||||||
|
usdtAddress: "0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c7",
|
||||||
|
receivingAddress: process.env.BRIDGE_AVAX_ADDRESS || "0x43DAb577f3279e11D311E7d628C6201d893A9Aa3",
|
||||||
|
rpcUrl: "https://api.avax.network/ext/bc/C/rpc",
|
||||||
|
explorerUrl: "https://snowtrace.io",
|
||||||
|
explorerTxPath: "/tx/",
|
||||||
|
decimals: 6,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// ─── ERC-20 Transfer event ABI ────────────────────────────────────────────────
|
||||||
|
// Transfer(address indexed from, address indexed to, uint256 value)
|
||||||
|
const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
||||||
|
|
||||||
|
// ─── Fetch USDT transfers to our address via eth_getLogs ──────────────────────
|
||||||
|
async function fetchUsdtTransfers(chain: ChainConfig, fromBlock: string = "latest"): Promise<Array<{
|
||||||
|
txHash: string;
|
||||||
|
fromAddress: string;
|
||||||
|
toAddress: string;
|
||||||
|
amount: number; // in USDT (human-readable)
|
||||||
|
blockNumber: number;
|
||||||
|
}>> {
|
||||||
|
try {
|
||||||
|
// Pad address to 32 bytes for topic matching
|
||||||
|
const paddedTo = "0x000000000000000000000000" + chain.receivingAddress.slice(2).toLowerCase();
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: 1,
|
||||||
|
method: "eth_getLogs",
|
||||||
|
params: [{
|
||||||
|
fromBlock: fromBlock === "latest" ? "latest" : fromBlock,
|
||||||
|
toBlock: "latest",
|
||||||
|
address: chain.usdtAddress,
|
||||||
|
topics: [
|
||||||
|
TRANSFER_TOPIC,
|
||||||
|
null, // any sender
|
||||||
|
paddedTo, // to our receiving address
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await fetch(chain.rpcUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(payload),
|
||||||
|
signal: AbortSignal.timeout(10000),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
if (!data.result || !Array.isArray(data.result)) return [];
|
||||||
|
|
||||||
|
return data.result.map((log: any) => {
|
||||||
|
const fromAddress = "0x" + log.topics[1].slice(26);
|
||||||
|
const toAddress = "0x" + log.topics[2].slice(26);
|
||||||
|
const rawAmount = BigInt(log.data);
|
||||||
|
const divisor = BigInt(10 ** chain.decimals);
|
||||||
|
const amount = Number(rawAmount) / Number(divisor);
|
||||||
|
const blockNumber = parseInt(log.blockNumber, 16);
|
||||||
|
|
||||||
|
return {
|
||||||
|
txHash: log.transactionHash,
|
||||||
|
fromAddress: fromAddress.toLowerCase(),
|
||||||
|
toAddress: toAddress.toLowerCase(),
|
||||||
|
amount,
|
||||||
|
blockNumber,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[BridgeMonitor] Error fetching transfers on ${chain.name}:`, err);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Get latest block number ──────────────────────────────────────────────────
|
||||||
|
async function getLatestBlock(chain: ChainConfig): Promise<number> {
|
||||||
|
try {
|
||||||
|
const res = await fetch(chain.rpcUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_blockNumber", params: [] }),
|
||||||
|
signal: AbortSignal.timeout(8000),
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
return parseInt(data.result, 16);
|
||||||
|
} catch {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Process new transfers and save to DB ────────────────────────────────────
|
||||||
|
async function processTransfers(chain: ChainConfig): Promise<void> {
|
||||||
|
const db = await getDb();
|
||||||
|
if (!db) return;
|
||||||
|
|
||||||
|
// Get recent blocks (last ~100 blocks ≈ ~5 min on BSC, ~20 min on ETH)
|
||||||
|
const latestBlock = await getLatestBlock(chain);
|
||||||
|
if (!latestBlock) return;
|
||||||
|
|
||||||
|
const lookbackBlocks = chain.chainId === 1 ? 50 : 200; // ETH slower, BSC faster
|
||||||
|
const fromBlock = "0x" + Math.max(0, latestBlock - lookbackBlocks).toString(16);
|
||||||
|
|
||||||
|
const transfers = await fetchUsdtTransfers(chain, fromBlock);
|
||||||
|
|
||||||
|
for (const transfer of transfers) {
|
||||||
|
if (transfer.amount < 0.01) continue; // ignore dust
|
||||||
|
|
||||||
|
// Check if already recorded
|
||||||
|
try {
|
||||||
|
const existing = await db
|
||||||
|
.select({ id: bridgeOrders.id })
|
||||||
|
.from(bridgeOrders)
|
||||||
|
.where(eq(bridgeOrders.txHash, transfer.txHash))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existing.length > 0) continue; // already recorded
|
||||||
|
|
||||||
|
// Calculate XIC amount
|
||||||
|
const xicAmount = transfer.amount / XIC_PRICE_USDT;
|
||||||
|
|
||||||
|
// Try to find matching intent (user pre-registered their XIC receive address)
|
||||||
|
const intent = await db
|
||||||
|
.select()
|
||||||
|
.from(bridgeIntents)
|
||||||
|
.where(and(
|
||||||
|
eq(bridgeIntents.fromChainId, chain.chainId),
|
||||||
|
eq(bridgeIntents.senderAddress, transfer.fromAddress),
|
||||||
|
eq(bridgeIntents.matched, false),
|
||||||
|
))
|
||||||
|
.orderBy(desc(bridgeIntents.createdAt))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
const xicReceiveAddress = intent.length > 0 ? intent[0].xicReceiveAddress : null;
|
||||||
|
|
||||||
|
// Record the order
|
||||||
|
await db.insert(bridgeOrders).values({
|
||||||
|
txHash: transfer.txHash,
|
||||||
|
walletAddress: transfer.fromAddress,
|
||||||
|
fromChainId: chain.chainId,
|
||||||
|
fromToken: "USDT",
|
||||||
|
fromAmount: String(transfer.amount),
|
||||||
|
toChainId: 56,
|
||||||
|
toToken: "XIC",
|
||||||
|
toAmount: String(xicAmount),
|
||||||
|
xicReceiveAddress,
|
||||||
|
status: "confirmed",
|
||||||
|
confirmedAt: new Date(),
|
||||||
|
blockNumber: transfer.blockNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Mark intent as matched
|
||||||
|
if (intent.length > 0) {
|
||||||
|
await db
|
||||||
|
.update(bridgeIntents)
|
||||||
|
.set({ matched: true, matchedOrderId: undefined })
|
||||||
|
.where(eq(bridgeIntents.id, intent[0].id));
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[BridgeMonitor] New ${chain.name} deposit: ${transfer.amount} USDT from ${transfer.fromAddress} → ${xicAmount} XIC`);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err?.code === "ER_DUP_ENTRY") continue;
|
||||||
|
console.error(`[BridgeMonitor] Error recording transfer ${transfer.txHash}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Start monitoring all chains ──────────────────────────────────────────────
|
||||||
|
let monitorInterval: NodeJS.Timeout | null = null;
|
||||||
|
|
||||||
|
export function startBridgeMonitor(): void {
|
||||||
|
if (monitorInterval) return;
|
||||||
|
|
||||||
|
console.log("[BridgeMonitor] Starting multi-chain USDT deposit monitor...");
|
||||||
|
|
||||||
|
const run = async () => {
|
||||||
|
for (const chain of BRIDGE_CHAINS) {
|
||||||
|
await processTransfers(chain).catch(err =>
|
||||||
|
console.error(`[BridgeMonitor] Error on ${chain.name}:`, err)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Run immediately, then every 30 seconds
|
||||||
|
run();
|
||||||
|
monitorInterval = setInterval(run, 30_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function stopBridgeMonitor(): void {
|
||||||
|
if (monitorInterval) {
|
||||||
|
clearInterval(monitorInterval);
|
||||||
|
monitorInterval = null;
|
||||||
|
console.log("[BridgeMonitor] Stopped.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Export chain config for use in routes ───────────────────────────────────
|
||||||
|
export function getChainConfig(chainId: number): ChainConfig | undefined {
|
||||||
|
return BRIDGE_CHAINS.find(c => c.chainId === chainId);
|
||||||
|
}
|
||||||
|
|
@ -25,11 +25,11 @@ const RPC_POOLS = {
|
||||||
"https://rpc.ankr.com/bsc",
|
"https://rpc.ankr.com/bsc",
|
||||||
],
|
],
|
||||||
ETH: [
|
ETH: [
|
||||||
"https://eth.llamarpc.com",
|
"https://ethereum.publicnode.com", // China-accessible
|
||||||
"https://ethereum.publicnode.com",
|
|
||||||
"https://rpc.ankr.com/eth",
|
"https://rpc.ankr.com/eth",
|
||||||
"https://1rpc.io/eth",
|
|
||||||
"https://eth.drpc.org",
|
"https://eth.drpc.org",
|
||||||
|
"https://1rpc.io/eth",
|
||||||
|
"https://eth.llamarpc.com",
|
||||||
"https://cloudflare-eth.com",
|
"https://cloudflare-eth.com",
|
||||||
"https://rpc.payload.de",
|
"https://rpc.payload.de",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import { publicProcedure, protectedProcedure, router } from "./_core/trpc";
|
||||||
import { getCombinedStats, getPresaleStats } from "./onchain";
|
import { getCombinedStats, getPresaleStats } from "./onchain";
|
||||||
import { getRecentPurchases } from "./trc20Monitor";
|
import { getRecentPurchases } from "./trc20Monitor";
|
||||||
import { getDb } from "./db";
|
import { getDb } from "./db";
|
||||||
import { trc20Purchases, trc20Intents, bridgeOrders } from "../drizzle/schema";
|
import { trc20Purchases, trc20Intents, bridgeOrders, bridgeIntents } from "../drizzle/schema";
|
||||||
import { eq, desc, sql } from "drizzle-orm";
|
import { eq, desc, sql } from "drizzle-orm";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
@ -42,7 +42,9 @@ const bridgeRouter = router({
|
||||||
toChainId: input.toChainId,
|
toChainId: input.toChainId,
|
||||||
toToken: input.toToken,
|
toToken: input.toToken,
|
||||||
toAmount: input.toAmount,
|
toAmount: input.toAmount,
|
||||||
status: "completed",
|
status: "confirmed" as const,
|
||||||
|
xicReceiveAddress: null,
|
||||||
|
confirmedAt: new Date(),
|
||||||
});
|
});
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
|
|
@ -51,7 +53,7 @@ const bridgeRouter = router({
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// List orders by wallet address
|
// List orders by wallet address (includes pending intents + confirmed orders)
|
||||||
myOrders: publicProcedure
|
myOrders: publicProcedure
|
||||||
.input(z.object({
|
.input(z.object({
|
||||||
walletAddress: z.string().min(1).max(64),
|
walletAddress: z.string().min(1).max(64),
|
||||||
|
|
@ -60,25 +62,70 @@ const bridgeRouter = router({
|
||||||
.query(async ({ input }) => {
|
.query(async ({ input }) => {
|
||||||
const db = await getDb();
|
const db = await getDb();
|
||||||
if (!db) return [];
|
if (!db) return [];
|
||||||
const rows = await db
|
const addr = input.walletAddress.toLowerCase();
|
||||||
|
// Query confirmed bridge orders
|
||||||
|
const confirmedRows = await db
|
||||||
.select()
|
.select()
|
||||||
.from(bridgeOrders)
|
.from(bridgeOrders)
|
||||||
.where(eq(bridgeOrders.walletAddress, input.walletAddress.toLowerCase()))
|
.where(eq(bridgeOrders.walletAddress, addr))
|
||||||
.orderBy(desc(bridgeOrders.createdAt))
|
.orderBy(desc(bridgeOrders.createdAt))
|
||||||
.limit(input.limit);
|
.limit(input.limit);
|
||||||
return rows.map(r => ({
|
// Query pending bridge intents (by xicReceiveAddress)
|
||||||
id: r.id,
|
const intentRows = await db
|
||||||
txHash: r.txHash,
|
.select()
|
||||||
walletAddress: r.walletAddress,
|
.from(bridgeIntents)
|
||||||
fromChainId: r.fromChainId,
|
.where(eq(bridgeIntents.xicReceiveAddress, addr))
|
||||||
fromToken: r.fromToken,
|
.orderBy(desc(bridgeIntents.createdAt))
|
||||||
fromAmount: Number(r.fromAmount),
|
.limit(input.limit);
|
||||||
toChainId: r.toChainId,
|
// Merge: intents first (pending), then confirmed orders
|
||||||
toToken: r.toToken,
|
const result = [
|
||||||
toAmount: Number(r.toAmount),
|
...intentRows.map(r => ({
|
||||||
status: r.status,
|
id: r.id,
|
||||||
createdAt: r.createdAt,
|
type: 'intent' as const,
|
||||||
}));
|
fromChainId: r.fromChainId,
|
||||||
|
xicReceiveAddress: r.xicReceiveAddress,
|
||||||
|
expectedUsdt: r.expectedUsdt ? Number(r.expectedUsdt) : null,
|
||||||
|
matched: r.matched,
|
||||||
|
status: r.matched ? 'confirmed' as const : 'pending' as const,
|
||||||
|
createdAt: r.createdAt,
|
||||||
|
})),
|
||||||
|
...confirmedRows.map(r => ({
|
||||||
|
id: r.id,
|
||||||
|
type: 'order' as const,
|
||||||
|
txHash: r.txHash,
|
||||||
|
walletAddress: r.walletAddress,
|
||||||
|
fromChainId: r.fromChainId,
|
||||||
|
fromToken: r.fromToken,
|
||||||
|
fromAmount: Number(r.fromAmount),
|
||||||
|
toChainId: r.toChainId,
|
||||||
|
toToken: r.toToken,
|
||||||
|
toAmount: Number(r.toAmount),
|
||||||
|
status: r.status,
|
||||||
|
createdAt: r.createdAt,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
return result.sort((a, b) => new Date(b.createdAt!).getTime() - new Date(a.createdAt!).getTime()).slice(0, input.limit);
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Register a bridge intent — user pre-registers before sending USDT
|
||||||
|
registerIntent: publicProcedure
|
||||||
|
.input(z.object({
|
||||||
|
fromChainId: z.number().int(),
|
||||||
|
senderAddress: z.string().max(64).or(z.literal("")).transform(v => v === "" ? undefined : v).optional(), // optional — filled when wallet connected
|
||||||
|
xicReceiveAddress: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "Invalid EVM address"),
|
||||||
|
expectedUsdt: z.number().min(0.01).optional(),
|
||||||
|
}))
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const db = await getDb();
|
||||||
|
if (!db) throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "DB unavailable" });
|
||||||
|
await db.insert(bridgeIntents).values({
|
||||||
|
fromChainId: input.fromChainId,
|
||||||
|
senderAddress: input.senderAddress ? input.senderAddress.toLowerCase() : null,
|
||||||
|
xicReceiveAddress: input.xicReceiveAddress,
|
||||||
|
expectedUsdt: input.expectedUsdt ? String(input.expectedUsdt) : null,
|
||||||
|
matched: false,
|
||||||
|
});
|
||||||
|
return { success: true, message: "Intent registered. Please send USDT now." };
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// List recent bridge orders (public)
|
// List recent bridge orders (public)
|
||||||
|
|
|
||||||
40
todo.md
40
todo.md
|
|
@ -144,11 +144,35 @@
|
||||||
|
|
||||||
## v12 Bridge跨链桥完善 + 钱包连接深度修复
|
## v12 Bridge跨链桥完善 + 钱包连接深度修复
|
||||||
|
|
||||||
- [ ] WalletSelector v5:ErrorHelpPanel组件(分类错误处理+MetaMask权限重置5步指南)
|
- [x] WalletSelector v5:ErrorHelpPanel组件(分类错误处理+MetaMask权限重置5步指南)
|
||||||
- [ ] WalletSelector v5:连接中状态改为"等待钱包授权"提示
|
- [x] WalletSelector v5:连接中状态改为"等待钱包授权"提示
|
||||||
- [ ] WalletSelector v5:错误后显示"重试"按钮和其他可用钱包
|
- [x] WalletSelector v5:错误后显示"重试"按钮和其他可用钱包
|
||||||
- [ ] Bridge页面:确认所有链(BSC/ETH/Polygon/Arbitrum/Avalanche)USDT→XIC路由逻辑
|
- [x] Bridge页面:确认所有链(BSC/ETH/Polygon/Arbitrum/Avalanche)USDT→XIC路由逻辑
|
||||||
- [ ] Bridge页面:Gas费说明(每条链原生代币:BNB/ETH/MATIC/ETH/AVAX)
|
- [x] Bridge页面:Gas费说明(每条链原生代币:BNB/ETH/MATIC/ETH/AVAX)
|
||||||
- [ ] 构建v12并部署到AI服务器(43.224.155.27)
|
- [x] 构建v12并部署到AI服务器(43.224.155.27)
|
||||||
- [ ] 同步代码到备份Git库(git.newassetchain.io)
|
- [x] 同步代码到备份Git库(git.newassetchain.io)
|
||||||
- [ ] 记录部署日志
|
- [x] 记录部署日志
|
||||||
|
|
||||||
|
## v13 自研跨链桥(完全移除Li.Fi)
|
||||||
|
|
||||||
|
- [x] 数据库:新增 bridge_deposits 表(多链USDT转入记录)
|
||||||
|
- [x] 后端:多链USDT收款地址配置(BSC/ETH/Polygon/Arbitrum/Avalanche)
|
||||||
|
- [x] 后端:链上USDT转入监听(每30秒轮询各链收款地址)
|
||||||
|
- [x] 后端:tRPC接口:提交转账意图(walletAddress + fromChain + usdtAmount + xicReceiveAddress)
|
||||||
|
- [x] 后端:tRPC接口:查询订单状态(by walletAddress)
|
||||||
|
- [x] 前端:完全移除Li.Fi依赖和代码
|
||||||
|
- [x] 前端:选链 → 显示对应链USDT收款地址 → 用户转账 → 实时状态跟踪
|
||||||
|
- [x] 前端:连接钱包后自动填写XIC接收地址
|
||||||
|
- [x] 前端:中英文双语支持
|
||||||
|
- [x] 构建并部署到AI服务器
|
||||||
|
- [x] 浏览器完整测试
|
||||||
|
- [x] 同步到备份Git库
|
||||||
|
- [x] 记录部署日志
|
||||||
|
|
||||||
|
## v13 收款地址更新(官方地址)
|
||||||
|
|
||||||
|
- [x] Bridge.tsx:更新BSC收款地址为 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3
|
||||||
|
- [x] Bridge.tsx:ETH/Polygon/Arbitrum 使用同一EVM地址 0x43DAb577f3279e11D311E7d628C6201d893A9Aa3
|
||||||
|
- [x] bridgeMonitor.ts:更新所有链收款地址
|
||||||
|
- [x] Home.tsx:更新TRC20收款地址为 TWc2ugYBFN5aSoimAh4qGt9oMyket6NYZp
|
||||||
|
- [x] contracts.ts:同步更新TRC20/ERC20/BEP20地址
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue