USDT0를 Stable로 브릿징하기
이 튜토리얼에서는 TypeScript와 ethers v6를 사용하여 이더리움 Sepolia에서 Stable 테스트넷으로 USDT0를 프로그래밍 방식으로 브릿징합니다. 스크립트를 점진적으로 빌드하고, 각 단계마다 하나의 함수를 추가합니다.
이 튜토리얼은 OFT Mesh 경로를 사용합니다. Sepolia의 OFT Adapter가 토큰을 잠그고, LayerZero의 이중 DVN 검증이 메시지를 확인하며, USDT0는 Stable에서 발행됩니다. 이 작동 방식에 대한 자세한 설명은 USDT0를 Stable로 브릿징하기를 참조하십시오.
전제 조건
- Node.js 18.0.0 이상 (
node --version으로 확인) - 제어하는 프라이빗 키가 있는 Sepolia 지갑 (실제 자금이 있는 키는 절대 사용하지 마십시오)
- 가스비를 위한 SepoliaETH (sepoliafaucet.com 또는 faucets.chain.link/sepolia에서 얻을 수 있습니다)
- 터미널에서 스크립트를 실행하는 데 대한 기본적인 지식
1. 프로젝트 설정
mkdir stable-bridge && cd stable-bridge
npm init -y
npm install ethers@6 @layerzerolabs/lz-v2-utilities
npm install -D tsxpackage.json은 다음을 포함해야 합니다:
{
"name": "stable-bridge",
"version": "1.0.0",
"scripts": {
"bridge": "tsx --env-file=.env bridge.ts"
},
"dependencies": {
"@layerzerolabs/lz-v2-utilities": "^2.3.39",
"ethers": "^6.13.0"
},
"devDependencies": {
"tsx": "^4.19.0"
}
}2. 환경 구성
자격 증명과 함께 .env 파일을 만드십시오:
PRIVATE_KEY=0xYOUR_PRIVATE_KEY_HERE
SEPOLIA_RPC_URL=https://rpc.sepolia.orgSEPOLIA_RPC_URL의 경우 다음 중 하나가 작동합니다:
- Public:
https://rpc.sepolia.org또는https://ethereum-sepolia-rpc.publicnode.com - Alchemy:
https://eth-sepolia.g.alchemy.com/v2/YOUR_KEY - Infura:
https://sepolia.infura.io/v3/YOUR_KEY
3. 스크립트 스캐폴딩
가져오기, 구성 및 main 함수가 포함된 bridge.ts를 만드십시오. 다음 단계에서 이 파일에 함수를 추가하고 main에서 호출할 것입니다.
import { ethers, Contract, Wallet, JsonRpcProvider } from "ethers";
import { Options } from "@layerzerolabs/lz-v2-utilities";
const PRIVATE_KEY = process.env.PRIVATE_KEY!;
const SEPOLIA_RPC_URL = process.env.SEPOLIA_RPC_URL || "https://rpc.sepolia.org";
// Contract addresses
const SEPOLIA_USDT0 = "0xc4DCC311c028e341fd8602D8eB89c5de94625927";
const SEPOLIA_OFT_ADAPTER = "0xc099cD946d5efCC35A99D64E808c1430cEf08126";
const STABLE_USDT0 = "0x78Cf24370174180738C5B8E352B6D14c83a6c9A9";
// Destination: Stable Testnet
const STABLE_TESTNET_EID = 40374;
// Minimal ABIs — only the functions we call
const ERC20_ABI = [
"function balanceOf(address) view returns (uint256)",
"function approve(address, uint256) returns (bool)",
"function allowance(address, address) view returns (uint256)",
"function mint(address, uint256)",
];
const OFT_ADAPTER_ABI = [
"function quoteSend((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd), bool) view returns ((uint256 nativeFee, uint256 lzTokenFee))",
"function send((uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd), (uint256 nativeFee, uint256 lzTokenFee), address) payable returns ((bytes32, uint64, (uint256, uint256)), (uint256, uint256))",
];
function addressToBytes32(addr: string): string {
return ethers.zeroPadValue(ethers.getBytes(ethers.getAddress(addr)), 32);
}
// 여기에 함수를 추가할 것입니다.
async function main() {
const provider = new JsonRpcProvider(SEPOLIA_RPC_URL);
const wallet = new Wallet(PRIVATE_KEY, provider);
const usdt0 = new Contract(SEPOLIA_USDT0, ERC20_ABI, wallet);
const oftAdapter = new Contract(SEPOLIA_OFT_ADAPTER, OFT_ADAPTER_ABI, wallet);
const amount = ethers.parseEther("1"); // 1 USDT0 (18 decimals)
// 여기에 함수 호출을 추가할 것입니다.
}
main().catch((err) => {
console.error(err.message);
process.exit(1);
});4. Sepolia에서 테스트 USDT0 발행
Sepolia의 테스트 USDT0 컨트랙트는 공개 mint 함수를 노출합니다. 다음 함수를 bridge.ts의 main 위에 추가하십시오:
async function mint(usdt0: Contract, receiver: string, amount: bigint) {
console.log(`Sepolia에서 ${ethers.formatEther(amount)} USDT0 발행 중...`);
const tx = await usdt0.mint(receiver, amount);
await tx.wait();
console.log(`발행 트랜잭션: ${tx.hash} 확인됨`);
const balance = await usdt0.balanceOf(receiver);
console.log(`USDT0 잔액: ${ethers.formatEther(balance)}`);
}그런 다음 main에서 호출하십시오:
await mint(usdt0, wallet.address, amount);스크립트를 실행하십시오:
npx tsx --env-file=.env bridge.ts체크포인트: 발행이 확인된 후 0이 아닌 USDT0 잔액이 로그되는 것을 볼 수 있습니다.
5. OFT 어댑터 승인
OFT 어댑터가 토큰을 이동하기 전에 ERC-20 허용량이 필요합니다. 다음 함수를 main 위에 추가하십시오:
async function approve(usdt0: Contract, spender: string, owner: string, amount: bigint) {
console.log("OFT 어댑터 승인 중...");
const tx = await usdt0.approve(spender, amount);
await tx.wait();
console.log(`승인 트랜잭션: ${tx.hash} 확인됨`);
const allowance = await usdt0.allowance(owner, spender);
console.log(`허용량: ${ethers.formatEther(allowance)}`);
}mint 다음에 main에 호출을 추가하십시오:
// await mint(usdt0, wallet.address, amount);
await approve(usdt0, SEPOLIA_OFT_ADAPTER, wallet.address, amount);스크립트를 실행하십시오. 이전 실행에서 이미 토큰이 있는 경우 await mint(...) 호출을 주석 처리할 수 있습니다.
체크포인트: 승인이 확인된 후 스크립트가 0이 아닌 허용량을 로그해야 합니다.
6. 수수료 견적 및 브릿지 트랜잭션 전송
quoteSend 호출은 SepoliaETH의 LayerZero 메시징 수수료를 반환하며, 이를 msg.value로 send에 전달합니다. 다음 함수를 main 위에 추가하십시오:
async function send(oftAdapter: Contract, receiver: string, amount: bigint) {
const options = Options.newOptions().addExecutorLzReceiveOption(0, 0).toBytes();
const sendParams = {
dstEid: STABLE_TESTNET_EID,
to: addressToBytes32(receiver),
amountLD: amount,
minAmountLD: amount,
extraOptions: options,
composeMsg: "0x",
oftCmd: "0x",
};
console.log("브릿지 수수료 견적 중...");
const feeResult = await oftAdapter.quoteSend(sendParams, false);
const fee = { nativeFee: feeResult.nativeFee, lzTokenFee: feeResult.lzTokenFee };
console.log(`브릿지 수수료: ${ethers.formatEther(fee.nativeFee)} ETH`);
console.log("브릿지 트랜잭션 전송 중...");
const tx = await oftAdapter.send(sendParams, fee, receiver, {
value: fee.nativeFee,
});
await tx.wait();
console.log(`브릿지 트랜잭션: ${tx.hash} 확인됨`);
console.log(`Sepolia Etherscan: https://sepolia.etherscan.io/tx/${tx.hash}`);
console.log(`LayerZero Scan: https://testnet.layerzeroscan.com/tx/${tx.hash}`);
}approve 다음에 main에 호출을 추가하십시오:
// await mint(usdt0, wallet.address, amount);
// await approve(usdt0, SEPOLIA_OFT_ADAPTER, wallet.address, amount);
await send(oftAdapter, wallet.address, amount);7. Stable 테스트넷 도착 확인
전송 후 스크립트는 토큰이 도착할 때까지 Stable 테스트넷 RPC를 폴링할 수 있습니다. 다음 함수를 main 위에 추가하십시오:
async function verify(receiver: string) {
console.log("DVN 검증 대기 중 (약 2분)...");
const stableProvider = new JsonRpcProvider("https://rpc.testnet.stable.xyz");
const stableUsdt0 = new Contract(STABLE_USDT0,
["function balanceOf(address) view returns (uint256)"], stableProvider);
const before: bigint = await stableUsdt0.balanceOf(receiver);
for (let i = 0; i < 24; i++) {
await new Promise((r) => setTimeout(r, 5000));
const current: bigint = await stableUsdt0.balanceOf(receiver);
if (current > before) {
console.log(`\nStable의 USDT0: ${ethers.formatEther(current)}`);
console.log(`탐색기: https://testnet.stablescan.xyz/address/${receiver}`);
return;
}
process.stdout.write(".");
}
console.log("\n토큰이 아직 도착하지 않았습니다. 수동으로 확인하십시오:");
console.log(`탐색기: https://testnet.stablescan.xyz/address/${receiver}`);
}send 다음에 main에 호출을 추가하십시오:
// await mint(usdt0, wallet.address, amount);
// await approve(usdt0, SEPOLIA_OFT_ADAPTER, wallet.address, amount);
// await send(oftAdapter, wallet.address, amount);
await verify(wallet.address);8. 전체 브릿지 실행
main 함수는 이제 다음과 같아야 합니다:
async function main() {
const provider = new JsonRpcProvider(SEPOLIA_RPC_URL);
const wallet = new Wallet(PRIVATE_KEY, provider);
const usdt0 = new Contract(SEPOLIA_USDT0, ERC20_ABI, wallet);
const oftAdapter = new Contract(SEPOLIA_OFT_ADAPTER, OFT_ADAPTER_ABI, wallet);
const amount = ethers.parseEther("1"); // 1 USDT0 (18 decimals)
await mint(usdt0, wallet.address, amount);
await approve(usdt0, SEPOLIA_OFT_ADAPTER, wallet.address, amount);
await send(oftAdapter, wallet.address, amount);
await verify(wallet.address);
}실행하십시오:
npx tsx --env-file=.env bridge.ts체크포인트: 다음과 같은 출력이 보여야 합니다:
Minting 1.0 USDT0 on Sepolia...
Mint tx: 0x3a1f...c9d2 confirmed
USDT0 balance: 1.0
Approving OFT Adapter...
Approve tx: 0x7b2e...f401 confirmed
Allowance: 1.0
Quoting bridge fee...
Bridge fee: 0.000101 ETH
Sending bridge transaction...
Bridge tx: 0xa94f...8c11 confirmed
Sepolia Etherscan: https://sepolia.etherscan.io/tx/0xa94f...8c11
LayerZero Scan: https://testnet.layerzeroscan.com/tx/0xa94f...8c11
Waiting for DVN verification (~2 minutes)...
......
USDT0 on Stable: 1.0Stable 테스트넷 탐색기에서 지갑 주소를 검색하여 발행 이벤트를 확인할 수도 있습니다.
빌드한 것
이더리움 Sepolia에서 Stable 테스트넷으로 USDT0를 브릿징했습니다. 이제 다음을 수행하는 방법을 알게 되었습니다:
- 컨트랙트의 공개
mint함수를 사용하여 Sepolia에서 테스트 USDT0 발행 - OFT 어댑터가 귀하를 대신하여 ERC-20 토큰을 지출하도록 승인
- 32바이트 주소 인코딩 및 executor 옵션으로 LayerZero
sendParams구성 - 자금을 약정하기 전에
quoteSend로 교차 체인 메시징 수수료 견적 send로 교차 체인 토큰 전송 실행 및 대상 체인에서 전달 확인- Stable의 RPC(
https://rpc.testnet.stable.xyz, 체인 ID2201) 및 Stablescan을 사용하여 온체인 상태 확인
다음 권장 사항
- 첫 USDT0 전송: 브릿지된 USDT0를 네이티브 및 ERC-20 전송과 함께 사용하십시오.
- USDT0를 Stable로 브릿징: OFT Mesh vs Legaacy Mesh 메커니즘에 대한 심층 분석.
- 테스트넷 정보: 전체 네트워크 매개 변수, RPC 엔드포인트 및 파우셋 세부 정보.

