0%

USDT USDC 两个稳定币项目开启了测试网

最近可有意思了USDC和USDT两家都几乎同时开启了测试网。

USDT Testnet:

本文重点讲USDT测试网,因为太坑了。

这是他的官网文档:https://docs.stable.xyz/en/developers/testnet/funding-guide

第二步你是如何都运行不成功的,因为第一步错了。

你需要mint这个代币USD₮

https://sepolia.etherscan.io/address/0xc4dcc311c028e341fd8602d8eb89c5de94625927#writeContract

为什么我会发现这个问题呢?因为我骂了AI一个小时他都改不对代码,我意识到可能是文档的问题。

他跨链会通过这个合约地址:0xc099cD946d5efCC35A99D64E808c1430cEf08126

我发现了一个用户和我一样,重试了多次也错误了多次,但是最后一次他成功了,原因的他发送了USD₮这个代币。然后顺藤摸瓜,发现教程的第一步就错了,后面不可能成功的。

第三步官方也有问题,他使用错误了浏览器,该合约在那个浏览器没有开源。去官方DC里面问,然后得到这个地址:

https://blockscout.testnet.stable.xyz/address/0xcAB8F3ed8528655E0C2fad1C504c6CfEccf50B90?tab=contract_abi

等待第二步的layerzero testnet执行成功后,再来调用第三步的链接里面的depositTo方法即可。

实际上,几乎所有的交易都被堵在了layerzero testnet上,详情:

https://testnet.layerzeroscan.com/address/0xc099cD946d5efCC35A99D64E808c1430cEf08126

参考跨链py代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import json
from web3 import Web3


# ==============================================================================
# 辅助函数: 将以太坊地址转换为 32 字节的 bytes32 格式
# 这在与需要 bytes32 类型地址参数的 LayerZero 合约交互时非常常用。
# ==============================================================================
def addr_to_32_bytes(addr: str) -> bytes:
"""
将一个标准的以太坊地址 (20字节) 转换为 32字节 的表示形式。

Args:
addr (str): 输入的以太坊地址字符串,例如 "0x..."

Returns:
bytes: 转换后的 32字节 bytes 对象。
"""
# 1. 使用 Web3.to_checksum_address 确保地址格式正确,并去掉 "0x" 前缀
hex_20 = Web3.to_checksum_address(addr)[2:]
# 2. 在左侧用 "0" 填充,直到总长度为 64 个字符 (32 字节 * 2 个十六进制字符/字节)
padded = hex_20.zfill(64)
# 3. 将填充后的十六进制字符串转换为 bytes 对象
return bytes.fromhex(padded)


# ==============================================================================
# 主函数
# ==============================================================================
def main():
# --- 1. 连接到以太坊网络 ---
# !! 注意:请将 YOUR_INFURA_PROJECT_ID 替换为您自己的 Infura 项目 ID !!
sepolia_rpc_url = "https://blockchain.googleapis.com/v1/projects/constant-rig-425116-j4/locations/us-central1/endpoints/ethereum-sepolia/rpc?key=AIzaSyCjLfQQq3AToHjT5OF62yXzIrMjh0Nreyk"
w3 = Web3(Web3.HTTPProvider(sepolia_rpc_url))

# 检查连接是否成功
if not w3.is_connected():
print("错误:无法连接到以太坊节点!请检查您的 RPC URL。")
return

print(f"成功连接到链 ID: {w3.eth.chain_id}")

# --- 2. 设置账户 ---
# 从环境变量中安全地加载你的私钥
private_key = "0x你的私钥

# 根据私钥创建账户对象
owner = w3.eth.account.from_key(private_key)
print(f"操作账户地址: {owner.address}")

# --- 3. 定义常量和地址 ---
SEPOLIA_USDT0 = Web3.to_checksum_address("0xc4DCC311c028e341fd8602D8eB89c5de94625927")
SEPOLIA_USDT0_OAPP = Web3.to_checksum_address("0xc099cD946d5efCC35A99D64E808c1430cEf08126")
RECEIVER_EID = 40374 # 目标链的 LayerZero 端点ID

# --- 4. 定义合约 ABI ---
# ERC20 的最小化 ABI,包含 approve 和 balanceOf
ERC20_ABI = json.dumps([
{"constant": False, "inputs": [{"name": "_spender", "type": "address"}, {"name": "_value", "type": "uint256"}],
"name": "approve", "outputs": [{"name": "", "type": "bool"}], "payable": False,
"stateMutability": "nonpayable", "type": "function"},
{"constant": True, "inputs": [{"name": "_owner", "type": "address"}], "name": "balanceOf",
"outputs": [{"name": "balance", "type": "uint256"}], "payable": False, "stateMutability": "view",
"type": "function"}
])

