Writing a one way bridge USDT program
-
Initialize contract on chain 1
// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.13;import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";import { console } from "forge-std/console.sol";import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";contract BridgeETH is Ownable {uint256 public balance;address public tokenAddress;event Deposit(address indexed depositor, uint amount);mapping(address => uint256) public pendingBalance;constructor(address _tokenAddress) Ownable(msg.sender) {tokenAddress = _tokenAddress;}function deposit(IERC20 _tokenAddress, uint256 _amount) public {require(address(_tokenAddress) == tokenAddress);require(_tokenAddress.allowance(msg.sender, address(this)) >= _amount);require(_tokenAddress.transferFrom(msg.sender, address(this), _amount));emit Deposit(msg.sender, _amount);}function withdraw(IERC20 _tokenAddress, uint256 _amount) public {require(address(_tokenAddress) == tokenAddress);require(pendingBalance[msg.sender] >= _amount);pendingBalance[msg.sender] -= _amount;_tokenAddress.transfer(msg.sender, _amount);}function burnedOnOppositeChain(address userAccount, uint256 _amount) public onlyOwner {pendingBalance[userAccount] += _amount;}} -
Initialize contract on chain 2
// SPDX-License-Identifier: UNLICENSEDpragma solidity ^0.8.13;import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";import { console } from "forge-std/console.sol";import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";interface IBUSDT is IERC20 {function mint(address _to, uint256 _amount) external;function burn(address _from, uint256 _amount) external;}contract BridgeETH is Ownable {uint256 public balance;address public tokenAddress;event Burn(address indexed burner, uint amount);mapping(address => uint256) public pendingBalance;constructor(address _tokenAddress) Ownable(msg.sender) {tokenAddress = _tokenAddress;}function burn(IBUSDT _tokenAddress, uint256 _amount) public {require(address(_tokenAddress) == tokenAddress);_tokenAddress.burn(msg.sender, _amount);emit Burn(msg.sender, _amount);}function withdraw(IBUSDT _tokenAddress, uint256 _amount) public {require(pendingBalance[msg.sender] >= _amount);pendingBalance[msg.sender] -= _amount;_tokenAddress.mint(msg.sender, _amount);}function depositedOnOppositeChain(address userAccount, uint256 _amount) public onlyOwner {pendingBalance[userAccount] += _amount;}} -
Grab the ABIs of both the contracts
const ABI1 = [{"inputs": [{"internalType": "address","name": "userAccount","type": "address"},{"internalType": "uint256","name": "_amount","type": "uint256"}],"name": "depositedOnOppositeChain","outputs": [],"stateMutability": "nonpayable","type": "function"}]const ABI2 = [{"inputs": [{"internalType": "address","name": "userAccount","type": "address"},{"internalType": "uint256","name": "_amount","type": "uint256"}],"name": "burnedOnOppositeChain","outputs": [],"stateMutability": "nonpayable","type": "function"}] -
Write the Node.js contract that will listen on events from one blockchain and call a function on the other blockchain
import { Contract, JsonRpcProvider, Wallet, id } from "ethers";import { abi } from "./abi";const provider = new JsonRpcProvider("https://eth-mainnet.g.alchemy.com/v2/nnY0qPUQLYsUvb5BKJM5bh81sI6O0PQG");const PRIVATE_KEY = "fba7342ef6879df2c735644c734ea69c140f423d84eb2d53fbdfd53fd5d7c586";const CONTRACT_ADDRESS = "0xdac17f958d2ee523a2206206994597c13d831ec7";function getWallet() {const wallet = new Wallet(PRIVATE_KEY, provider);return wallet;}async function pollBlock(blockNumber: number) {console.log("before logs")const logs = await provider.getLogs({address: CONTRACT_ADDRESS,fromBlock: blockNumber,toBlock: blockNumber,topics: [id("Deposit(address,uint256)")]});logs.forEach(async log => {const from = log.topics[1];const amount = log.topics[2];await sendTxn(from, amount);});}async function sendTxn(from: string, amount: string) {const wallet = getWallet();const contract = new Contract(CONTRACT_ADDRESS, abi, wallet);const tx = await contract.depositedOnOppositeChain(from, amount);tx.wait();}pollBlock(21493826)