Skip to content

Commit fba8830

Browse files
committed
chore: add optional gas params
1 parent eef42fe commit fba8830

2 files changed

Lines changed: 130 additions & 17 deletions

File tree

examples/oft/tasks/sendEvm.ts

Lines changed: 105 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import path from 'path'
2+
13
import { BigNumber, ContractTransaction } from 'ethers'
24
import { parseUnits } from 'ethers/lib/utils'
35
import { HardhatRuntimeEnvironment } from 'hardhat/types'
46

5-
import { createGetHreByEid } from '@layerzerolabs/devtools-evm-hardhat'
7+
import { OmniPointHardhat, createGetHreByEid } from '@layerzerolabs/devtools-evm-hardhat'
68
import { createLogger } from '@layerzerolabs/io-devtools'
79
import { ChainType, endpointIdToChainType, endpointIdToNetwork } from '@layerzerolabs/lz-definitions'
8-
import { addressToBytes32 } from '@layerzerolabs/lz-v2-utilities'
9-
10-
import layerzeroConfig from '../layerzero.config'
10+
import { Options, addressToBytes32 } from '@layerzerolabs/lz-v2-utilities'
1111

1212
import { SendResult } from './types'
1313
import { DebugLogger, KnownErrors, getLayerZeroScanLink } from './utils'
@@ -19,14 +19,29 @@ export interface EvmArgs {
1919
dstEid: number
2020
amount: string
2121
to: string
22+
oappConfig: string
2223
minAmount?: string
23-
extraOptions?: string
24+
extraLzReceiveOptions?: string[]
25+
extraLzComposeOptions?: string[]
26+
extraNativeDropOptions?: string[]
2427
composeMsg?: string
2528
oftAddress?: string
2629
}
2730