# OFTAdapter 实现合约的正确 ABI (从 Etherscan 获取)
# 注意:我们与代理合约地址交互,但需要使用实现合约的ABI
OFT_ADAPTER_ABI = """
[
{"inputs":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"_sendParam","type":"tuple"},{"internalType":"bool","name":"_payInLzToken","type":"bool"}],"name":"quoteSend","outputs":[{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"fee","type":"tuple"}],"stateMutability":"view","type":"function"},
{"inputs":[{"components":[{"internalType":"uint32","name":"dstEid","type":"uint32"},{"internalType":"bytes32","name":"to","type":"bytes32"},{"internalType":"uint256","name":"amountLD","type":"uint256"},{"internalType":"uint256","name":"minAmountLD","type":"uint256"},{"internalType":"bytes","name":"extraOptions","type":"bytes"},{"internalType":"bytes","name":"composeMsg","type":"bytes"},{"internalType":"bytes","name":"oftCmd","type":"bytes"}],"internalType":"struct SendParam","name":"_sendParam","type":"tuple"},{"components":[{"internalType":"uint256","name":"nativeFee","type":"uint256"},{"internalType":"uint256","name":"lzTokenFee","type":"uint256"}],"internalType":"struct MessagingFee","name":"_fee","type":"tuple"},{"internalType":"address","name":"_refundAddress","type":"address"}],"name":"send","outputs":[{"components":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"uint64","name":"nonce","type":"uint64"},{"internalType":"uint32","name":"eid","type":"uint32"}],"internalType":"struct MsgReceipt","name":"","type":"tuple"}],"stateMutability":"payable","type":"function"}
]
"""

# --- 5. 创建合约实例 ---
usdt0_contract = w3.eth.contract(address=SEPOLIA_USDT0, abi=ERC20_ABI)
oft_adapter_contract = w3.eth.contract(address=SEPOLIA_USDT0_OAPP, abi=OFT_ADAPTER_ABI)

# --- 6. 授权 (Approve) ---
# 授权 OFTAdapter 合约可以从我们的账户中提取指定数量的 USDT
amount_to_send = w3.to_wei(1, 'ether') # 将 1 个 ether 单位转换为 wei (10^18)

print(f"准备授权合约 {SEPOLIA_USDT0_OAPP} 使用 {w3.from_wei(amount_to_send, 'ether')} USDT...")

# 构造 approve 交易
approve_tx = usdt0_contract.functions.approve(
SEPOLIA_USDT0_OAPP,
amount_to_send
).build_transaction({
'from': owner.address,
'nonce': w3.eth.get_transaction_count(owner.address),
'gas': 200000, # Gas limit,对于 approve 通常足够
'gasPrice': w3.eth.gas_price
})

# 签名并发送交易
signed_approve_tx = w3.eth.account.sign_transaction(approve_tx, private_key)
approve_tx_hash = w3.eth.send_raw_transaction(signed_approve_tx.raw_transaction)

# 等待交易确认
print(f"Approve 交易已发送, 哈希: {approve_tx_hash.hex()}")
w3.eth.wait_for_transaction_receipt(approve_tx_hash)
print("✅ Approve 交易成功!")

# --- 7. 准备 LayerZero send 参数 ---
# `Options.newOptions().addExecutorLzReceiveOption(0, 0).toBytes()` 在JS中生成的字节串
# 这个值表示在目标链上不为执行交易额外提供gas (gas=0, value=0)
options = bytes.fromhex("00030100110100000000000000000000000000000000")

# 构建 send 函数需要的参数结构体,使用字典方便阅读
send_params = {
"dstEid": RECEIVER_EID,
"to": addr_to_32_bytes(owner.address), # 将接收者地址转换为 bytes32
"amountLD": amount_to_send,
"minAmountLD": amount_to_send,
"extraOptions": options,
"composeMsg": b"", # 空字节字符串
"oftCmd": b"", # 空字节字符串
}

# --- 8. 获取跨链费用 (quoteSend) ---
print("\n正在获取跨链费用...")
# 调用合约函数时,如果参数是结构体,web3.py 需要传入一个列表或元组
fee_struct = oft_adapter_contract.functions.quoteSend(
list(send_params.values()), # 将字典的值按顺序转为列表
False # _payInLzToken 参数
).call()

# fee_struct 是一个元组,格式为 (nativeFee, lzTokenFee)
native_fee = fee_struct[0]
print(f"获取到预估的原生代币费用: {w3.from_wei(native_fee, 'ether')} ETH")

# --- 9. 执行跨链发送 (send) ---
print("\n准备执行跨链发送...")

# 构造 send 交易
send_tx = oft_adapter_contract.functions.send(
list(send_params.values()), # 第一个参数:_sendParam 结构体
fee_struct, # 第二个参数:_fee 结构体
owner.address # 第三个参数:_refundAddress
).build_transaction({
'from': owner.address,
'value': native_fee, # 交易的 msg.value,用于支付跨链费用
'nonce': w3.eth.get_transaction_count(owner.address),
'gas': 3500000, # 跨链交易通常需要更高的 gas limit
'gasPrice': w3.eth.gas_price
})

# 签名并发送交易
signed_send_tx = w3.eth.account.sign_transaction(send_tx, private_key)
send_tx_hash = w3.eth.send_raw_transaction(signed_send_tx.raw_transaction)

# 等待交易确认
print(f"Send 交易已发送, 哈希: {send_tx_hash.hex()}")
w3.eth.wait_for_transaction_receipt(send_tx_hash)
print("✅ 跨链 Send 交易成功!")


if __name__ == "__main__":
main()

USDC Testnet:

跨链工具:https://www.npmjs.com/package/create-bridge-kit

该工具开发者推特:@realchriswilder

使用方法:

1
2
3
4
npx create-bridge-kit my-bridge-app
cd my-bridge-app
npm install
npm run dev