import { getNetworkNameForEid, types } from '@layerzerolabs/devtools-evm-hardhat';
import type { EndpointId } from '@layerzerolabs/lz-definitions';
import { addressToBytes32 } from '@layerzerolabs/lz-v2-utilities';
import { Options } from '@layerzerolabs/lz-v2-utilities';
import type { BigNumberish, BytesLike } from 'ethers';
import { Contract } from 'zksync-ethers';
import { task } from 'hardhat/config';
// LzOftHelper ABI (only the functions we need)
const HELPER_ABI = [
"function send(address oftContract, tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam) external returns (tuple(bytes32 guid, uint256 nonce, bytes32 recipient, bytes message), tuple(uint256 amountLD, uint256 minAmountLD))",
"function addressToBytes32(address _addr) external pure returns (bytes32)"
];
// Helper contract address
const HELPER_ADDRESS = "0x88172F3041Bd0787520dbc9Bd33D3d48e1fb46dc";
interface Args {
amount: string;
to: string;
toEid: EndpointId;
sophon?: boolean;
}
interface SendParam {
dstEid: EndpointId; // Destination endpoint ID, represented as a number.
to: BytesLike; // Recipient address, represented as bytes.
amountLD: BigNumberish; // Amount to send in local decimals.
minAmountLD: BigNumberish; // Minimum amount to send in local decimals.
extraOptions: BytesLike; // Additional options supplied by the caller to be used in the LayerZero message.
composeMsg: BytesLike; // The composed message for the send() operation.
oftCmd: BytesLike; // The OFT command to be executed, unused in default OFT implementations.
}
// send tokens from a contract on one network to another
task('lz:oft:send', 'Sends tokens from either OFT or OFTAdapter')
.addParam('to', 'contract address on network B', undefined, types.string)
.addParam('toEid', 'destination endpoint ID', undefined, types.eid)
.addParam('amount', 'amount to transfer in token decimals', undefined, types.string)
.addOptionalParam('sophon', 'use LzOftHelper for Sophon network', false, types.boolean)
.setAction(async (taskArgs: Args, { ethers, deployments }) => {
const toAddress = taskArgs.to;
const eidB = taskArgs.toEid;
// Get the contract factories
const oftDeployment = await deployments.get('MyOFT');
const [signer] = await ethers.getSigners();
// Create contract instances
const oftContract = new Contract(oftDeployment.address, oftDeployment.abi, signer);
const helperContract = new Contract(HELPER_ADDRESS, HELPER_ABI, signer);
const decimals = await oftContract.decimals();
const amount = ethers.utils.parseUnits(taskArgs.amount, decimals);
const options = Options.newOptions().addExecutorLzReceiveOption(65000, 0).toBytes();
// Now you can interact with the correct contract
const oft = oftContract;
const sendParam: SendParam = {
dstEid: eidB,
to: addressToBytes32(toAddress),
amountLD: amount,
minAmountLD: amount,
extraOptions: options,
composeMsg: ethers.utils.arrayify('0x'), // Assuming no composed message
oftCmd: ethers.utils.arrayify('0x'), // Assuming no OFT command is needed
};
console.log(
`sending ${taskArgs.amount} token(s) to network ${getNetworkNameForEid(eidB)} (${eidB})`
);
if (taskArgs.sophon) {
const innerTokenAddress = await oft.token();
const ERC20Factory = await ethers.getContractFactory('ERC20');
const innerToken = ERC20Factory.attach(innerTokenAddress);
// Approve tokens to helper contract
await innerToken.approve(helperContract.getAddress(), amount);
// Send via helper
const tx = await helperContract.send(oftContract.getAddress(), sendParam);
console.log(`Send tx initiated via helper. See: https://layerzeroscan.com/tx/${tx.hash}`);
} else {
// Original direct sending logic
const feeQuote = await oft.quoteSend(sendParam, false);
const nativeFee = feeQuote.nativeFee;
console.log(
`sending ${taskArgs.amount} token(s) to network ${getNetworkNameForEid(eidB)} (${eidB})`,
);
const ERC20Factory = await ethers.getContractFactory('ERC20');
const innerTokenAddress = await oft.token();
// // If the token address !== address(this), then this is an OFT Adapter
// if (innerTokenAddress !== oft.address) {
// // If the contract is OFT Adapter, get decimals from the inner token
// const innerToken = ERC20Factory.attach(innerTokenAddress);
// // Approve the amount to be spent by the oft contract
// await innerToken.approve(oftDeployment.address, amount);
// }
const r = await oft.send(sendParam, {nativeFee: nativeFee, lzTokenFee: 0}, signer.address, {
value: nativeFee,
});
console.log(`Send tx initiated directly. See: https://layerzeroscan.com/tx/${r.hash}`);
}
});