2831
export async function sendEvm(
29-
{ srcEid, dstEid, amount, to, minAmount, extraOptions, composeMsg, oftAddress }: EvmArgs,
32+
{
33+
srcEid,
34+
dstEid,
35+
amount,
36+
to,
37+
oappConfig,
38+
minAmount,
39+
extraLzReceiveOptions,
40+
extraLzComposeOptions,
41+
extraNativeDropOptions,
42+
composeMsg,
43+
oftAddress,
44+
}: EvmArgs,
3045
hre: HardhatRuntimeEnvironment
3146
): Promise<SendResult> {
3247
if (endpointIdToChainType(srcEid) !== ChainType.EVM) {
@@ -51,12 +66,13 @@ export async function sendEvm(
5166
if (oftAddress) {
5267
wrapperAddress = oftAddress
5368
} else {
54-
const { contracts } = typeof layerzeroConfig === 'function' ? await layerzeroConfig() : layerzeroConfig
55-
const wrapper = contracts.find((c) => c.contract.eid === srcEid)
69+
const layerZeroConfig = (await import(path.resolve('./', oappConfig))).default
70+
const { contracts } = typeof layerZeroConfig === 'function' ? await layerZeroConfig() : layerZeroConfig
71+
const wrapper = contracts.find((c: { contract: OmniPointHardhat }) => c.contract.eid === srcEid)
5672
if (!wrapper) throw new Error(`No config for EID ${srcEid}`)
5773
wrapperAddress = wrapper.contract.contractName
5874
? (await srcEidHre.deployments.get(wrapper.contract.contractName)).address
59-
: wrapper.contract.address!
75+
: wrapper.contract.address || ''
6076
}
6177

6278
// 2️⃣ load IOFT ABI, extend it with token()
@@ -78,18 +94,95 @@ export async function sendEvm(
7894
// hex string → Uint8Array → zero-pad to 32 bytes
7995
const toBytes = addressToBytes32(to)
8096

81-
// 6️⃣ build sendParam and dispatch
97+
// 6️⃣ Build options dynamically using Options.newOptions()
98+
let options = Options.newOptions()
99+
100+
// Add lzReceive options
101+
if (extraLzReceiveOptions && extraLzReceiveOptions.length > 0) {
102+
// Handle case where Hardhat's CSV parsing splits "gas,value" into separate elements
103+
if (extraLzReceiveOptions.length % 2 !== 0) {
104+
throw new Error(
105+
`Invalid lzReceive options: received ${extraLzReceiveOptions.length} values, but expected pairs of gas,value`
106+
)
107+
}
108+
109+
for (let i = 0; i < extraLzReceiveOptions.length; i += 2) {
110+
const gas = Number(extraLzReceiveOptions[i])
111+
const value = Number(extraLzReceiveOptions[i + 1]) || 0
112+
options = options.addExecutorLzReceiveOption(gas, value)
113+
logger.info(`Added lzReceive option: ${gas} gas, ${value} value`)
114+
}
115+
}
116+
117+
// Add lzCompose options
118+
if (extraLzComposeOptions && extraLzComposeOptions.length > 0) {
119+
// Handle case where Hardhat's CSV parsing splits "index,gas,value" into separate elements
120+
if (extraLzComposeOptions.length % 3 !== 0) {
121+
throw new Error(
122+
`Invalid lzCompose options: received ${extraLzComposeOptions.length} values, but expected triplets of index,gas,value`
123+
)
124+
}
125+
126+
for (let i = 0; i < extraLzComposeOptions.length; i += 3) {
127+
const index = Number(extraLzComposeOptions[i])
128+
const gas = Number(extraLzComposeOptions[i + 1])
129+
const value = Number(extraLzComposeOptions[i + 2]) || 0
130+
options = options.addExecutorComposeOption(index, gas, value)
131+
logger.info(`Added lzCompose option: index ${index}, ${gas} gas, ${value} value`)
132+
}
133+
}
134+
135+
// Add native drop options
136+
if (extraNativeDropOptions && extraNativeDropOptions.length > 0) {
137+
// Handle case where Hardhat's CSV parsing splits "amount,recipient" into separate elements
138+
if (extraNativeDropOptions.length % 2 !== 0) {
139+
throw new Error(
140+
`Invalid native drop options: received ${extraNativeDropOptions.length} values, but expected pairs of amount,recipient`
141+
)
142+
}
143+
144+
for (let i = 0; i < extraNativeDropOptions.length; i += 2) {
145+
const amountStr = extraNativeDropOptions[i]
146+
const recipient = extraNativeDropOptions[i + 1]
147+
148+
if (!amountStr || !recipient) {
149+
throw new Error(
150+
`Invalid native drop option: Both amount and recipient must be provided. Got amount="${amountStr}", recipient="${recipient}"`
151+
)
152+
}
153+
154+
try {
155+
options = options.addExecutorNativeDropOption(amountStr.trim(), recipient.trim())
156+
logger.info(`Added native drop option: ${amountStr.trim()} wei to ${recipient.trim()}`)
157+
} catch (error) {
158+
// Provide helpful context if the amount exceeds protocol limits
159+
const maxUint128 = BigInt('340282366920938463463374607431768211455') // 2^128 - 1
160+
const maxUint128Ether = Number(maxUint128) / 1e18 // Convert to ETH for readability
161+
162+
throw new Error(
163+
`Failed to add native drop option with amount ${amountStr.trim()} wei. ` +
164+
`LayerZero protocol constrains native drop amounts to uint128 maximum ` +
165+
`(${maxUint128.toString()} wei ≈ ${maxUint128Ether.toFixed(2)} ETH). ` +
166+
`Original error: ${error instanceof Error ? error.message : String(error)}`
167+
)
168+
}
169+
}
170+
}
171+
172+
const extraOptions = options.toHex()
173+
174+
// 7️⃣ build sendParam and dispatch
82175
const sendParam = {
83176
dstEid,
84177
to: toBytes,
85178
amountLD: amountUnits.toString(),
86179
minAmountLD: minAmount ? parseUnits(minAmount, decimals).toString() : amountUnits.toString(),
87-
extraOptions: extraOptions ? extraOptions.toString() : '0x',
180+
extraOptions: extraOptions,
88181
composeMsg: composeMsg ? composeMsg.toString() : '0x',
89182
oftCmd: '0x',
90183
}
91184

92-
// 6️⃣ Quote (MessagingFee = { nativeFee, lzTokenFee })
185+
// 8️⃣ Quote (MessagingFee = { nativeFee, lzTokenFee })
93186
logger.info('Quoting the native gas cost for the send transaction...')
94187
let msgFee: { nativeFee: BigNumber; lzTokenFee: BigNumber }
95188
try {

examples/oft/tasks/sendOFT.ts

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { task, types } from 'hardhat/config'
22
import { HardhatRuntimeEnvironment } from 'hardhat/types'
33

4+
import { types as cliTypes } from '@layerzerolabs/devtools-evm-hardhat'
45
import { ChainType, endpointIdToChainType, endpointIdToNetwork } from '@layerzerolabs/lz-definitions'
56

67
import { EvmArgs, sendEvm } from './sendEvm'
@@ -12,10 +13,15 @@ interface MasterArgs {
1213
dstEid: number
1314
amount: string
1415
to: string
16+
oappConfig: string
1517
/** Minimum amount to receive in case of custom slippage or fees (human readable units, e.g. "1.5") */
1618
minAmount?: string
17-
/** Extra options for sending additional gas units to lzReceive, lzCompose, or receiver address */
18-
extraOptions?: string
19+
/** Array of lzReceive options as comma-separated values "gas,value" - e.g. --extra-lz-receive-options "200000,0" */
20+
extraLzReceiveOptions?: string[]
21+
/** Array of lzCompose options as comma-separated values "index,gas,value" - e.g. --extra-lz-compose-options "0,500000,0" */
22+
extraLzComposeOptions?: string[]
23+
/** Array of native drop options as comma-separated values "amount,recipient" - e.g. --extra-native-drop-options "1000000000000000000,0x1234..." */
24+
extraNativeDropOptions?: string[]
1925
/** Arbitrary bytes message to deliver alongside the OFT */
2026
composeMsg?: string
2127
/** EVM: 20-byte hex address */
@@ -27,18 +33,32 @@ task('lz:oft:send', 'Sends OFT tokens cross‐chain from EVM chains')
2733
.addParam('dstEid', 'Destination endpoint ID', undefined, types.int)
2834
.addParam('amount', 'Amount to send (human readable units, e.g. "1.5")', undefined, types.string)
2935
.addParam('to', 'Recipient address (20-byte hex for EVM)', undefined, types.string)
36+
.addOptionalParam('oappConfig', 'Path to the LayerZero config file', 'layerzero.config.ts', types.string)
3037
.addOptionalParam(
3138
'minAmount',
3239
'Minimum amount to receive in case of custom slippage or fees (human readable units, e.g. "1.5")',
3340
undefined,
3441
types.string
3542
)
3643
.addOptionalParam(
37-
'extraOptions',
38-
'Extra options for sending additional gas units to lzReceive, lzCompose, or receiver address',
44+
'extraLzReceiveOptions',
45+
'Array of extra lzReceive options in format "gas,value" (e.g. ["200000,0", "100000,1000000000000000000"])',
3946
undefined,
40-
types.string
47+
cliTypes.csv
48+
)
49+
.addOptionalParam(
50+
'extraLzComposeOptions',
51+
'Array of extra lzCompose options in format "index,gas,value" (e.g. ["0,500000,0", "1,300000,1000000000000000000"])',
52+
undefined,
53+
cliTypes.csv
54+
)
55+
.addOptionalParam(
56+
'extraNativeDropOptions',
57+
'Array of extra native drop options in format "amount,recipient" (e.g. ["1000000000000000000,0x1234..."])',
58+
undefined,
59+
cliTypes.csv
4160
)
61+
.addOptionalParam('composeMsg', 'Arbitrary bytes message to deliver alongside the OFT', undefined, types.string)
4262
.addOptionalParam(
4363
'oftAddress',
4464
'Override the source local deployment OFT address (20-byte hex for EVM)',

0 commit comments

Comments
 (0)