From 079695fddbdacd09d43dae1f114e16c756326b92 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Wed, 18 Jun 2025 14:40:34 -0700 Subject: [PATCH 01/28] Enhance EVM send script and Aptos module with message encoding and decoding features - Updated `evm-send.ts` to encode messages using `solidityPack` and log the encoded message. - Modified Aptos module to include a new `DecodedMessage` struct for storing received message details. - Added utility functions for byte and hex string conversions in a new `utils.move` file. - Implemented view functions to retrieve decoded message data from the Aptos module. --- examples/oapp-aptos-move/scripts/evm-send.ts | 37 +++-- examples/oapp-aptos-move/sources/oapp.move | 114 +++++++++++-- examples/oapp-aptos-move/sources/utils.move | 159 +++++++++++++++++++ 3 files changed, 285 insertions(+), 25 deletions(-) create mode 100644 examples/oapp-aptos-move/sources/utils.move diff --git a/examples/oapp-aptos-move/scripts/evm-send.ts b/examples/oapp-aptos-move/scripts/evm-send.ts index 5cb9ff9e01..6f220dc681 100644 --- a/examples/oapp-aptos-move/scripts/evm-send.ts +++ b/examples/oapp-aptos-move/scripts/evm-send.ts @@ -2,7 +2,9 @@ import { ethers } from 'ethers' import { EndpointId } from '@layerzerolabs/lz-definitions' import { Options } from '@layerzerolabs/lz-v2-utilities' + import 'dotenv/config' +import { makeBytes32 } from '../../../packages/devtools/dist' // ABI for the functions we need const ABI = [ @@ -27,29 +29,40 @@ async function main() { // Create contract instance const myOApp = new ethers.Contract(contractAddress, ABI, wallet) - // Destination endpoint ID for Aptos - const aptosEid = EndpointId.APTOS_V2_TESTNET + // Destination endpoint ID for Aptos/Movement + const aptosEid = EndpointId.APTOS_V2_TESTNET // or EndpointId.MOVEMENT_V2_TESTNET + + // Example addresses and number to encode + const address1 = '0x1234567890123456789012345678901234567890' + const address2 = '0x9876543210987654321098765432109876543210' + const number = ethers.BigNumber.from('123456789012345678901234567890') + + const encodedMessage = ethers.utils.solidityPack( + ['bytes32', 'bytes32', 'uint256'], + [makeBytes32(address1), makeBytes32(address2), ethers.BigNumber.from(number)] + ) + + const hexString = ethers.utils.hexlify(encodedMessage) - // Message to send - const message = 'Hello Aptos!' + console.log('Encoded message:', hexString) + console.log('Address1:', address1) + console.log('Address2:', address2) + console.log('Number:', number.toString()) // Build options with gas for execution - const options = Options.newOptions() - .addExecutorLzReceiveOption(200000, 0) // Similar to test file - .toHex() - .toString() + const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString() try { // Get quote - const [nativeFee] = await myOApp.quote(aptosEid, message, options, false) - console.log(`Quote for message: ${nativeFee} ETH`) + const [nativeFee] = await myOApp.quote(aptosEid, hexString, options, false) + console.log(`Quote for message: ${ethers.utils.formatEther(nativeFee)} ETH`) // Send message - const tx = await myOApp.send(aptosEid, message, options, { + const tx = await myOApp.send(aptosEid, hexString, options, { value: nativeFee.toString(), }) - console.log('Sending message to Aptos...') + console.log('Sending encoded message to Aptos/Movement...') const receipt = await tx.wait() console.log(`Transaction hash: ${receipt?.transactionHash}`) console.log('Message sent!') diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index 85c589f75e..2cfd627b75 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -9,11 +9,19 @@ module oapp::oapp { use std::option::Option; use std::primary_fungible_store; use std::signer::address_of; + use std::string; + use std::vector; + use aptos_std::from_bcs; - use endpoint_v2_common::bytes32::Bytes32; + #[test_only] + use std::account; + + use endpoint_v2_common::bytes32::{Self, Bytes32}; use endpoint_v2_common::native_token; + use endpoint_v2_common::serde; use oapp::oapp_core::{combine_options, lz_quote, lz_send, refund_fees}; use oapp::oapp_store::OAPP_ADDRESS; + use oapp::utils::{bytes_to_string, hex_string_to_bytes}; friend oapp::oapp_receive; friend oapp::oapp_compose; @@ -24,13 +32,23 @@ module oapp::oapp { value: u64 } - fun init_module(account: &signer) { - move_to(account, Counter { value: 0 }); + struct DecodedMessage has key { + address1: address, + address2: address, + number: u256, + counter: u64, + raw_message: vector } - #[view] - public fun get_counter(): u64 acquires Counter { - borrow_global(@oapp).value + fun init_module(account: &signer) { + move_to(account, Counter { value: 0 }); + move_to(account, DecodedMessage { + address1: @0x0, + address2: @0x0, + number: 0, + counter: 0, + raw_message: vector::empty() + }); } public(friend) fun lz_receive_impl( @@ -41,18 +59,59 @@ module oapp::oapp { _message: vector, _extra_data: vector, receive_value: Option, - ) acquires Counter { - // Deposit any received value + ) acquires Counter, DecodedMessage { option::destroy(receive_value, |value| primary_fungible_store::deposit(OAPP_ADDRESS(), value)); - // Increment counter - let counter = borrow_global_mut(@oapp); + let counter = borrow_global_mut(OAPP_ADDRESS()); counter.value = counter.value + 1; - // todo: Perform any actions with received message here + let string_length = ( + (*vector::borrow(&_message, 60) as u64) << 24 | + (*vector::borrow(&_message, 61) as u64) << 16 | + (*vector::borrow(&_message, 62) as u64) << 8 | + (*vector::borrow(&_message, 63) as u64) + ); + + let string_start = 64; + let string_end = string_start + string_length; + let string_bytes = vector::slice(&_message, string_start, string_end); + let decoded_string = bytes_to_string(string_bytes); + + let string_content_bytes = *string::bytes(&decoded_string); + let hex_part_bytes = vector::slice(&string_content_bytes, 2, vector::length(&string_content_bytes)); + let hex_part_string = bytes_to_string(hex_part_bytes); + let hex_content = hex_string_to_bytes(hex_part_string); + + let addr1_bytes = vector::slice(&hex_content, 0, 32); + let decoded_addr1 = from_bcs::to_address(addr1_bytes); + + let addr2_bytes = vector::slice(&hex_content, 32, 64); + let decoded_addr2 = from_bcs::to_address(addr2_bytes); + + let hex_content_len = vector::length(&hex_content); + let number_bytes = if (hex_content_len >= 96) { + vector::slice(&hex_content, 64, 96) + } else { + vector::slice(&hex_content, 64, hex_content_len) + }; + + let number_u256 = 0u256; + let j = 0; + let num_bytes_len = vector::length(&number_bytes); + while (j < num_bytes_len) { + let byte_val = *vector::borrow(&number_bytes, j); + number_u256 = (number_u256 << 8) + (byte_val as u256); + j = j + 1; + }; + + let decoded_message = borrow_global_mut(OAPP_ADDRESS()); + decoded_message.address1 = decoded_addr1; + decoded_message.address2 = decoded_addr2; + decoded_message.number = number_u256; + decoded_message.counter = counter.value; + decoded_message.raw_message = _message; } - // todo: replicate the logic in here where sending a message must happen public entry fun example_message_sender( account: &signer, @@ -126,8 +185,37 @@ module oapp::oapp { 0 } + // ================================================== View Functions =========================================== + + #[view] + public fun get_decoded_address1(): address acquires DecodedMessage { + borrow_global(OAPP_ADDRESS()).address1 + } + + #[view] + public fun get_decoded_address2(): address acquires DecodedMessage { + borrow_global(OAPP_ADDRESS()).address2 + } + + #[view] + public fun get_decoded_number(): u256 acquires DecodedMessage { + borrow_global(OAPP_ADDRESS()).number + } + + #[view] + public fun get_counter_value(): u64 acquires Counter { + borrow_global(OAPP_ADDRESS()).value + } + + #[view] + public fun get_raw_message(): vector acquires DecodedMessage { + borrow_global(OAPP_ADDRESS()).raw_message + } + // ================================================== Error Codes ================================================= const ECOMPOSE_NOT_IMPLEMENTED: u64 = 1; const EINSUFFICIENT_BALANCE: u64 = 2; -} + const EINVALID_HEX_CHAR: u64 = 3; + const EINVALID_LENGTH: u64 = 4; +} \ No newline at end of file diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move new file mode 100644 index 0000000000..246760ff8d --- /dev/null +++ b/examples/oapp-aptos-move/sources/utils.move @@ -0,0 +1,159 @@ +module oapp::utils { + use std::string::{Self, String}; + use std::vector; + use aptos_std::from_bcs; + use aptos_std::bcs; + + /// Converts a vector of bytes to a UTF-8 string + /// Will abort if the bytes are not valid UTF-8 + public fun bytes_to_string(bytes: vector): String { + string::utf8(bytes) + } + + /// Converts a UTF-8 string to a vector of bytes + public fun string_to_bytes(str: String): vector { + *string::bytes(&str) + } + + /// Safely converts a vector of bytes to a UTF-8 string + /// Returns an Option: Some(string) if valid UTF-8, None if invalid + public fun try_bytes_to_string(bytes: vector): std::option::Option { + if (string::try_utf8(bytes) != std::option::none()) { + std::option::some(string::utf8(bytes)) + } else { + std::option::none() + } + } + + /// Converts a hex string (without 0x prefix) to a vector of bytes + /// Example: "48656c6c6f" -> b"Hello" + /// Automatically pads odd-length strings with a leading zero + public fun hex_string_to_bytes(hex_str: String): vector { + let hex_bytes = string::bytes(&hex_str); + let len = vector::length(hex_bytes); + + let padded_hex = if (len % 2 == 1) { + let padded = vector::empty(); + vector::push_back(&mut padded, 48); + vector::append(&mut padded, *hex_bytes); + padded + } else { + *hex_bytes + }; + + let padded_len = vector::length(&padded_hex); + let result = vector::empty(); + let i = 0; + while (i < padded_len) { + let high_nibble = hex_char_to_u8(*vector::borrow(&padded_hex, i)); + let low_nibble = hex_char_to_u8(*vector::borrow(&padded_hex, i + 1)); + let byte_val = (high_nibble << 4) | low_nibble; + vector::push_back(&mut result, byte_val); + i = i + 2; + }; + result + } + + /// Converts a hex string to an Aptos address + /// Supports both with and without 0x prefix + /// The hex string should represent exactly 32 bytes (64 hex characters) + public fun hex_string_to_address(hex_str: String): address { + let clean_hex = strip_hex_prefix(hex_str); + let hex_bytes = hex_string_to_bytes(clean_hex); + + let padded_bytes = pad_to_32_bytes(hex_bytes); + from_bcs::to_address(padded_bytes) + } + + /// Converts an address to a hex string with 0x prefix + public fun address_to_hex_string(addr: address): String { + let addr_bytes = bcs::to_bytes(&addr); + let hex_str = bytes_to_hex_string(addr_bytes); + let prefix = b"0x"; + let hex_bytes = string::bytes(&hex_str); + vector::append(&mut prefix, *hex_bytes); + string::utf8(prefix) + } + + /// Converts bytes to a hex string (lowercase) + public fun bytes_to_hex_string(bytes: vector): String { + let hex_chars = b"0123456789abcdef"; + let result = vector::empty(); + + let i = 0; + let len = vector::length(&bytes); + while (i < len) { + let byte = *vector::borrow(&bytes, i); + let high = (byte >> 4) & 0x0f; + let low = byte & 0x0f; + vector::push_back(&mut result, *vector::borrow(&hex_chars, (high as u64))); + vector::push_back(&mut result, *vector::borrow(&hex_chars, (low as u64))); + i = i + 1; + }; + + string::utf8(result) + } + + /// Helper function to strip 0x or 0X prefix from hex string + fun strip_hex_prefix(hex_str: String): String { + let bytes = string::bytes(&hex_str); + let len = vector::length(bytes); + + if (len >= 2) { + let first = *vector::borrow(bytes, 0); + let second = *vector::borrow(bytes, 1); + + if (first == 48 && (second == 120 || second == 88)) { + let remaining = vector::empty(); + let i = 2; + while (i < len) { + vector::push_back(&mut remaining, *vector::borrow(bytes, i)); + i = i + 1; + }; + return string::utf8(remaining) + } + }; + + hex_str + } + + /// Helper function to pad bytes to 32 bytes (left-padded with zeros) + fun pad_to_32_bytes(bytes: vector): vector { + let len = vector::length(&bytes); + assert!(len <= 32, EINVALID_ADDRESS_LENGTH); + + if (len == 32) { + return bytes + }; + + let result = vector::empty(); + let padding_needed = 32 - len; + let i = 0; + + while (i < padding_needed) { + vector::push_back(&mut result, 0); + i = i + 1; + }; + + vector::append(&mut result, bytes); + result + } + + /// Helper function to convert a hex character to u8 + fun hex_char_to_u8(char: u8): u8 { + if (char >= 48 && char <= 57) { + char - 48 + } else if (char >= 65 && char <= 70) { + char - 65 + 10 + } else if (char >= 97 && char <= 102) { + char - 97 + 10 + } else { + abort EINVALID_HEX_CHARACTER + } + } + + const EINVALID_LENGTH: u64 = 1; + const EINVALID_HEX_LENGTH: u64 = 2; + const EINVALID_HEX_CHARACTER: u64 = 3; + const EINVALID_ADDRESS_LENGTH: u64 = 4; +} \ No newline at end of file From 6d6949646b85e2a179054f5ddd9b8641c6db2f37 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Thu, 19 Jun 2025 19:00:53 -0700 Subject: [PATCH 02/28] Refactor Aptos module and utility functions for improved message handling - Renamed `DecodedMessage` struct to `ReceiveData` for clarity. - Simplified message parsing logic in `parse_message` function. - Updated `lz_receive_impl` to utilize the new `ReceiveData` struct. - Removed unused utility functions and constants from `utils.move`. - Added unit tests for `parse_message` to ensure correct functionality. --- examples/oapp-aptos-move/sources/oapp.move | 87 ++++++++------- examples/oapp-aptos-move/sources/utils.move | 104 +++--------------- .../oapp-aptos-move/tests/oapp_tests.move | 42 +++++++ 3 files changed, 105 insertions(+), 128 deletions(-) create mode 100644 examples/oapp-aptos-move/tests/oapp_tests.move diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index 2cfd627b75..e195852374 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -32,7 +32,7 @@ module oapp::oapp { value: u64 } - struct DecodedMessage has key { + struct ReceiveData has key { address1: address, address2: address, number: u256, @@ -42,7 +42,7 @@ module oapp::oapp { fun init_module(account: &signer) { move_to(account, Counter { value: 0 }); - move_to(account, DecodedMessage { + move_to(account, ReceiveData { address1: @0x0, address2: @0x0, number: 0, @@ -51,36 +51,20 @@ module oapp::oapp { }); } - public(friend) fun lz_receive_impl( - _src_eid: u32, - _sender: Bytes32, - _nonce: u64, - _guid: Bytes32, - _message: vector, - _extra_data: vector, - receive_value: Option, - ) acquires Counter, DecodedMessage { - option::destroy(receive_value, |value| primary_fungible_store::deposit(OAPP_ADDRESS(), value)); - - let counter = borrow_global_mut(OAPP_ADDRESS()); - counter.value = counter.value + 1; - + public fun parse_message(message: vector): (address, address, u256) { let string_length = ( - (*vector::borrow(&_message, 60) as u64) << 24 | - (*vector::borrow(&_message, 61) as u64) << 16 | - (*vector::borrow(&_message, 62) as u64) << 8 | - (*vector::borrow(&_message, 63) as u64) + (*vector::borrow(&message, 60) as u64) << 24 | + (*vector::borrow(&message, 61) as u64) << 16 | + (*vector::borrow(&message, 62) as u64) << 8 | + (*vector::borrow(&message, 63) as u64) ); let string_start = 64; let string_end = string_start + string_length; - let string_bytes = vector::slice(&_message, string_start, string_end); - let decoded_string = bytes_to_string(string_bytes); - - let string_content_bytes = *string::bytes(&decoded_string); - let hex_part_bytes = vector::slice(&string_content_bytes, 2, vector::length(&string_content_bytes)); - let hex_part_string = bytes_to_string(hex_part_bytes); - let hex_content = hex_string_to_bytes(hex_part_string); + let string_bytes = vector::slice(&message, string_start, string_end); + + let hex_bytes = vector::slice(&string_bytes, 2, vector::length(&string_bytes)); + let hex_content = hex_string_to_bytes(string::utf8(hex_bytes)); let addr1_bytes = vector::slice(&hex_content, 0, 32); let decoded_addr1 = from_bcs::to_address(addr1_bytes); @@ -104,12 +88,31 @@ module oapp::oapp { j = j + 1; }; - let decoded_message = borrow_global_mut(OAPP_ADDRESS()); - decoded_message.address1 = decoded_addr1; - decoded_message.address2 = decoded_addr2; - decoded_message.number = number_u256; - decoded_message.counter = counter.value; - decoded_message.raw_message = _message; + (decoded_addr1, decoded_addr2, number_u256) + } + + public(friend) fun lz_receive_impl( + _src_eid: u32, + _sender: Bytes32, + _nonce: u64, + _guid: Bytes32, + _message: vector, + _extra_data: vector, + receive_value: Option, + ) acquires Counter, ReceiveData { + option::destroy(receive_value, |value| primary_fungible_store::deposit(OAPP_ADDRESS(), value)); + + let counter = borrow_global_mut(OAPP_ADDRESS()); + counter.value = counter.value + 1; + + let (decoded_addr1, decoded_addr2, number_u256) = parse_message(_message); + + let receive_data = borrow_global_mut(OAPP_ADDRESS()); + receive_data.address1 = decoded_addr1; + receive_data.address2 = decoded_addr2; + receive_data.number = number_u256; + receive_data.counter = counter.value; + receive_data.raw_message = _message; } // todo: replicate the logic in here where sending a message must happen @@ -188,18 +191,18 @@ module oapp::oapp { // ================================================== View Functions =========================================== #[view] - public fun get_decoded_address1(): address acquires DecodedMessage { - borrow_global(OAPP_ADDRESS()).address1 + public fun get_decoded_address1(): address acquires ReceiveData { + borrow_global(OAPP_ADDRESS()).address1 } #[view] - public fun get_decoded_address2(): address acquires DecodedMessage { - borrow_global(OAPP_ADDRESS()).address2 + public fun get_decoded_address2(): address acquires ReceiveData { + borrow_global(OAPP_ADDRESS()).address2 } #[view] - public fun get_decoded_number(): u256 acquires DecodedMessage { - borrow_global(OAPP_ADDRESS()).number + public fun get_decoded_number(): u256 acquires ReceiveData { + borrow_global(OAPP_ADDRESS()).number } #[view] @@ -208,8 +211,8 @@ module oapp::oapp { } #[view] - public fun get_raw_message(): vector acquires DecodedMessage { - borrow_global(OAPP_ADDRESS()).raw_message + public fun get_raw_message(): vector acquires ReceiveData { + borrow_global(OAPP_ADDRESS()).raw_message } // ================================================== Error Codes ================================================= @@ -218,4 +221,6 @@ module oapp::oapp { const EINSUFFICIENT_BALANCE: u64 = 2; const EINVALID_HEX_CHAR: u64 = 3; const EINVALID_LENGTH: u64 = 4; + + } \ No newline at end of file diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index 246760ff8d..c53a7f87d5 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -15,16 +15,6 @@ module oapp::utils { *string::bytes(&str) } - /// Safely converts a vector of bytes to a UTF-8 string - /// Returns an Option: Some(string) if valid UTF-8, None if invalid - public fun try_bytes_to_string(bytes: vector): std::option::Option { - if (string::try_utf8(bytes) != std::option::none()) { - std::option::some(string::utf8(bytes)) - } else { - std::option::none() - } - } - /// Converts a hex string (without 0x prefix) to a vector of bytes /// Example: "48656c6c6f" -> b"Hello" /// Automatically pads odd-length strings with a leading zero @@ -32,9 +22,10 @@ module oapp::utils { let hex_bytes = string::bytes(&hex_str); let len = vector::length(hex_bytes); + // If the hex string is odd length, pad with leading zero let padded_hex = if (len % 2 == 1) { let padded = vector::empty(); - vector::push_back(&mut padded, 48); + vector::push_back(&mut padded, ASCII_ZERO); vector::append(&mut padded, *hex_bytes); padded } else { @@ -54,27 +45,6 @@ module oapp::utils { result } - /// Converts a hex string to an Aptos address - /// Supports both with and without 0x prefix - /// The hex string should represent exactly 32 bytes (64 hex characters) - public fun hex_string_to_address(hex_str: String): address { - let clean_hex = strip_hex_prefix(hex_str); - let hex_bytes = hex_string_to_bytes(clean_hex); - - let padded_bytes = pad_to_32_bytes(hex_bytes); - from_bcs::to_address(padded_bytes) - } - - /// Converts an address to a hex string with 0x prefix - public fun address_to_hex_string(addr: address): String { - let addr_bytes = bcs::to_bytes(&addr); - let hex_str = bytes_to_hex_string(addr_bytes); - let prefix = b"0x"; - let hex_bytes = string::bytes(&hex_str); - vector::append(&mut prefix, *hex_bytes); - string::utf8(prefix) - } - /// Converts bytes to a hex string (lowercase) public fun bytes_to_hex_string(bytes: vector): String { let hex_chars = b"0123456789abcdef"; @@ -94,66 +64,26 @@ module oapp::utils { string::utf8(result) } - /// Helper function to strip 0x or 0X prefix from hex string - fun strip_hex_prefix(hex_str: String): String { - let bytes = string::bytes(&hex_str); - let len = vector::length(bytes); - - if (len >= 2) { - let first = *vector::borrow(bytes, 0); - let second = *vector::borrow(bytes, 1); - - if (first == 48 && (second == 120 || second == 88)) { - let remaining = vector::empty(); - let i = 2; - while (i < len) { - vector::push_back(&mut remaining, *vector::borrow(bytes, i)); - i = i + 1; - }; - return string::utf8(remaining) - } - }; - - hex_str - } - - /// Helper function to pad bytes to 32 bytes (left-padded with zeros) - fun pad_to_32_bytes(bytes: vector): vector { - let len = vector::length(&bytes); - assert!(len <= 32, EINVALID_ADDRESS_LENGTH); - - if (len == 32) { - return bytes - }; - - let result = vector::empty(); - let padding_needed = 32 - len; - let i = 0; - - while (i < padding_needed) { - vector::push_back(&mut result, 0); - i = i + 1; - }; - - vector::append(&mut result, bytes); - result - } - /// Helper function to convert a hex character to u8 fun hex_char_to_u8(char: u8): u8 { - if (char >= 48 && char <= 57) { - char - 48 - } else if (char >= 65 && char <= 70) { - char - 65 + 10 - } else if (char >= 97 && char <= 102) { - char - 97 + 10 + if (char >= ASCII_ZERO && char <= ASCII_NINE) { + char - ASCII_ZERO + } else if (char >= ASCII_UPPERCASE_A && char <= ASCII_UPPERCASE_F) { + char - ASCII_UPPERCASE_A + 10 + } else if (char >= ASCII_LOWERCASE_A && char <= ASCII_LOWERCASE_F) { + char - ASCII_LOWERCASE_A + 10 } else { abort EINVALID_HEX_CHARACTER } } - const EINVALID_LENGTH: u64 = 1; - const EINVALID_HEX_LENGTH: u64 = 2; - const EINVALID_HEX_CHARACTER: u64 = 3; - const EINVALID_ADDRESS_LENGTH: u64 = 4; + const ASCII_ZERO: u8 = 48; + const ASCII_NINE: u8 = 57; + const ASCII_UPPERCASE_A: u8 = 65; + const ASCII_UPPERCASE_F: u8 = 70; + const ASCII_LOWERCASE_A: u8 = 97; + const ASCII_LOWERCASE_F: u8 = 102; + + const EINVALID_HEX_CHARACTER: u64 = 1; + const EINVALID_ADDRESS_LENGTH: u64 = 2; } \ No newline at end of file diff --git a/examples/oapp-aptos-move/tests/oapp_tests.move b/examples/oapp-aptos-move/tests/oapp_tests.move new file mode 100644 index 0000000000..1c376aa759 --- /dev/null +++ b/examples/oapp-aptos-move/tests/oapp_tests.move @@ -0,0 +1,42 @@ +#[test_only] +module oapp::oapp_tests { + use std::string; + use std::vector; + use aptos_std::from_bcs; + use oapp::utils::hex_string_to_bytes; + use oapp::oapp::parse_message; + + #[test] + fun test_parse_message() { + let expected_addr1 = @0x1234567890123456789012345678901234567890; + let expected_addr2 = @0x9876543210987654321098765432109876543210; + let expected_number: u256 = 123456789012345678901234567890; + + let encoded_data = x"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c23078303030303030303030303030303030303030303030303030313233343536373839303132333435363738393031323334353637383930313233343536373839303030303030303030303030303030303030303030303030303938373635343332313039383736353433323130393837363534333231303938373635343332313030303030303030303030303030303030303030303030303030303030303030303030303030303031386565393066663663333733653065653465336630616432000000000000000000000000000000000000000000000000000000000000"; + + let (actual_addr1, actual_addr2, actual_number) = parse_message(encoded_data); + + assert!(actual_addr1 == expected_addr1, 1); + assert!(actual_addr2 == expected_addr2, 2); + assert!(actual_number == expected_number, 3); + } + + #[test] + fun test_parse_message_with_prefix() { + let expected_addr1 = @0x58b730d07e98a22f2b357bee721115c986e4dc873c1884763708ee3d4006f74e; + let expected_addr2 = @0x58b730d07e98a22f2b357bee721115c986e4dc873c1884763708ee3d4006f74e; + let expected_number: u256 = 123456789012345678901234567890; + + let encoded_data = x"000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c23078353862373330643037653938613232663262333537626565373231313135633938366534646338373363313838343736333730386565336434303036663734653538623733306430376539386132326632623335376265653732313131356339383665346463383733633138383437363337303865653364343030366637346530303030303030303030303030303030303030303030303030303030303030303030303030303031386565393066663663333733653065653465336630616432000000000000000000000000000000000000000000000000000000000000"; + + let (actual_addr1, actual_addr2, actual_number) = parse_message(encoded_data); + + std::debug::print(&actual_addr1); + std::debug::print(&actual_addr2); + std::debug::print(&actual_number); + + assert!(actual_addr1 == expected_addr1, 1); + assert!(actual_addr2 == expected_addr2, 2); + assert!(actual_number == expected_number, 3); + } +} \ No newline at end of file From 2bcd7186a7391d49398eb3d47a68ce41f466f8cd Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Thu, 19 Jun 2025 19:05:43 -0700 Subject: [PATCH 03/28] Add script to analyze decoded values from Aptos OApp - Introduced a new script `analyze-decoded-values.ts` to analyze and log decoded values from the OApp on Aptos. - The script retrieves various decoded values and compares them against expected results, providing insights into potential decoding issues. - Removed the obsolete `aptos-move-get-count.ts` script to streamline the codebase. --- .../scripts/analyze-decoded-values.ts | 118 ++++++++++++++++++ .../scripts/aptos-move-get-count.ts | 47 ------- 2 files changed, 118 insertions(+), 47 deletions(-) create mode 100644 examples/oapp-aptos-move/scripts/analyze-decoded-values.ts delete mode 100644 examples/oapp-aptos-move/scripts/aptos-move-get-count.ts diff --git a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts new file mode 100644 index 0000000000..4dcffc46d1 --- /dev/null +++ b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts @@ -0,0 +1,118 @@ +import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk' +import { ethers } from 'ethers' + +/** + * Script to analyze the decoded values and understand the decoding issue + */ +async function main() { + const config = new AptosConfig({ network: Network.TESTNET }) + const aptos = new Aptos(config) + const oappAddress = '' + + console.log('Analyzing decoded values...') + console.log('---') + + // Get all decoded values + const [counterResult, decodedCounterResult, address1Result, address2Result, numberResult] = await Promise.all([ + aptos.view({ + payload: { + function: `${oappAddress}::oapp::get_counter`, + typeArguments: [], + }, + }), + aptos.view({ + payload: { + function: `${oappAddress}::oapp::get_decoded_counter`, + typeArguments: [], + }, + }), + aptos.view({ + payload: { + function: `${oappAddress}::oapp::get_decoded_address1`, + typeArguments: [], + }, + }), + aptos.view({ + payload: { + function: `${oappAddress}::oapp::get_decoded_address2`, + typeArguments: [], + }, + }), + aptos.view({ + payload: { + function: `${oappAddress}::oapp::get_decoded_number`, + typeArguments: [], + }, + }), + ]) + + console.log('Current decoded values:') + console.log('- Address 1:', address1Result[0]) + console.log('- Address 2:', address2Result[0]) + console.log('- Number:', numberResult[0]) + console.log('---') + + // What we expected to send + console.log('Expected values:') + console.log('- Address 1: 0x1234567890123456789012345678901234567890') + console.log('- Address 2: 0x9876543210987654321098765432109876543210') + console.log('- Number: 123456789012345678901234567890') + console.log('---') + + // Analyze the decoded values + console.log('Analysis:') + + // Address 1 is 0x20 = 32 in decimal + console.log('\nAddress 1 (0x20 = 32):') + console.log('- This is suspiciously the standard ABI offset value') + console.log('- In ABI encoding, dynamic data starts with a 32-byte offset pointer') + + // Address 2 is 0xc2 = 194 in decimal + console.log('\nAddress 2 (0xc2 = 194):') + console.log('- 194 bytes = 0xc2 in hex') + console.log('- This might be the length of the hex string data') + + // Let's check what our actual message would look like + const address1 = '0x1234567890123456789012345678901234567890' + const address2 = '0x9876543210987654321098765432109876543210' + const number = ethers.BigNumber.from('123456789012345678901234567890') + + // Our packed message + const packedMessage = ethers.utils.solidityPack( + ['bytes32', 'bytes32', 'uint256'], + [ethers.utils.hexZeroPad(address1, 32), ethers.utils.hexZeroPad(address2, 32), number] + ) + + console.log('\nOur packed message:') + console.log('- Length:', packedMessage.length - 2, 'characters (excluding 0x)') + console.log('- As bytes:', (packedMessage.length - 2) / 2, 'bytes') + console.log('- Hex:', packedMessage) + + // ABI encoded version + const abiEncoded = ethers.utils.defaultAbiCoder.encode(['string'], [packedMessage]) + console.log('\nABI-encoded version:') + console.log('- Total length:', abiEncoded.length - 2, 'characters') + console.log('- As bytes:', (abiEncoded.length - 2) / 2, 'bytes') + console.log('- First 64 chars:', '0x' + abiEncoded.substring(2, 66)) + console.log('- Offset (first 32 bytes):', '0x' + abiEncoded.substring(2, 66)) + console.log('- String length (next 32 bytes):', '0x' + abiEncoded.substring(66, 130)) + + // Decode the values + const offset = parseInt(abiEncoded.substring(2, 66), 16) + const strLength = parseInt(abiEncoded.substring(66, 130), 16) + console.log('- Offset value:', offset, '(0x' + offset.toString(16) + ')') + console.log('- String length value:', strLength, '(0x' + strLength.toString(16) + ')') + + console.log('\nConclusion:') + console.log('The Move contract is reading the ABI encoding metadata instead of the actual data!') + console.log("- It's reading byte 0 as address1 (getting the offset = 0x20 = 32)") + console.log("- It's reading byte 32 as address2 (getting part of the length field = 0xc2 = 194)") + console.log('- The large number is likely from reading subsequent ABI encoding bytes') + console.log('\nThe contract needs to:') + console.log('1. Skip the ABI encoding wrapper (first 64+ bytes)') + console.log('2. Parse the hex string inside') + console.log('3. Convert the hex string to bytes') + console.log('4. Then decode the addresses and number from those bytes') +} + +main().catch(console.error) diff --git a/examples/oapp-aptos-move/scripts/aptos-move-get-count.ts b/examples/oapp-aptos-move/scripts/aptos-move-get-count.ts deleted file mode 100644 index 0c662a0d9a..0000000000 --- a/examples/oapp-aptos-move/scripts/aptos-move-get-count.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * @file aptos-move-get-count.ts - * @description A script to get the current counter value from the OApp on Aptos. - * This counter increments each time a message is successfully received by the OApp, - * providing a simple way to confirm that cross-chain communication is working. - */ - -import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk' -import * as dotenv from 'dotenv' - -// Load environment variables from .env file -dotenv.config() - -// OApp configuration -const OAPP_ADDRESS = '' // Set your OApp's address on Aptos -const NETWORK = Network.TESTNET // Aptos network configuration - -// Initialize Aptos client -const aptos = new Aptos(new AptosConfig({ network: NETWORK })) - -/** - * Gets the current counter value from the OApp contract on Aptos. - * This function demonstrates how to call a view function on an Aptos contract. - */ -async function getCount() { - try { - // Call the view function to get the counter value - const counter = await aptos.view({ - payload: { - function: `${OAPP_ADDRESS}::oapp::get_counter`, - functionArguments: [], - }, - }) - - console.log('Current counter value:', counter) - return counter - } catch (error) { - console.error('Error getting counter value:', error) - throw error - } -} - -// Execute the getCount function and handle any errors -getCount().catch((error) => { - console.error(error) - process.exit(1) -}) From fea0b6358148628070d20f6532a736fbbc63718d Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Thu, 19 Jun 2025 19:19:48 -0700 Subject: [PATCH 04/28] Remove redundant logging and analysis from `analyze-decoded-values.ts` script to streamline output. The script now focuses on essential decoded values without unnecessary commentary, enhancing clarity and conciseness. --- .../scripts/analyze-decoded-values.ts | 65 +------------------ 1 file changed, 2 insertions(+), 63 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts index 4dcffc46d1..9525505633 100644 --- a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts +++ b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts @@ -1,5 +1,4 @@ import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk' -import { ethers } from 'ethers' /** * Script to analyze the decoded values and understand the decoding issue @@ -47,72 +46,12 @@ async function main() { ]) console.log('Current decoded values:') + console.log('- Counter:', counterResult[0]) + console.log('- Decoded Counter:', decodedCounterResult[0]) console.log('- Address 1:', address1Result[0]) console.log('- Address 2:', address2Result[0]) console.log('- Number:', numberResult[0]) console.log('---') - - // What we expected to send - console.log('Expected values:') - console.log('- Address 1: 0x1234567890123456789012345678901234567890') - console.log('- Address 2: 0x9876543210987654321098765432109876543210') - console.log('- Number: 123456789012345678901234567890') - console.log('---') - - // Analyze the decoded values - console.log('Analysis:') - - // Address 1 is 0x20 = 32 in decimal - console.log('\nAddress 1 (0x20 = 32):') - console.log('- This is suspiciously the standard ABI offset value') - console.log('- In ABI encoding, dynamic data starts with a 32-byte offset pointer') - - // Address 2 is 0xc2 = 194 in decimal - console.log('\nAddress 2 (0xc2 = 194):') - console.log('- 194 bytes = 0xc2 in hex') - console.log('- This might be the length of the hex string data') - - // Let's check what our actual message would look like - const address1 = '0x1234567890123456789012345678901234567890' - const address2 = '0x9876543210987654321098765432109876543210' - const number = ethers.BigNumber.from('123456789012345678901234567890') - - // Our packed message - const packedMessage = ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'uint256'], - [ethers.utils.hexZeroPad(address1, 32), ethers.utils.hexZeroPad(address2, 32), number] - ) - - console.log('\nOur packed message:') - console.log('- Length:', packedMessage.length - 2, 'characters (excluding 0x)') - console.log('- As bytes:', (packedMessage.length - 2) / 2, 'bytes') - console.log('- Hex:', packedMessage) - - // ABI encoded version - const abiEncoded = ethers.utils.defaultAbiCoder.encode(['string'], [packedMessage]) - console.log('\nABI-encoded version:') - console.log('- Total length:', abiEncoded.length - 2, 'characters') - console.log('- As bytes:', (abiEncoded.length - 2) / 2, 'bytes') - console.log('- First 64 chars:', '0x' + abiEncoded.substring(2, 66)) - console.log('- Offset (first 32 bytes):', '0x' + abiEncoded.substring(2, 66)) - console.log('- String length (next 32 bytes):', '0x' + abiEncoded.substring(66, 130)) - - // Decode the values - const offset = parseInt(abiEncoded.substring(2, 66), 16) - const strLength = parseInt(abiEncoded.substring(66, 130), 16) - console.log('- Offset value:', offset, '(0x' + offset.toString(16) + ')') - console.log('- String length value:', strLength, '(0x' + strLength.toString(16) + ')') - - console.log('\nConclusion:') - console.log('The Move contract is reading the ABI encoding metadata instead of the actual data!') - console.log("- It's reading byte 0 as address1 (getting the offset = 0x20 = 32)") - console.log("- It's reading byte 32 as address2 (getting part of the length field = 0xc2 = 194)") - console.log('- The large number is likely from reading subsequent ABI encoding bytes') - console.log('\nThe contract needs to:') - console.log('1. Skip the ABI encoding wrapper (first 64+ bytes)') - console.log('2. Parse the hex string inside') - console.log('3. Convert the hex string to bytes') - console.log('4. Then decode the addresses and number from those bytes') } main().catch(console.error) From 99d51612d8fb23e2641e907f3e1614ed668e16c3 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Thu, 19 Jun 2025 19:24:03 -0700 Subject: [PATCH 05/28] Update `analyze-decoded-values.ts` to correct function calls for retrieving decoded values. The script now accurately fetches and logs the raw message alongside other decoded values, enhancing the analysis output. --- .../scripts/analyze-decoded-values.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts index 9525505633..17e573b7d7 100644 --- a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts +++ b/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts @@ -12,7 +12,7 @@ async function main() { console.log('---') // Get all decoded values - const [counterResult, decodedCounterResult, address1Result, address2Result, numberResult] = await Promise.all([ + const [counterResult, address1Result, address2Result, numberResult, rawMessageResult] = await Promise.all([ aptos.view({ payload: { function: `${oappAddress}::oapp::get_counter`, @@ -21,25 +21,25 @@ async function main() { }), aptos.view({ payload: { - function: `${oappAddress}::oapp::get_decoded_counter`, + function: `${oappAddress}::oapp::get_decoded_address1`, typeArguments: [], }, }), aptos.view({ payload: { - function: `${oappAddress}::oapp::get_decoded_address1`, + function: `${oappAddress}::oapp::get_decoded_address2`, typeArguments: [], }, }), aptos.view({ payload: { - function: `${oappAddress}::oapp::get_decoded_address2`, + function: `${oappAddress}::oapp::get_decoded_number`, typeArguments: [], }, }), aptos.view({ payload: { - function: `${oappAddress}::oapp::get_decoded_number`, + function: `${oappAddress}::oapp::get_raw_message`, typeArguments: [], }, }), @@ -47,10 +47,10 @@ async function main() { console.log('Current decoded values:') console.log('- Counter:', counterResult[0]) - console.log('- Decoded Counter:', decodedCounterResult[0]) console.log('- Address 1:', address1Result[0]) console.log('- Address 2:', address2Result[0]) console.log('- Number:', numberResult[0]) + console.log('- Raw Message:', rawMessageResult[0]) console.log('---') } From 7cf513480b6d5236345a66b183b3cb48aa08b58f Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 11:31:55 -0700 Subject: [PATCH 06/28] Refactor `evm-send.ts` and `utils.move` for clarity and efficiency - Updated `evm-send.ts` to use a specific contract address and renamed variables for better readability. - Enhanced logging to include network information and improved message formatting. - Moved constant definitions in `utils.move` to the top for better organization and removed duplicates to streamline the code. --- examples/oapp-aptos-move/scripts/evm-send.ts | 28 +++++++++++--------- examples/oapp-aptos-move/sources/utils.move | 14 +++++----- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/evm-send.ts b/examples/oapp-aptos-move/scripts/evm-send.ts index 6f220dc681..6d7c65b405 100644 --- a/examples/oapp-aptos-move/scripts/evm-send.ts +++ b/examples/oapp-aptos-move/scripts/evm-send.ts @@ -1,10 +1,9 @@ import { ethers } from 'ethers' -import { EndpointId } from '@layerzerolabs/lz-definitions' +import { makeBytes32 } from '@layerzerolabs/devtools' +import { EndpointId, getNetworkForChainId } from '@layerzerolabs/lz-definitions' import { Options } from '@layerzerolabs/lz-v2-utilities' - import 'dotenv/config' -import { makeBytes32 } from '../../../packages/devtools/dist' // ABI for the functions we need const ABI = [ @@ -24,22 +23,22 @@ async function main() { const wallet = new ethers.Wallet(privateKey, provider) // Contract address - const contractAddress = '' + const contractAddress = '0x21E23d7740771d005c189330a0B617A3c3f4Db50' // Create contract instance const myOApp = new ethers.Contract(contractAddress, ABI, wallet) // Destination endpoint ID for Aptos/Movement - const aptosEid = EndpointId.APTOS_V2_TESTNET // or EndpointId.MOVEMENT_V2_TESTNET + const aptosMoveEid = EndpointId.APTOS_V2_TESTNET // Example addresses and number to encode const address1 = '0x1234567890123456789012345678901234567890' const address2 = '0x9876543210987654321098765432109876543210' - const number = ethers.BigNumber.from('123456789012345678901234567890') + const num = ethers.BigNumber.from('123456789012345678901234567890') const encodedMessage = ethers.utils.solidityPack( ['bytes32', 'bytes32', 'uint256'], - [makeBytes32(address1), makeBytes32(address2), ethers.BigNumber.from(number)] + [makeBytes32(address1), makeBytes32(address2), num] ) const hexString = ethers.utils.hexlify(encodedMessage) @@ -47,24 +46,27 @@ async function main() { console.log('Encoded message:', hexString) console.log('Address1:', address1) console.log('Address2:', address2) - console.log('Number:', number.toString()) + console.log('Number:', num) // Build options with gas for execution const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString() try { // Get quote - const [nativeFee] = await myOApp.quote(aptosEid, hexString, options, false) - console.log(`Quote for message: ${ethers.utils.formatEther(nativeFee)} ETH`) + const [nativeFee] = await myOApp.quote(aptosMoveEid, hexString, options, false) + console.log(`Quote for message: ${ethers.utils.formatEther(nativeFee)} native.`) // Send message - const tx = await myOApp.send(aptosEid, hexString, options, { + const tx = await myOApp.send(aptosMoveEid, hexString, options, { value: nativeFee.toString(), }) - console.log('Sending encoded message to Aptos/Movement...') + const network = getNetworkForChainId(EndpointId.APTOS_V2_TESTNET) + const networkString = network.chainName + '-' + network.env + console.log(`Sending encoded message to ${networkString}...`) + const receipt = await tx.wait() - console.log(`Transaction hash: ${receipt?.transactionHash}`) + console.log(`Transaction hash: https://layerzeroscan.com/tx/${receipt?.transactionHash}`) console.log('Message sent!') } catch (error) { console.error('Error sending message:', error) diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index c53a7f87d5..800d0b434b 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -4,6 +4,13 @@ module oapp::utils { use aptos_std::from_bcs; use aptos_std::bcs; + const ASCII_ZERO: u8 = 48; + const ASCII_NINE: u8 = 57; + const ASCII_UPPERCASE_A: u8 = 65; + const ASCII_UPPERCASE_F: u8 = 70; + const ASCII_LOWERCASE_A: u8 = 97; + const ASCII_LOWERCASE_F: u8 = 102; + /// Converts a vector of bytes to a UTF-8 string /// Will abort if the bytes are not valid UTF-8 public fun bytes_to_string(bytes: vector): String { @@ -77,13 +84,6 @@ module oapp::utils { } } - const ASCII_ZERO: u8 = 48; - const ASCII_NINE: u8 = 57; - const ASCII_UPPERCASE_A: u8 = 65; - const ASCII_UPPERCASE_F: u8 = 70; - const ASCII_LOWERCASE_A: u8 = 97; - const ASCII_LOWERCASE_F: u8 = 102; - const EINVALID_HEX_CHARACTER: u64 = 1; const EINVALID_ADDRESS_LENGTH: u64 = 2; } \ No newline at end of file From b20792dfacd96c937a5111cb38569d7f2d6b41d9 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 11:34:11 -0700 Subject: [PATCH 07/28] Add parameter encoding and decoding to Move OApp example --- .changeset/weak-crabs-chew.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/weak-crabs-chew.md diff --git a/.changeset/weak-crabs-chew.md b/.changeset/weak-crabs-chew.md new file mode 100644 index 0000000000..e6fe866469 --- /dev/null +++ b/.changeset/weak-crabs-chew.md @@ -0,0 +1,5 @@ +--- +"@layerzerolabs/oapp-aptos-example": patch +--- + +Adding param encoding and decoding to Move OApp example. From c3a6dfb066d7207a504facff1c9097b21cbd16c0 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 12:28:32 -0700 Subject: [PATCH 08/28] Update README and scripts for improved cross-chain message handling - Revised README to reflect changes in Aptos CLI version requirements and added new testing instructions for OApp contracts. - Introduced new scripts `aptos-get-received-values.ts` and `evm-get-received-message.ts` for verifying cross-chain message delivery. - Enhanced `aptos-move-send.ts` to clarify OApp address configuration and improved logging for transaction fees. - Refactored `MyOApp.sol` to streamline message sending and receiving logic, ensuring better clarity and functionality. --- examples/oapp-aptos-move/README.md | 54 ++++++- examples/oapp-aptos-move/contracts/MyOApp.sol | 134 ++++++++++++------ ...values.ts => aptos-get-received-values.ts} | 8 +- .../scripts/aptos-move-send.ts | 7 +- ...t-count.ts => evm-get-received-message.ts} | 14 +- examples/oapp-aptos-move/sources/oapp.move | 2 + 6 files changed, 158 insertions(+), 61 deletions(-) rename examples/oapp-aptos-move/scripts/{analyze-decoded-values.ts => aptos-get-received-values.ts} (83%) rename examples/oapp-aptos-move/scripts/{get-count.ts => evm-get-received-message.ts} (56%) diff --git a/examples/oapp-aptos-move/README.md b/examples/oapp-aptos-move/README.md index 6e67decd52..61571a65f0 100644 --- a/examples/oapp-aptos-move/README.md +++ b/examples/oapp-aptos-move/README.md @@ -96,15 +96,19 @@ APTOS_PRIVATE_KEY= Then run `source .env` in order for your values to be mapped. -> **Important:** If using Aptos CLI version >= 6.1.0 (required for Aptos chain), you need to uncomment the following lines in Move.toml and remove the existing AptosFramework dependency: +> **Important:** If using Aptos CLI version 3.5.0 (required for Movement chain), you need to uncomment the following lines in Move.toml and remove the existing AptosFramework dependency: > > ``` > # [dependencies.AptosFramework] -> # git = "https://github.com/aptos-labs/aptos-framework.git" -> # rev = "mainnet" -> # subdir = "aptos-framework" +> # git = "https://github.com/movementlabsxyz/aptos-core.git" +> # rev = "movement-cli-v3.5.0" +> # subdir = "aptos-move/framework/aptos-framework" > ``` +## Contracts + +The starter OApp contracts are located in `./sources/oapp.move` and `./contracts/MyOApp.sol`. Feel free to adjust these based on the needs of you application. There are also some test scripts in `./scripts/` that allow you to test sending messages between your OApps. + ## OApp Config Setup Before running the deploy and wire commands, first inside of `move.layerzero.config.ts`, set the delegate and owner address to your deployer account address. These can be changed in the future with commands shown later in this README, but for now they should be set to the address you will be running the commands from (deployer account address). @@ -207,6 +211,48 @@ If `--only-calldata ` is specified, only the calldata is generated and not pnpm run lz:sdk:move:wire --oapp-config move.layerzero.config.ts ``` +### Testing Send + +This OApp example includes several test scripts in `./scripts`. **Before running any script, you must manually update the values inside each file with your deployed contract addresses and configuration.** + +For EVM scripts (`evm-send.ts`, `evm-get-message.ts`), update the following values inside the files: + +- `contractAddress` with your deployed EVM contract address +- RPC URL in the `JsonRpcProvider` with your EVM chain's RPC endpoint +- Set `EVM_PRIVATE_KEY` environment variable for sending transactions + +For Aptos/Movement scripts (`aptos-move-send.ts`, `aptos-get-receive-values.ts`), update the following values inside the files: + +- `OAPP_ADDRESS` or `oappAddress` with your deployed Aptos/Movement contract address +- Set `APTOS_PRIVATE_KEY` and `ACCOUNT_ADDRESS` environment variables for sending transactions +- Adjust the `Network` configuration if needed (TESTNET/MAINNET) + +To test sending from your EVM deployment to your deployed OApp on Aptos or Movement, run: + +```bash +ts-node scripts/evm-send.ts +``` + +For demonstration purposes, we have added encoding and decoding of some useful parameters in `oapp.move` and `evm-send.ts`. These are for demonstration purposes only and should be adjusted based on the needs of your application. + +To confirm your values have been sent to your Aptos or Movement OApp, run: + +```bash +ts-node scripts/aptos-get-received-values.ts +``` + +To send a test value from your Aptos/Movement OApp to your EVM OApp, run: + +```bash +ts-node scripts/aptos-move-send.ts +``` + +To check that your message has been registered in your EVM OApp, run: + +```bash +ts-node scripts/evm-get-received-message.ts +``` + ### Transferring Ownership of your Move OApp There are three steps to transferring ownership of your Move OApp: diff --git a/examples/oapp-aptos-move/contracts/MyOApp.sol b/examples/oapp-aptos-move/contracts/MyOApp.sol index 316a91aa33..800c3e03f7 100644 --- a/examples/oapp-aptos-move/contracts/MyOApp.sol +++ b/examples/oapp-aptos-move/contracts/MyOApp.sol @@ -1,34 +1,28 @@ -// SPDX-License-Identifier: MIT - +// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.22; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; -import { OApp, MessagingFee, Origin } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; -import { MessagingReceipt } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol"; +import { OApp, Origin, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; import { OAppOptionsType3 } from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OAppOptionsType3.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; contract MyOApp is OApp, OAppOptionsType3 { - constructor(address _endpoint, address _delegate) OApp(_endpoint, _delegate) Ownable(_delegate) {} + /// @notice Last string received from any remote chain + string public lastMessage; - string public data = "Nothing received yet."; - uint256 public counter = 0; + /// @notice Msg type for sending a string, for use in OAppOptionsType3 as an enforced option + uint16 public constant SEND = 1; - /** - * @notice Sends a message from the source chain to a destination chain. - * @param _dstEid The endpoint ID of the destination chain. - * @param _message The message string to be sent. - * @param _options Additional options for message execution. - * @dev Encodes the message as bytes and sends it using the `_lzSend` internal function. - * @return receipt A `MessagingReceipt` struct containing details of the message sent. - */ - function send( - uint32 _dstEid, - string memory _message, - bytes calldata _options - ) external payable returns (MessagingReceipt memory receipt) { - bytes memory _payload = abi.encode(_message); - receipt = _lzSend(_dstEid, _payload, _options, MessagingFee(msg.value, 0), payable(msg.sender)); - } + /// @notice Initialize with Endpoint V2 and owner address + /// @param _endpoint The local chain's LayerZero Endpoint V2 address + /// @param _owner The address permitted to configure this OApp + constructor(address _endpoint, address _owner) OApp(_endpoint, _owner) Ownable(_owner) {} + + // ────────────────────────────────────────────────────────────────────────────── + // 0. (Optional) Quote business logic + // + // Example: Get a quote from the Endpoint for a cost estimate of sending a message. + // Replace this to mirror your own send business logic. + // ────────────────────────────────────────────────────────────────────────────── /** * @notice Quotes the gas needed to pay for the full omnichain transaction in native gas or ZRO token. @@ -38,35 +32,89 @@ contract MyOApp is OApp, OAppOptionsType3 { * @param _payInLzToken Whether to return fee in ZRO token. * @return fee A `MessagingFee` struct containing the calculated gas fee in either the native token or ZRO token. */ - function quote( + function quoteSend( uint32 _dstEid, - string memory _message, - bytes memory _options, + string calldata _message, + bytes calldata _options, bool _payInLzToken ) public view returns (MessagingFee memory fee) { - bytes memory payload = abi.encode(_message); - fee = _quote(_dstEid, payload, _options, _payInLzToken); + bytes memory _message = abi.encode(_message); + // combineOptions (from OAppOptionsType3) merges enforced options set by the contract owner + // with any additional execution options provided by the caller + fee = _quote(_dstEid, _message, combineOptions(_dstEid, SEND, _options), _payInLzToken); } - /** - * @dev Internal function override to handle incoming messages from another chain. - * @dev _origin A struct containing information about the message sender. - * @dev _guid A unique global packet identifier for the message. - * @dev payload The encoded message payload being received. - * - * @dev The following params are unused in the current implementation of the OApp. - * @dev _executor The address of the Executor responsible for processing the message. - * @dev _extraData Arbitrary data appended by the Executor to the message. - * - * Increments the counter. - */ + // ────────────────────────────────────────────────────────────────────────────── + // 1. Send business logic + // + // Example: send a simple string to a remote chain. Replace this with your + // own state-update logic, then encode whatever data your application needs. + // ────────────────────────────────────────────────────────────────────────────── + + /// @notice Send a string to a remote OApp on another chain + /// @param _dstEid Destination Endpoint ID (uint32) + /// @param _message The string to send + /// @param _options Execution options for gas on the destination (bytes) + function send(uint32 _dstEid, string calldata _message, bytes calldata _options) external payable { + // 1. (Optional) Update any local state here. + // e.g., record that a message was "sent": + // sentCount += 1; + + // 2. Encode any data structures you wish to send into bytes + // You can use abi.encode, abi.encodePacked, or directly splice bytes + // if you know the format of your data structures + bytes memory _message = abi.encode(_message); + + // 3. Call OAppSender._lzSend to package and dispatch the cross-chain message + // - _dstEid: remote chain's Endpoint ID + // - _message: ABI-encoded string + // - _options: combined execution options (enforced + caller-provided) + // - MessagingFee(msg.value, 0): pay all gas as native token; no ZRO + // - payable(msg.sender): refund excess gas to caller + // + // combineOptions (from OAppOptionsType3) merges enforced options set by the contract owner + // with any additional execution options provided by the caller + _lzSend( + _dstEid, + _message, + combineOptions(_dstEid, SEND, _options), + MessagingFee(msg.value, 0), + payable(msg.sender) + ); + } + + // ────────────────────────────────────────────────────────────────────────────── + // 2. Receive business logic + // + // Override _lzReceive to decode the incoming bytes and apply your logic. + // The base OAppReceiver.lzReceive ensures: + // • Only the LayerZero Endpoint can call this method + // • The sender is a registered peer (peers[srcEid] == origin.sender) + // ────────────────────────────────────────────────────────────────────────────── + + /// @notice Invoked by OAppReceiver when EndpointV2.lzReceive is called + /// @dev _origin Metadata (source chain, sender address, nonce) + /// @dev _guid Global unique ID for tracking this message + /// @param _message ABI-encoded bytes (the string we sent earlier) + /// @dev _executor Executor address that delivered the message + /// @dev _extraData Additional data from the Executor (unused here) function _lzReceive( Origin calldata /*_origin*/, bytes32 /*_guid*/, - bytes calldata /*payload*/, + bytes calldata _message, address /*_executor*/, bytes calldata /*_extraData*/ ) internal override { - counter += 1; + // 1. Decode the incoming bytes into a string + // You can use abi.decode, abi.decodePacked, or directly splice bytes + // if you know the format of your data structures + string memory _message = abi.decode(_message, (string)); + + // 2. Apply your custom logic. In this example, store it in `lastMessage`. + lastMessage = _message; + + // 3. (Optional) Trigger further on-chain actions. + // e.g., emit an event, mint tokens, call another contract, etc. + // emit MessageReceived(_origin.srcEid, _message); } } diff --git a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts b/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts similarity index 83% rename from examples/oapp-aptos-move/scripts/analyze-decoded-values.ts rename to examples/oapp-aptos-move/scripts/aptos-get-received-values.ts index 17e573b7d7..46a2571be3 100644 --- a/examples/oapp-aptos-move/scripts/analyze-decoded-values.ts +++ b/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts @@ -1,17 +1,18 @@ import { Aptos, AptosConfig, Network } from '@aptos-labs/ts-sdk' /** - * Script to analyze the decoded values and understand the decoding issue + * A utility script to verify cross-chain message delivery by checking the last received values. + * The values are updated each time a message is successfully received by the OApp, + * providing a simple way to confirm that cross-chain communication is working as expected. */ async function main() { const config = new AptosConfig({ network: Network.TESTNET }) const aptos = new Aptos(config) const oappAddress = '' - console.log('Analyzing decoded values...') + console.log('Fetching receive values...') console.log('---') - // Get all decoded values const [counterResult, address1Result, address2Result, numberResult, rawMessageResult] = await Promise.all([ aptos.view({ payload: { @@ -45,7 +46,6 @@ async function main() { }), ]) - console.log('Current decoded values:') console.log('- Counter:', counterResult[0]) console.log('- Address 1:', address1Result[0]) console.log('- Address 2:', address2Result[0]) diff --git a/examples/oapp-aptos-move/scripts/aptos-move-send.ts b/examples/oapp-aptos-move/scripts/aptos-move-send.ts index feb14c6372..db47edbdb1 100644 --- a/examples/oapp-aptos-move/scripts/aptos-move-send.ts +++ b/examples/oapp-aptos-move/scripts/aptos-move-send.ts @@ -38,10 +38,11 @@ const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY const ACCOUNT_ADDRESS = process.env.ACCOUNT_ADDRESS // OApp configuration -const OAPP_ADDRESS = '' // Set your OApp's address on Aptos -const REMOTE_EID = EndpointId.BSC_V2_TESTNET // Destination chain endpoint ID +const OAPP_ADDRESS = '' // Set your OApp's address on Aptos or Movement const NETWORK = Network.TESTNET // Aptos network configuration +const REMOTE_EID = EndpointId.BSC_V2_TESTNET // Destination chain endpoint ID + // Initialize Aptos account and client const privateKey = PrivateKey.formatPrivateKey(APTOS_PRIVATE_KEY, PrivateKeyVariants.Ed25519) const signerAccount = Account.fromPrivateKey({ @@ -75,7 +76,7 @@ async function send() { // Log fee details console.log('Quote details:') console.log('Native fee (octas):', quote[0]) - console.log('Native fee (APT):', Number(quote[0]) / 100000000) + console.log('Native fee:', Number(quote[0]) / 100000000) console.log('ZRO fee:', quote[1]) // Verify account has sufficient balance diff --git a/examples/oapp-aptos-move/scripts/get-count.ts b/examples/oapp-aptos-move/scripts/evm-get-received-message.ts similarity index 56% rename from examples/oapp-aptos-move/scripts/get-count.ts rename to examples/oapp-aptos-move/scripts/evm-get-received-message.ts index 9e836f03c2..37175cc604 100644 --- a/examples/oapp-aptos-move/scripts/get-count.ts +++ b/examples/oapp-aptos-move/scripts/evm-get-received-message.ts @@ -1,21 +1,21 @@ import { ethers } from 'ethers' /** - * A utility script to verify cross-chain message delivery by checking the counter value. - * The counter increments each time a message is successfully received by the OApp, + * A utility script to verify cross-chain message delivery by checking the last received message. + * The lastMessage is updated each time a message is successfully received by the OApp, * providing a simple way to confirm that cross-chain communication is working as expected. */ async function main() { - const abi = ['function counter() view returns (uint256)'] + const abi = ['function lastMessage() view returns (string)'] - const contractAddress = 'your-contract-address' + const contractAddress = 'your-EVM-OApp-contract-address' - const provider = new ethers.providers.JsonRpcProvider('your-rpc-url') + const provider = new ethers.providers.JsonRpcProvider('your-EVM-chain-rpc-url') const contract = new ethers.Contract(contractAddress, abi, provider) - const counter = await contract.counter() - console.log('Counter value:', counter.toString()) + const lastMessage = await contract.lastMessage() + console.log('Last received message:', lastMessage) } main() diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index e195852374..eb0e5687c7 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -113,6 +113,8 @@ module oapp::oapp { receive_data.number = number_u256; receive_data.counter = counter.value; receive_data.raw_message = _message; + + // Optionally, you can add any additional logic here to handle the received message. } // todo: replicate the logic in here where sending a message must happen From a101b8315ffa2937797d45ec386a6e35bfa57ef8 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 12:29:13 -0700 Subject: [PATCH 09/28] lock file --- pnpm-lock.yaml | 86 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 69 insertions(+), 17 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5a72c359f2..a62c1c6c70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -623,6 +623,9 @@ importers: '@jest/globals': specifier: ^29.7.0 version: 29.7.0 + '@layerzerolabs/devtools': + specifier: ~1.0.0 + version: link:../../packages/devtools '@layerzerolabs/devtools-extensible-cli': specifier: ^0.0.7 version: link:../../packages/devtools-extensible-cli @@ -3805,7 +3808,7 @@ importers: version: 2.16.2 jest: specifier: ^29.6.2 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tsup: specifier: ~8.0.1 version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3) @@ -3990,7 +3993,7 @@ importers: version: 29.5.12 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tslib: specifier: ~2.6.2 version: 2.6.3 @@ -4518,13 +4521,13 @@ importers: version: 3.0.75 '@layerzerolabs/lz-solana-sdk-v2': specifier: ^3.0.0 - version: 3.0.0(typescript@5.5.3) + version: 3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': specifier: ^3.0.75 version: 3.0.75 '@layerzerolabs/oft-v2-solana-sdk': specifier: ^3.0.38 - version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/protocol-devtools': specifier: ~2.0.0 version: link:../protocol-devtools @@ -5145,13 +5148,13 @@ importers: version: 3.0.75 '@layerzerolabs/lz-solana-sdk-v2': specifier: ^3.0.59 - version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': specifier: ^3.0.75 version: 3.0.75 '@layerzerolabs/oft-v2-solana-sdk': specifier: ^3.0.59 - version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + version: 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/protocol-devtools': specifier: ~2.0.0 version: link:../protocol-devtools @@ -5256,7 +5259,7 @@ importers: version: 12.6.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + version: 29.7.0(@types/node@18.18.14) tsup: specifier: ^8.0.1 version: 8.0.1(@swc/core@1.4.0)(typescript@5.5.3) @@ -9967,7 +9970,7 @@ packages: - utf-8-validate dev: false - /@layerzerolabs/lz-solana-sdk-v2@3.0.0(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-sPvLXeQUO9QLpjOuWE7V+V8yfoI4E/NBYsH9lO2aPx0LYkQa+88ACgPq43B/zFROUD8238WuSb+doGrn3PKtJQ==} dependencies: '@ethersproject/abi': 5.7.0 @@ -9998,7 +10001,7 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/lz-solana-sdk-v2@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-zyuqBYxaVtSa+STdcbO/uzzQV2kyxmBX3flNbvOq7kS2QHBivuaYd/XDbNLE54/egQ63yMtFcilqwy3VzNpclw==} dependencies: '@layerzerolabs/lz-corekit-solana': 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) @@ -10106,7 +10109,7 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/lz-solana-sdk-v2@3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/lz-solana-sdk-v2@3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-FfZLkFHIOPIFLkevQD0QSrfVR2UgREG6YF6oSR92XPViuVpAq0LAyCTXQi915bdiSdQ/Mwd+9eU/9ZPlk9f6sA==} dependencies: '@layerzerolabs/lz-corekit-solana': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) @@ -10434,12 +10437,12 @@ packages: '@layerzerolabs/lz-definitions': 3.0.75 dev: true - /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/oft-v2-solana-sdk@3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-P06/a5+ixph0u1AQkDZ0P0oFaIAdfGPl/UezMfWXUpiWLth428RT0rrMR6qI7z6X1uxqlUFNIotz2ET1fyFcpQ==} dependencies: '@ethersproject/bytes': 5.7.0 '@layerzerolabs/lz-foundation': 3.0.38(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) - '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': 3.0.86 '@metaplex-foundation/beet': 0.7.2 '@metaplex-foundation/beet-solana': 0.4.1 @@ -10465,12 +10468,12 @@ packages: - utf-8-validate dev: true - /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3): + /@layerzerolabs/oft-v2-solana-sdk@3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3): resolution: {integrity: sha512-ijbvj6/Gc4O4WLHfqnrBuKUtIhpaYws/ORj2apZFT1RKSgAX8CCJ9aZmn0ClamEG98i+PpXoroPPU46LMOMZyA==} dependencies: '@ethersproject/bytes': 5.7.0 '@layerzerolabs/lz-foundation': 3.0.66(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) - '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(typescript@5.5.3) + '@layerzerolabs/lz-solana-sdk-v2': 3.0.86(@swc/core@1.4.0)(@types/node@18.18.14)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.3) '@layerzerolabs/lz-v2-utilities': 3.0.86 '@metaplex-foundation/beet': 0.7.2 '@metaplex-foundation/beet-solana': 0.4.1 @@ -10630,7 +10633,7 @@ packages: '@ton/crypto': 3.3.0 '@ton/sandbox': 0.22.0(@ton/core@0.59.0)(@ton/crypto@3.3.0) '@ton/test-utils': 0.4.2(@jest/globals@29.7.0)(@ton/core@0.59.0)(chai@4.5.0) - axios: 1.8.4 + axios: 1.7.9 dataloader: 2.2.2 symbol.inspect: 1.0.1 teslabot: 1.5.0 @@ -10658,7 +10661,7 @@ packages: '@ton/crypto': 3.3.0 '@ton/sandbox': 0.22.0(@ton/core@0.59.0)(@ton/crypto@3.3.0) '@ton/test-utils': 0.4.2(@ton/core@0.59.0)(chai@4.4.1) - axios: 1.8.4 + axios: 1.7.9 dataloader: 2.2.2 symbol.inspect: 1.0.1 teslabot: 1.5.0 @@ -10715,7 +10718,7 @@ packages: '@ton/crypto': 3.3.0 '@ton/sandbox': 0.22.0(@ton/core@0.59.0)(@ton/crypto@3.3.0) '@ton/test-utils': 0.4.2(@jest/globals@29.7.0)(@ton/core@0.59.0)(chai@4.5.0) - axios: 1.8.4 + axios: 1.7.9 dataloader: 2.2.2 symbol.inspect: 1.0.1 teslabot: 1.5.0 @@ -19261,6 +19264,34 @@ packages: - babel-plugin-macros - supports-color + /jest-cli@29.7.0(@types/node@18.18.14): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.18.14)(ts-node@10.9.2) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest-cli@29.7.0(@types/node@18.18.14)(ts-node@10.9.2): resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -19673,6 +19704,27 @@ packages: merge-stream: 2.0.0 supports-color: 8.1.1 + /jest@29.7.0(@types/node@18.18.14): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.18.14) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /jest@29.7.0(@types/node@18.18.14)(ts-node@10.9.2): resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} From e45c4d6b9745e4fed4dafc8ad6cbd0384728528b Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 12:52:52 -0700 Subject: [PATCH 10/28] Fix environment variable names and update OApp address in `aptos-move-send.ts` - Corrected the environment variable name from `ACCOUNT_ADDRESS` to `APTOS_ACCOUNT_ADDRESS` for consistency. - Updated the OApp address to a specific value for clarity in configuration. - Ensured all references to the account address are aligned with the new variable name. --- examples/oapp-aptos-move/scripts/aptos-move-send.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/aptos-move-send.ts b/examples/oapp-aptos-move/scripts/aptos-move-send.ts index db47edbdb1..9bd6ab9d44 100644 --- a/examples/oapp-aptos-move/scripts/aptos-move-send.ts +++ b/examples/oapp-aptos-move/scripts/aptos-move-send.ts @@ -29,13 +29,13 @@ import { Options } from '@layerzerolabs/lz-v2-utilities' dotenv.config() // Validate required environment variables -if (!process.env.APTOS_PRIVATE_KEY || !process.env.ACCOUNT_ADDRESS) { - throw new Error('Please set APTOS_PRIVATE_KEY and ACCOUNT_ADDRESS in your .env file') +if (!process.env.APTOS_PRIVATE_KEY || !process.env.APTOS_ACCOUNT_ADDRESS) { + throw new Error('Please set APTOS_PRIVATE_KEY and APTOS_ACCOUNT_ADDRESS in your .env file') } // Configuration constants const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY -const ACCOUNT_ADDRESS = process.env.ACCOUNT_ADDRESS +const APTOS_ACCOUNT_ADDRESS = process.env.APTOS_ACCOUNT_ADDRESS // OApp configuration const OAPP_ADDRESS = '' // Set your OApp's address on Aptos or Movement @@ -47,7 +47,7 @@ const REMOTE_EID = EndpointId.BSC_V2_TESTNET // Destination chain endpoint ID const privateKey = PrivateKey.formatPrivateKey(APTOS_PRIVATE_KEY, PrivateKeyVariants.Ed25519) const signerAccount = Account.fromPrivateKey({ privateKey: new Ed25519PrivateKey(privateKey), - address: ACCOUNT_ADDRESS, + address: APTOS_ACCOUNT_ADDRESS, }) const aptos = new Aptos(new AptosConfig({ network: NETWORK })) @@ -80,7 +80,7 @@ async function send() { console.log('ZRO fee:', quote[1]) // Verify account has sufficient balance - const balance = await aptos.account.getAccountAPTAmount({ accountAddress: ACCOUNT_ADDRESS }) + const balance = await aptos.account.getAccountAPTAmount({ accountAddress: APTOS_ACCOUNT_ADDRESS }) console.log('Account balance:', Number(balance) / 100000000, 'APT') if (Number(balance) < Number(quote[0])) { @@ -97,7 +97,7 @@ async function send() { // Create the transaction const transaction: SimpleTransaction = await aptos.transaction.build.simple({ - sender: ACCOUNT_ADDRESS, + sender: APTOS_ACCOUNT_ADDRESS, data: payload, options: { maxGasAmount: 100000, From 2eb12f8e75068e5c4e5f123c06904f22398da44a Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 13:17:56 -0700 Subject: [PATCH 11/28] smol improvements --- examples/oapp-aptos-move/move.layerzero.config.ts | 4 ++-- .../scripts/aptos-get-received-values.ts | 2 +- examples/oapp-aptos-move/scripts/evm-send.ts | 10 +++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/oapp-aptos-move/move.layerzero.config.ts b/examples/oapp-aptos-move/move.layerzero.config.ts index 61bd999ad3..01667ab44a 100644 --- a/examples/oapp-aptos-move/move.layerzero.config.ts +++ b/examples/oapp-aptos-move/move.layerzero.config.ts @@ -74,7 +74,7 @@ const config: OAppOmniGraphHardhat = { }, ulnConfig: { // The number of block confirmations to wait on Aptos before emitting the message from the source chain. - confirmations: BigInt(260), + confirmations: BigInt(10), // The address of the DVNs you will pay to verify a sent message on the source chain. // The destination tx will wait until ALL `requiredDVNs` verify the message. requiredDVNs: ['0x756f8ab056688d22687740f4a9aeec3b361170b28d08b719e28c4d38eed1043e'], @@ -143,7 +143,7 @@ const config: OAppOmniGraphHardhat = { }, receiveConfig: { ulnConfig: { - confirmations: BigInt(260), + confirmations: BigInt(10), requiredDVNs: ['0x0eE552262f7B562eFcED6DD4A7e2878AB897d405'], optionalDVNThreshold: 0, }, diff --git a/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts b/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts index 46a2571be3..940dd11b99 100644 --- a/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts +++ b/examples/oapp-aptos-move/scripts/aptos-get-received-values.ts @@ -16,7 +16,7 @@ async function main() { const [counterResult, address1Result, address2Result, numberResult, rawMessageResult] = await Promise.all([ aptos.view({ payload: { - function: `${oappAddress}::oapp::get_counter`, + function: `${oappAddress}::oapp::get_counter_value`, typeArguments: [], }, }), diff --git a/examples/oapp-aptos-move/scripts/evm-send.ts b/examples/oapp-aptos-move/scripts/evm-send.ts index 6d7c65b405..f943e1fb37 100644 --- a/examples/oapp-aptos-move/scripts/evm-send.ts +++ b/examples/oapp-aptos-move/scripts/evm-send.ts @@ -5,9 +5,9 @@ import { EndpointId, getNetworkForChainId } from '@layerzerolabs/lz-definitions' import { Options } from '@layerzerolabs/lz-v2-utilities' import 'dotenv/config' -// ABI for the functions we need +// ABI for the functions we need - updated to match the Solidity contract const ABI = [ - 'function quote(uint32 _dstEid, string memory _message, bytes memory _options, bool _payInLzToken) public view returns (tuple(uint256 nativeFee, uint256 lzTokenFee))', + 'function quoteSend(uint32 _dstEid, string memory _message, bytes memory _options, bool _payInLzToken) public view returns (tuple(uint256 nativeFee, uint256 lzTokenFee))', 'function send(uint32 _dstEid, string memory _message, bytes calldata _options) external payable returns (tuple(bytes32 guid, uint256 nonce, bytes32 messageId))', ] @@ -22,8 +22,8 @@ async function main() { const provider = new ethers.providers.JsonRpcProvider('https://data-seed-prebsc-1-s1.binance.org:8545') const wallet = new ethers.Wallet(privateKey, provider) - // Contract address - const contractAddress = '0x21E23d7740771d005c189330a0B617A3c3f4Db50' + // Contract address - update this to your deployed contract address + const contractAddress = '' // Create contract instance const myOApp = new ethers.Contract(contractAddress, ABI, wallet) @@ -53,7 +53,7 @@ async function main() { try { // Get quote - const [nativeFee] = await myOApp.quote(aptosMoveEid, hexString, options, false) + const [nativeFee] = await myOApp.quoteSend(aptosMoveEid, hexString, options, false) console.log(`Quote for message: ${ethers.utils.formatEther(nativeFee)} native.`) // Send message From d1ae147264e606da385a3874ac06ad25ac6dc27d Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 13:18:11 -0700 Subject: [PATCH 12/28] pnpm lock --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4710dab1cb..f266807afc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -624,7 +624,7 @@ importers: specifier: ^29.7.0 version: 29.7.0 '@layerzerolabs/devtools': - specifier: ~1.0.0 + specifier: ^1.0.0 version: link:../../packages/devtools '@layerzerolabs/devtools-extensible-cli': specifier: ^0.0.7 From 51efec8cf32cbcb203682bc44f469402c9be2283 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 13:19:10 -0700 Subject: [PATCH 13/28] Remove redundant test case for message sending functionality in `MyOApp.test.ts` to streamline the test suite. --- .../test/hardhat/MyOApp.test.ts | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/examples/oapp-aptos-move/test/hardhat/MyOApp.test.ts b/examples/oapp-aptos-move/test/hardhat/MyOApp.test.ts index e4550147ce..014254c782 100644 --- a/examples/oapp-aptos-move/test/hardhat/MyOApp.test.ts +++ b/examples/oapp-aptos-move/test/hardhat/MyOApp.test.ts @@ -1,10 +1,7 @@ import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' import { Contract, ContractFactory } from 'ethers' import { deployments, ethers } from 'hardhat' -import { Options } from '@layerzerolabs/lz-v2-utilities' - describe('MyOApp Test', function () { // Constant representing a mock Endpoint ID for testing purposes const eidA = 1 @@ -61,24 +58,4 @@ describe('MyOApp Test', function () { await myOAppA.connect(ownerA).setPeer(eidB, ethers.utils.zeroPad(myOAppB.address, 32)) await myOAppB.connect(ownerB).setPeer(eidA, ethers.utils.zeroPad(myOAppA.address, 32)) }) - - // A test case to verify message sending functionality - it('should increment counter when receiving a message', async function () { - // Assert initial counter state in both MyOApp instances - expect((await myOAppA.counter()).toNumber()).to.equal(0) - expect((await myOAppB.counter()).toNumber()).to.equal(0) - - const options = Options.newOptions().addExecutorLzReceiveOption(200000, 0).toHex().toString() - - // Define native fee and quote for the message send operation - let nativeFee = 0 - ;[nativeFee] = await myOAppA.quote(eidB, 'Test message.', options, false) - - // Execute send operation from myOAppA - await myOAppA.send(eidB, 'Test message.', options, { value: nativeFee.toString() }) - - // Assert the counter was incremented in the receiving app - expect((await myOAppA.counter()).toNumber()).to.equal(0) - expect((await myOAppB.counter()).toNumber()).to.equal(1) - }) }) From d584ae1c754a2cec79c2478ec6f21247323fa04e Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Fri, 20 Jun 2025 13:32:49 -0700 Subject: [PATCH 14/28] Enhance cross-chain message handling in `aptos-move-send.ts` and `evm-get-received-message.ts` - Added ABI encoding for messages in `aptos-move-send.ts` to ensure compatibility with Solidity contracts. - Improved logging for message sending and transaction links. - Updated placeholder addresses in both scripts for clarity and consistency. - Set a specific RPC URL in `evm-get-received-message.ts` for better connectivity. --- .../oapp-aptos-move/scripts/aptos-move-send.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/aptos-move-send.ts b/examples/oapp-aptos-move/scripts/aptos-move-send.ts index 9bd6ab9d44..3c9a1d5daa 100644 --- a/examples/oapp-aptos-move/scripts/aptos-move-send.ts +++ b/examples/oapp-aptos-move/scripts/aptos-move-send.ts @@ -21,6 +21,7 @@ import { SimpleTransaction, } from '@aptos-labs/ts-sdk' import * as dotenv from 'dotenv' +import { ethers } from 'ethers' import { EndpointId } from '@layerzerolabs/lz-definitions' import { Options } from '@layerzerolabs/lz-v2-utilities' @@ -38,7 +39,7 @@ const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY const APTOS_ACCOUNT_ADDRESS = process.env.APTOS_ACCOUNT_ADDRESS // OApp configuration -const OAPP_ADDRESS = '' // Set your OApp's address on Aptos or Movement +const OAPP_ADDRESS = '' const NETWORK = Network.TESTNET // Aptos network configuration const REMOTE_EID = EndpointId.BSC_V2_TESTNET // Destination chain endpoint ID @@ -60,9 +61,16 @@ async function send() { const options = Options.newOptions().addExecutorLzReceiveOption(BigInt(30000)) const extraOptions = options.toBytes() - // Prepare the message + // Prepare the message - ABI encode it to match Solidity contract expectations const message = 'Hello, EVM!' - const messageBytes = new TextEncoder().encode(message) + console.log('Sending message:', message) + + // ABI encode the string to match what the Solidity contract expects + const abiEncodedMessage = ethers.utils.defaultAbiCoder.encode(['string'], [message]) + console.log('ABI encoded message:', abiEncodedMessage) + + // Convert to bytes array for Aptos + const messageBytes = ethers.utils.arrayify(abiEncodedMessage) const messageArray = new Uint8Array(messageBytes) // Get fee quote for the cross-chain message @@ -115,7 +123,7 @@ async function send() { transactionHash: signedTransaction.hash, }) - console.log(`Transaction hash: ${executedTransaction.hash}`) + console.log(`Transaction link: https://layerzeroscan.com/tx/${executedTransaction.hash}`) console.log('Message sent!') } From 40527e326bc2260da7eaac19243dd38c9d8593ab Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Sun, 22 Jun 2025 17:24:23 -0700 Subject: [PATCH 15/28] Update `evm-send.ts` to use empty placeholders for addresses and number - Changed example addresses and number to empty strings for user-defined input. - Improved clarity by indicating that users should fill in the required values for Aptos/Movement. --- examples/oapp-aptos-move/scripts/evm-send.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/evm-send.ts b/examples/oapp-aptos-move/scripts/evm-send.ts index f943e1fb37..d1000404cb 100644 --- a/examples/oapp-aptos-move/scripts/evm-send.ts +++ b/examples/oapp-aptos-move/scripts/evm-send.ts @@ -31,10 +31,10 @@ async function main() { // Destination endpoint ID for Aptos/Movement const aptosMoveEid = EndpointId.APTOS_V2_TESTNET - // Example addresses and number to encode - const address1 = '0x1234567890123456789012345678901234567890' - const address2 = '0x9876543210987654321098765432109876543210' - const num = ethers.BigNumber.from('123456789012345678901234567890') + // Fill in the addresses and number to send to Aptos/Movement + const address1 = '' + const address2 = '' + const num = ethers.BigNumber.from('') const encodedMessage = ethers.utils.solidityPack( ['bytes32', 'bytes32', 'uint256'], From 9d32c14ab597da5b42ccdaf6fd27ab0fb35ec338 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 11:40:56 -0700 Subject: [PATCH 16/28] Remove unused dependency `@layerzerolabs/devtools` from `pnpm-lock.yaml` to streamline project dependencies. --- pnpm-lock.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f266807afc..eebbf288d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -623,9 +623,6 @@ importers: '@jest/globals': specifier: ^29.7.0 version: 29.7.0 - '@layerzerolabs/devtools': - specifier: ^1.0.0 - version: link:../../packages/devtools '@layerzerolabs/devtools-extensible-cli': specifier: ^0.0.7 version: link:../../packages/devtools-extensible-cli From 599dc1e8a89e739dcbfe9b2ec6c98f2bc38e60ef Mon Sep 17 00:00:00 2001 From: Alexander Liteplo <65365446+AlexanderLiteplo@users.noreply.github.com> Date: Mon, 23 Jun 2025 15:21:50 -0700 Subject: [PATCH 17/28] Update examples/oapp-aptos-move/contracts/MyOApp.sol Co-authored-by: abaltes-lz --- examples/oapp-aptos-move/contracts/MyOApp.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/oapp-aptos-move/contracts/MyOApp.sol b/examples/oapp-aptos-move/contracts/MyOApp.sol index 800c3e03f7..e2a87b12df 100644 --- a/examples/oapp-aptos-move/contracts/MyOApp.sol +++ b/examples/oapp-aptos-move/contracts/MyOApp.sol @@ -97,7 +97,7 @@ contract MyOApp is OApp, OAppOptionsType3 { /// @dev _guid Global unique ID for tracking this message /// @param _message ABI-encoded bytes (the string we sent earlier) /// @dev _executor Executor address that delivered the message - /// @dev _extraData Additional data from the Executor (unused here) + /// @dev _extraData Additional data from the Executor (unused by the LayerZero executor) function _lzReceive( Origin calldata /*_origin*/, bytes32 /*_guid*/, From 08b767c8673d6a3578f1598caa5738ca5da08f11 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 16:04:48 -0700 Subject: [PATCH 18/28] correcting licenses --- .changeset/purple-apes-yell.md | 21 +++++++++++++++++++ .../contracts/MyMintBurnOFTAdapter.sol | 2 +- .../mint-burn-oft-adapter/contracts/MyOFT.sol | 2 +- .../contracts/MyNativeOFTAdapter.sol | 2 +- .../native-oft-adapter/contracts/MyOFT.sol | 2 +- examples/oapp-aptos-move/contracts/MyOApp.sol | 2 +- examples/oapp/contracts/MyOApp.sol | 5 +++-- .../contracts/MyOFT.sol | 2 +- .../oft-adapter-initia/contracts/MyOFT.sol | 2 +- examples/oft-adapter/contracts/MyOFT.sol | 2 +- .../oft-adapter/contracts/MyOFTAdapter.sol | 2 +- .../oft-alt/contracts/MyOFTAdapterAlt.sol | 2 +- examples/oft-alt/contracts/MyOFTAlt.sol | 2 +- examples/oft-aptos-move/contracts/MyOFT.sol | 2 +- .../contracts/MyHyperLiquidComposer.sol | 2 +- examples/oft-hyperliquid/contracts/MyOFT.sol | 2 +- examples/oft-initia/contracts/MyOFT.sol | 2 +- examples/oft-solana/contracts/MyOFT.sol | 2 +- .../contracts/MyOFTAdapterFeeUpgradeable.sol | 2 +- .../contracts/MyOFTAdapterUpgradeable.sol | 2 +- .../contracts/MyOFTFeeUpgradeable.sol | 2 +- .../contracts/MyOFTUpgradeable.sol | 2 +- examples/oft/contracts/MyOFT.sol | 2 +- examples/omni-call/contracts/OmniCall.sol | 2 +- .../contracts/OmniCallMsgCodecLib.sol | 2 +- .../contracts/interfaces/IOmniCall.sol | 2 +- .../onft721-zksync/contracts/MyONFT721.sol | 2 +- examples/onft721/contracts/MyONFT721.sol | 2 +- .../onft721/contracts/MyONFT721Adapter.sol | 2 +- 29 files changed, 51 insertions(+), 29 deletions(-) create mode 100644 .changeset/purple-apes-yell.md diff --git a/.changeset/purple-apes-yell.md b/.changeset/purple-apes-yell.md new file mode 100644 index 0000000000..fb02533462 --- /dev/null +++ b/.changeset/purple-apes-yell.md @@ -0,0 +1,21 @@ +--- +"@layerzerolabs/oft-adapter-aptos-move-example": patch +"@layerzerolabs/mint-burn-oft-adapter-example": patch +"@layerzerolabs/native-oft-adapter-example": patch +"@layerzerolabs/oft-adapter-initia-example": patch +"@layerzerolabs/oapp-aptos-example": patch +"@layerzerolabs/oft-hyperliquid-example": patch +"@layerzerolabs/oft-upgradeable-example": patch +"@layerzerolabs/oft-aptos-move-example": patch +"@layerzerolabs/onft721-zksync-example": patch +"@layerzerolabs/oft-adapter-example": patch +"@layerzerolabs/oft-initia-example": patch +"@layerzerolabs/oft-solana-example": patch +"@layerzerolabs/omni-call-example": patch +"@layerzerolabs/oft-alt-example": patch +"@layerzerolabs/onft721-example": patch +"@layerzerolabs/oapp-example": patch +"@layerzerolabs/oft-example": patch +--- + +Updating license from UNLICENSED to MIT. diff --git a/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol b/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol index b03d501dd4..e7ad46b686 100644 --- a/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol +++ b/examples/mint-burn-oft-adapter/contracts/MyMintBurnOFTAdapter.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/mint-burn-oft-adapter/contracts/MyOFT.sol b/examples/mint-burn-oft-adapter/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/mint-burn-oft-adapter/contracts/MyOFT.sol +++ b/examples/mint-burn-oft-adapter/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/native-oft-adapter/contracts/MyNativeOFTAdapter.sol b/examples/native-oft-adapter/contracts/MyNativeOFTAdapter.sol index 03a1b7cca4..1cd21fdb49 100644 --- a/examples/native-oft-adapter/contracts/MyNativeOFTAdapter.sol +++ b/examples/native-oft-adapter/contracts/MyNativeOFTAdapter.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/native-oft-adapter/contracts/MyOFT.sol b/examples/native-oft-adapter/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/native-oft-adapter/contracts/MyOFT.sol +++ b/examples/native-oft-adapter/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oapp-aptos-move/contracts/MyOApp.sol b/examples/oapp-aptos-move/contracts/MyOApp.sol index 800c3e03f7..6b90666d66 100644 --- a/examples/oapp-aptos-move/contracts/MyOApp.sol +++ b/examples/oapp-aptos-move/contracts/MyOApp.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OApp, Origin, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; diff --git a/examples/oapp/contracts/MyOApp.sol b/examples/oapp/contracts/MyOApp.sol index dc4f0beb38..dc1096af73 100644 --- a/examples/oapp/contracts/MyOApp.sol +++ b/examples/oapp/contracts/MyOApp.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OApp, Origin, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol"; @@ -9,7 +9,8 @@ contract MyOApp is OApp, OAppOptionsType3 { /// @notice Last string received from any remote chain string public lastMessage; - /// @notice Msg type for sending a string, for use in OAppOptionsType3 as an enforced option + /// @notice The only Message Type in use for this OApp: sending an arbitrary string. + /// Different message types can be assigned different enforced options per destination endpoint ID. uint16 public constant SEND = 1; /// @notice Initialize with Endpoint V2 and owner address diff --git a/examples/oft-adapter-aptos-move/contracts/MyOFT.sol b/examples/oft-adapter-aptos-move/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-adapter-aptos-move/contracts/MyOFT.sol +++ b/examples/oft-adapter-aptos-move/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-adapter-initia/contracts/MyOFT.sol b/examples/oft-adapter-initia/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-adapter-initia/contracts/MyOFT.sol +++ b/examples/oft-adapter-initia/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-adapter/contracts/MyOFT.sol b/examples/oft-adapter/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-adapter/contracts/MyOFT.sol +++ b/examples/oft-adapter/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-adapter/contracts/MyOFTAdapter.sol b/examples/oft-adapter/contracts/MyOFTAdapter.sol index f87f5e5119..365e740015 100644 --- a/examples/oft-adapter/contracts/MyOFTAdapter.sol +++ b/examples/oft-adapter/contracts/MyOFTAdapter.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-alt/contracts/MyOFTAdapterAlt.sol b/examples/oft-alt/contracts/MyOFTAdapterAlt.sol index 5444b0ef11..f4b3eac059 100644 --- a/examples/oft-alt/contracts/MyOFTAdapterAlt.sol +++ b/examples/oft-alt/contracts/MyOFTAdapterAlt.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-alt/contracts/MyOFTAlt.sol b/examples/oft-alt/contracts/MyOFTAlt.sol index 8e6b6b2f99..ca924f85fa 100644 --- a/examples/oft-alt/contracts/MyOFTAlt.sol +++ b/examples/oft-alt/contracts/MyOFTAlt.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-aptos-move/contracts/MyOFT.sol b/examples/oft-aptos-move/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-aptos-move/contracts/MyOFT.sol +++ b/examples/oft-aptos-move/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-hyperliquid/contracts/MyHyperLiquidComposer.sol b/examples/oft-hyperliquid/contracts/MyHyperLiquidComposer.sol index c2b3fee16a..1b2c221c58 100644 --- a/examples/oft-hyperliquid/contracts/MyHyperLiquidComposer.sol +++ b/examples/oft-hyperliquid/contracts/MyHyperLiquidComposer.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { HyperLiquidComposer } from "@layerzerolabs/hyperliquid-composer/contracts/HyperLiquidComposer.sol"; diff --git a/examples/oft-hyperliquid/contracts/MyOFT.sol b/examples/oft-hyperliquid/contracts/MyOFT.sol index f0c28c8c5f..f6e1adc8c5 100644 --- a/examples/oft-hyperliquid/contracts/MyOFT.sol +++ b/examples/oft-hyperliquid/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-initia/contracts/MyOFT.sol b/examples/oft-initia/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-initia/contracts/MyOFT.sol +++ b/examples/oft-initia/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-solana/contracts/MyOFT.sol b/examples/oft-solana/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft-solana/contracts/MyOFT.sol +++ b/examples/oft-solana/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/oft-upgradeable/contracts/MyOFTAdapterFeeUpgradeable.sol b/examples/oft-upgradeable/contracts/MyOFTAdapterFeeUpgradeable.sol index 155d360aaf..202a44c1c3 100644 --- a/examples/oft-upgradeable/contracts/MyOFTAdapterFeeUpgradeable.sol +++ b/examples/oft-upgradeable/contracts/MyOFTAdapterFeeUpgradeable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OFTAdapterFeeUpgradeable } from "@layerzerolabs/oft-evm-upgradeable/contracts/oft/OFTAdapterFeeUpgradeable.sol"; diff --git a/examples/oft-upgradeable/contracts/MyOFTAdapterUpgradeable.sol b/examples/oft-upgradeable/contracts/MyOFTAdapterUpgradeable.sol index c4d3646145..9c34be2145 100644 --- a/examples/oft-upgradeable/contracts/MyOFTAdapterUpgradeable.sol +++ b/examples/oft-upgradeable/contracts/MyOFTAdapterUpgradeable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OFTAdapterUpgradeable } from "@layerzerolabs/oft-evm-upgradeable/contracts/oft/OFTAdapterUpgradeable.sol"; diff --git a/examples/oft-upgradeable/contracts/MyOFTFeeUpgradeable.sol b/examples/oft-upgradeable/contracts/MyOFTFeeUpgradeable.sol index f40720fb89..88acb96341 100644 --- a/examples/oft-upgradeable/contracts/MyOFTFeeUpgradeable.sol +++ b/examples/oft-upgradeable/contracts/MyOFTFeeUpgradeable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OFTFeeUpgradeable } from "@layerzerolabs/oft-evm-upgradeable/contracts/oft/OFTFeeUpgradeable.sol"; diff --git a/examples/oft-upgradeable/contracts/MyOFTUpgradeable.sol b/examples/oft-upgradeable/contracts/MyOFTUpgradeable.sol index 1eeaf8a1ec..6e7883cde5 100644 --- a/examples/oft-upgradeable/contracts/MyOFTUpgradeable.sol +++ b/examples/oft-upgradeable/contracts/MyOFTUpgradeable.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { OFTUpgradeable } from "@layerzerolabs/oft-evm-upgradeable/contracts/oft/OFTUpgradeable.sol"; diff --git a/examples/oft/contracts/MyOFT.sol b/examples/oft/contracts/MyOFT.sol index f8bc7b47f6..fc0694fc77 100644 --- a/examples/oft/contracts/MyOFT.sol +++ b/examples/oft/contracts/MyOFT.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; diff --git a/examples/omni-call/contracts/OmniCall.sol b/examples/omni-call/contracts/OmniCall.sol index faf0a4813b..5ea5c6f36b 100644 --- a/examples/omni-call/contracts/OmniCall.sol +++ b/examples/omni-call/contracts/OmniCall.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// ----------------------------------------------------------------------- diff --git a/examples/omni-call/contracts/OmniCallMsgCodecLib.sol b/examples/omni-call/contracts/OmniCallMsgCodecLib.sol index 2295bdabcb..2f34f0e24b 100644 --- a/examples/omni-call/contracts/OmniCallMsgCodecLib.sol +++ b/examples/omni-call/contracts/OmniCallMsgCodecLib.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// ----------------------------------------------------------------------- diff --git a/examples/omni-call/contracts/interfaces/IOmniCall.sol b/examples/omni-call/contracts/interfaces/IOmniCall.sol index 3bfc56d097..ff3a53e4bc 100644 --- a/examples/omni-call/contracts/interfaces/IOmniCall.sol +++ b/examples/omni-call/contracts/interfaces/IOmniCall.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity 0.8.22; /// ----------------------------------------------------------------------- diff --git a/examples/onft721-zksync/contracts/MyONFT721.sol b/examples/onft721-zksync/contracts/MyONFT721.sol index 64350c3c69..a668960733 100644 --- a/examples/onft721-zksync/contracts/MyONFT721.sol +++ b/examples/onft721-zksync/contracts/MyONFT721.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { ONFT721 } from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721.sol"; diff --git a/examples/onft721/contracts/MyONFT721.sol b/examples/onft721/contracts/MyONFT721.sol index 64350c3c69..a668960733 100644 --- a/examples/onft721/contracts/MyONFT721.sol +++ b/examples/onft721/contracts/MyONFT721.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { ONFT721 } from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721.sol"; diff --git a/examples/onft721/contracts/MyONFT721Adapter.sol b/examples/onft721/contracts/MyONFT721Adapter.sol index 5a14f57761..1a31ce20d9 100644 --- a/examples/onft721/contracts/MyONFT721Adapter.sol +++ b/examples/onft721/contracts/MyONFT721Adapter.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.22; import { ONFT721Adapter } from "@layerzerolabs/onft-evm/contracts/onft721/ONFT721Adapter.sol"; From 5a33bf411e4e47a27ff77036b6215a1a986b4295 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 16:10:27 -0700 Subject: [PATCH 19/28] Update comments in MyOApp.sol for clarity on unused parameters and message types - Clarified the description of the unused _extraData parameter in the _lzReceive function. - Updated the comment for the SEND constant to specify its role in sending arbitrary strings and the potential for different message types with enforced options. --- examples/oapp-aptos-move/contracts/MyOApp.sol | 3 ++- examples/oapp/contracts/MyOApp.sol | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/oapp-aptos-move/contracts/MyOApp.sol b/examples/oapp-aptos-move/contracts/MyOApp.sol index 9c0a835ab4..646f29971b 100644 --- a/examples/oapp-aptos-move/contracts/MyOApp.sol +++ b/examples/oapp-aptos-move/contracts/MyOApp.sol @@ -9,7 +9,8 @@ contract MyOApp is OApp, OAppOptionsType3 { /// @notice Last string received from any remote chain string public lastMessage; - /// @notice Msg type for sending a string, for use in OAppOptionsType3 as an enforced option + /// @notice The only Message Type in use for this OApp: sending an arbitrary string. + /// Different message types can be assigned different enforced options per destination endpoint ID. uint16 public constant SEND = 1; /// @notice Initialize with Endpoint V2 and owner address diff --git a/examples/oapp/contracts/MyOApp.sol b/examples/oapp/contracts/MyOApp.sol index dc1096af73..29618723fa 100644 --- a/examples/oapp/contracts/MyOApp.sol +++ b/examples/oapp/contracts/MyOApp.sol @@ -98,7 +98,7 @@ contract MyOApp is OApp, OAppOptionsType3 { /// @dev _guid Global unique ID for tracking this message /// @param _message ABI-encoded bytes (the string we sent earlier) /// @dev _executor Executor address that delivered the message - /// @dev _extraData Additional data from the Executor (unused here) + /// @dev _extraData Additional data from the Executor (unused by the LayerZero executor) function _lzReceive( Origin calldata /*_origin*/, bytes32 /*_guid*/, From c07d1289df0cd9fd99b8dfba6958d7b0f722ef22 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 16:13:54 -0700 Subject: [PATCH 20/28] Refactor environment variable validation in `aptos-move-send.ts` - Simplified the validation of required environment variables by using constants for `APTOS_PRIVATE_KEY` and `APTOS_ACCOUNT_ADDRESS`. - Improved code readability and maintainability by reducing redundancy in the validation logic. --- examples/oapp-aptos-move/scripts/aptos-move-send.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/oapp-aptos-move/scripts/aptos-move-send.ts b/examples/oapp-aptos-move/scripts/aptos-move-send.ts index 3c9a1d5daa..19a380782b 100644 --- a/examples/oapp-aptos-move/scripts/aptos-move-send.ts +++ b/examples/oapp-aptos-move/scripts/aptos-move-send.ts @@ -29,15 +29,15 @@ import { Options } from '@layerzerolabs/lz-v2-utilities' // Load environment variables from .env file dotenv.config() +// Configuration constants +const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY || '' +const APTOS_ACCOUNT_ADDRESS = process.env.APTOS_ACCOUNT_ADDRESS || '' + // Validate required environment variables -if (!process.env.APTOS_PRIVATE_KEY || !process.env.APTOS_ACCOUNT_ADDRESS) { +if (!APTOS_PRIVATE_KEY || !APTOS_ACCOUNT_ADDRESS) { throw new Error('Please set APTOS_PRIVATE_KEY and APTOS_ACCOUNT_ADDRESS in your .env file') } -// Configuration constants -const APTOS_PRIVATE_KEY = process.env.APTOS_PRIVATE_KEY -const APTOS_ACCOUNT_ADDRESS = process.env.APTOS_ACCOUNT_ADDRESS - // OApp configuration const OAPP_ADDRESS = '' const NETWORK = Network.TESTNET // Aptos network configuration From e3c2b7d4c13aa2c72a40a175c2c40a26267177fd Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 16:23:26 -0700 Subject: [PATCH 21/28] Refactor `oapp.move` and `utils.move` for code simplification and clarity - Removed the unused `bytes_to_string` function from `utils.move` to streamline the module. - Updated `hex_string_to_bytes` to enforce even-length hex strings, improving error handling. - Simplified the logic in `hex_string_to_bytes` and `bytes_to_hex_string` for better readability. --- examples/oapp-aptos-move/sources/oapp.move | 2 +- examples/oapp-aptos-move/sources/utils.move | 31 ++++++--------------- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index eb0e5687c7..aa376b2bff 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -21,7 +21,7 @@ module oapp::oapp { use endpoint_v2_common::serde; use oapp::oapp_core::{combine_options, lz_quote, lz_send, refund_fees}; use oapp::oapp_store::OAPP_ADDRESS; - use oapp::utils::{bytes_to_string, hex_string_to_bytes}; + use oapp::utils::hex_string_to_bytes; friend oapp::oapp_receive; friend oapp::oapp_compose; diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index 800d0b434b..facc50d41a 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -11,12 +11,6 @@ module oapp::utils { const ASCII_LOWERCASE_A: u8 = 97; const ASCII_LOWERCASE_F: u8 = 102; - /// Converts a vector of bytes to a UTF-8 string - /// Will abort if the bytes are not valid UTF-8 - public fun bytes_to_string(bytes: vector): String { - string::utf8(bytes) - } - /// Converts a UTF-8 string to a vector of bytes public fun string_to_bytes(str: String): vector { *string::bytes(&str) @@ -24,27 +18,19 @@ module oapp::utils { /// Converts a hex string (without 0x prefix) to a vector of bytes /// Example: "48656c6c6f" -> b"Hello" - /// Automatically pads odd-length strings with a leading zero + /// Aborts if the hex string has odd length (not properly formatted) public fun hex_string_to_bytes(hex_str: String): vector { let hex_bytes = string::bytes(&hex_str); let len = vector::length(hex_bytes); - // If the hex string is odd length, pad with leading zero - let padded_hex = if (len % 2 == 1) { - let padded = vector::empty(); - vector::push_back(&mut padded, ASCII_ZERO); - vector::append(&mut padded, *hex_bytes); - padded - } else { - *hex_bytes - }; + // Assert that hex string has even length + assert!(len % 2 == 0, EINVALID_HEX_LENGTH); - let padded_len = vector::length(&padded_hex); - let result = vector::empty(); + let result = vector[]; let i = 0; - while (i < padded_len) { - let high_nibble = hex_char_to_u8(*vector::borrow(&padded_hex, i)); - let low_nibble = hex_char_to_u8(*vector::borrow(&padded_hex, i + 1)); + while (i < len) { + let high_nibble = hex_char_to_u8(*vector::borrow(hex_bytes, i)); + let low_nibble = hex_char_to_u8(*vector::borrow(hex_bytes, i + 1)); let byte_val = (high_nibble << 4) | low_nibble; vector::push_back(&mut result, byte_val); i = i + 2; @@ -55,7 +41,7 @@ module oapp::utils { /// Converts bytes to a hex string (lowercase) public fun bytes_to_hex_string(bytes: vector): String { let hex_chars = b"0123456789abcdef"; - let result = vector::empty(); + let result = vector[]; let i = 0; let len = vector::length(&bytes); @@ -86,4 +72,5 @@ module oapp::utils { const EINVALID_HEX_CHARACTER: u64 = 1; const EINVALID_ADDRESS_LENGTH: u64 = 2; + const EINVALID_HEX_LENGTH: u64 = 3; } \ No newline at end of file From f5f2ce7f8fac92345d944e30cd2586eb0a3b96b2 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 16:30:50 -0700 Subject: [PATCH 22/28] Add message parsing function and improve error code organization in oapp.move and utils.move - Introduced `parse_message` function in `oapp.move` to extract addresses and a u256 number from a cross-chain message. - Enhanced documentation for the new function to clarify the message format. - Organized error codes in `utils.move` for better readability and maintainability. --- examples/oapp-aptos-move/sources/oapp.move | 5 +++++ examples/oapp-aptos-move/sources/utils.move | 2 ++ 2 files changed, 7 insertions(+) diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index aa376b2bff..7a9aaf0f94 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -51,6 +51,11 @@ module oapp::oapp { }); } + /// Parses a cross-chain message to extract two addresses and a u256 number + /// The message format contains hex-encoded data representing: + /// - First 32 bytes: address1 + /// - Next 32 bytes: address2 + /// - Remaining bytes: u256 number public fun parse_message(message: vector): (address, address, u256) { let string_length = ( (*vector::borrow(&message, 60) as u64) << 24 | diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index facc50d41a..ffeb23fb5d 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -70,6 +70,8 @@ module oapp::utils { } } + // ================================================== Error Codes ================================================= + const EINVALID_HEX_CHARACTER: u64 = 1; const EINVALID_ADDRESS_LENGTH: u64 = 2; const EINVALID_HEX_LENGTH: u64 = 3; From 7a3a2a09d5ce43dc5a6fdfd925016436c60f9053 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 17:10:23 -0700 Subject: [PATCH 23/28] Refactor address and number extraction in `oapp.move` for improved clarity and efficiency - Replaced manual byte slicing and conversion logic with utility functions `extract_address` and `extract_u256` for better readability and maintainability. - Simplified the extraction process by utilizing a mutable position tracker. --- examples/oapp-aptos-move/sources/oapp.move | 30 +++++----------------- 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index 7a9aaf0f94..479557c669 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -15,10 +15,9 @@ module oapp::oapp { #[test_only] use std::account; - use endpoint_v2_common::bytes32::{Self, Bytes32}; use endpoint_v2_common::native_token; - use endpoint_v2_common::serde; + use endpoint_v2_common::serde::{extract_address, extract_u256}; use oapp::oapp_core::{combine_options, lz_quote, lz_send, refund_fees}; use oapp::oapp_store::OAPP_ADDRESS; use oapp::utils::hex_string_to_bytes; @@ -70,28 +69,11 @@ module oapp::oapp { let hex_bytes = vector::slice(&string_bytes, 2, vector::length(&string_bytes)); let hex_content = hex_string_to_bytes(string::utf8(hex_bytes)); - - let addr1_bytes = vector::slice(&hex_content, 0, 32); - let decoded_addr1 = from_bcs::to_address(addr1_bytes); - - let addr2_bytes = vector::slice(&hex_content, 32, 64); - let decoded_addr2 = from_bcs::to_address(addr2_bytes); - - let hex_content_len = vector::length(&hex_content); - let number_bytes = if (hex_content_len >= 96) { - vector::slice(&hex_content, 64, 96) - } else { - vector::slice(&hex_content, 64, hex_content_len) - }; - - let number_u256 = 0u256; - let j = 0; - let num_bytes_len = vector::length(&number_bytes); - while (j < num_bytes_len) { - let byte_val = *vector::borrow(&number_bytes, j); - number_u256 = (number_u256 << 8) + (byte_val as u256); - j = j + 1; - }; + + let pos = 0; + let decoded_addr1 = extract_address(&hex_content, &mut pos); + let decoded_addr2 = extract_address(&hex_content, &mut pos); + let number_u256 = extract_u256(&hex_content, &mut pos); (decoded_addr1, decoded_addr2, number_u256) } From b9a90a5b975124b2b47b458f22ff950e979cadb6 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 17:31:15 -0700 Subject: [PATCH 24/28] Refactor `parse_message` function in `oapp.move` for enhanced efficiency and readability - Replaced manual byte extraction with a mutable position tracker and utility function for extracting u256 values. - Streamlined the logic for slicing the message vector, improving overall clarity and maintainability. --- examples/oapp-aptos-move/sources/oapp.move | 15 +++++---------- examples/oapp-aptos-move/sources/utils.move | 5 ----- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index 479557c669..c0b29adf3d 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -56,16 +56,11 @@ module oapp::oapp { /// - Next 32 bytes: address2 /// - Remaining bytes: u256 number public fun parse_message(message: vector): (address, address, u256) { - let string_length = ( - (*vector::borrow(&message, 60) as u64) << 24 | - (*vector::borrow(&message, 61) as u64) << 16 | - (*vector::borrow(&message, 62) as u64) << 8 | - (*vector::borrow(&message, 63) as u64) - ); - - let string_start = 64; - let string_end = string_start + string_length; - let string_bytes = vector::slice(&message, string_start, string_end); + let string_len_offset = 0; + let offset = extract_u256(&message, &mut string_len_offset); + string_len_offset = (offset as u64); + let string_length = extract_u256(&message, &mut string_len_offset); + let string_bytes = vector::slice(&message, string_len_offset, string_len_offset + (string_length as u64)); let hex_bytes = vector::slice(&string_bytes, 2, vector::length(&string_bytes)); let hex_content = hex_string_to_bytes(string::utf8(hex_bytes)); diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index ffeb23fb5d..01251ef838 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -11,11 +11,6 @@ module oapp::utils { const ASCII_LOWERCASE_A: u8 = 97; const ASCII_LOWERCASE_F: u8 = 102; - /// Converts a UTF-8 string to a vector of bytes - public fun string_to_bytes(str: String): vector { - *string::bytes(&str) - } - /// Converts a hex string (without 0x prefix) to a vector of bytes /// Example: "48656c6c6f" -> b"Hello" /// Aborts if the hex string has odd length (not properly formatted) From 3aba4bcfb8efebfa1c0065643b93f86a4f684c8c Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 17:37:37 -0700 Subject: [PATCH 25/28] pnpm lock --- pnpm-lock.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eebbf288d9..f266807afc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -623,6 +623,9 @@ importers: '@jest/globals': specifier: ^29.7.0 version: 29.7.0 + '@layerzerolabs/devtools': + specifier: ^1.0.0 + version: link:../../packages/devtools '@layerzerolabs/devtools-extensible-cli': specifier: ^0.0.7 version: link:../../packages/devtools-extensible-cli From 379fafcf41f0bbd9f0e6869e328ca934f36e7fd7 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 17:41:43 -0700 Subject: [PATCH 26/28] package.json adding devtools --- examples/oapp-aptos-move/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/oapp-aptos-move/package.json b/examples/oapp-aptos-move/package.json index 9a2fc5318f..913554887d 100644 --- a/examples/oapp-aptos-move/package.json +++ b/examples/oapp-aptos-move/package.json @@ -35,6 +35,7 @@ "@aptos-labs/ts-sdk": "^1.33.1", "@babel/core": "^7.23.9", "@jest/globals": "^29.7.0", + "@layerzerolabs/devtools": "^1.0.0", "@layerzerolabs/devtools-extensible-cli": "^0.0.7", "@layerzerolabs/devtools-move": "^1.0.9", "@layerzerolabs/eslint-config-next": "~2.3.39", From f01ca5e35117f3338dca0c0da87694d6086f9773 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Mon, 23 Jun 2025 18:00:41 -0700 Subject: [PATCH 27/28] Refactor imports in `oapp.move`, `utils.move`, and `oapp_tests.move` for code clarity - Removed unused imports from `oapp.move` and `utils.move` to streamline the modules. - Cleaned up the test file `oapp_tests.move` by eliminating unnecessary dependencies, enhancing readability. --- examples/oapp-aptos-move/sources/oapp.move | 5 +---- examples/oapp-aptos-move/sources/utils.move | 2 -- examples/oapp-aptos-move/tests/oapp_tests.move | 5 +---- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/examples/oapp-aptos-move/sources/oapp.move b/examples/oapp-aptos-move/sources/oapp.move index c0b29adf3d..78e4aad87e 100644 --- a/examples/oapp-aptos-move/sources/oapp.move +++ b/examples/oapp-aptos-move/sources/oapp.move @@ -11,11 +11,8 @@ module oapp::oapp { use std::signer::address_of; use std::string; use std::vector; - use aptos_std::from_bcs; - #[test_only] - use std::account; - use endpoint_v2_common::bytes32::{Self, Bytes32}; + use endpoint_v2_common::bytes32::Bytes32; use endpoint_v2_common::native_token; use endpoint_v2_common::serde::{extract_address, extract_u256}; use oapp::oapp_core::{combine_options, lz_quote, lz_send, refund_fees}; diff --git a/examples/oapp-aptos-move/sources/utils.move b/examples/oapp-aptos-move/sources/utils.move index 01251ef838..0bbef65b5c 100644 --- a/examples/oapp-aptos-move/sources/utils.move +++ b/examples/oapp-aptos-move/sources/utils.move @@ -1,8 +1,6 @@ module oapp::utils { use std::string::{Self, String}; use std::vector; - use aptos_std::from_bcs; - use aptos_std::bcs; const ASCII_ZERO: u8 = 48; const ASCII_NINE: u8 = 57; diff --git a/examples/oapp-aptos-move/tests/oapp_tests.move b/examples/oapp-aptos-move/tests/oapp_tests.move index 1c376aa759..821fc15a1f 100644 --- a/examples/oapp-aptos-move/tests/oapp_tests.move +++ b/examples/oapp-aptos-move/tests/oapp_tests.move @@ -1,9 +1,6 @@ #[test_only] module oapp::oapp_tests { - use std::string; - use std::vector; - use aptos_std::from_bcs; - use oapp::utils::hex_string_to_bytes; + use oapp::oapp::parse_message; #[test] From 4069f91474a4314416c634d1c59ff9032393ce32 Mon Sep 17 00:00:00 2001 From: alexanderliteplo Date: Tue, 24 Jun 2025 14:06:46 -0700 Subject: [PATCH 28/28] Refactor `MyOApp.sol` and related scripts for improved data handling and clarity - Updated `MyOApp.sol` to replace the `lastMessage` string with `address1`, `address2`, and `num` for better data representation. - Modified scripts to align with the new data structure, ensuring proper encoding and retrieval of the updated fields. - Enhanced comments in utility scripts to clarify the purpose of the changes and improve overall readability. --- examples/oapp-aptos-move/contracts/MyOApp.sol | 8 +- examples/oapp-aptos-move/package.json | 1 - .../scripts/aptos-move-send.ts | 11 +- .../scripts/evm-get-received-message.ts | 20 +++- examples/oapp-aptos-move/scripts/evm-send.ts | 14 +-- pnpm-lock.yaml | 107 +++++++++++++++--- 6 files changed, 126 insertions(+), 35 deletions(-) diff --git a/examples/oapp-aptos-move/contracts/MyOApp.sol b/examples/oapp-aptos-move/contracts/MyOApp.sol index 646f29971b..8feca07bf7 100644 --- a/examples/oapp-aptos-move/contracts/MyOApp.sol +++ b/examples/oapp-aptos-move/contracts/MyOApp.sol @@ -7,7 +7,9 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; contract MyOApp is OApp, OAppOptionsType3 { /// @notice Last string received from any remote chain - string public lastMessage; + address public address1; + address public address2; + uint256 public num; /// @notice The only Message Type in use for this OApp: sending an arbitrary string. /// Different message types can be assigned different enforced options per destination endpoint ID. @@ -109,11 +111,9 @@ contract MyOApp is OApp, OAppOptionsType3 { // 1. Decode the incoming bytes into a string // You can use abi.decode, abi.decodePacked, or directly splice bytes // if you know the format of your data structures - string memory _message = abi.decode(_message, (string)); + (address1, address2, num) = abi.decode(_message, (address, address, uint256)); // 2. Apply your custom logic. In this example, store it in `lastMessage`. - lastMessage = _message; - // 3. (Optional) Trigger further on-chain actions. // e.g., emit an event, mint tokens, call another contract, etc. // emit MessageReceived(_origin.srcEid, _message); diff --git a/examples/oapp-aptos-move/package.json b/examples/oapp-aptos-move/package.json index 913554887d..9a2fc5318f 100644 --- a/examples/oapp-aptos-move/package.json +++ b/examples/oapp-aptos-move/package.json @@ -35,7 +35,6 @@ "@aptos-labs/ts-sdk": "^1.33.1", "@babel/core": "^7.23.9", "@jest/globals": "^29.7.0", - "@layerzerolabs/devtools": "^1.0.0", "@layerzerolabs/devtools-extensible-cli": "^0.0.7", "@layerzerolabs/devtools-move": "^1.0.9", "@layerzerolabs/eslint-config-next": "~2.3.39", diff --git a/examples/oapp-aptos-move/scripts/aptos-move-send.ts b/examples/oapp-aptos-move/scripts/aptos-move-send.ts index 19a380782b..e7218e69c6 100644 --- a/examples/oapp-aptos-move/scripts/aptos-move-send.ts +++ b/examples/oapp-aptos-move/scripts/aptos-move-send.ts @@ -62,11 +62,16 @@ async function send() { const extraOptions = options.toBytes() // Prepare the message - ABI encode it to match Solidity contract expectations - const message = 'Hello, EVM!' - console.log('Sending message:', message) + const address1 = '0x' // EVM 20 byte address + const address2 = '0x' // EVM 20 byte address + const num = ethers.BigNumber.from('0') + console.log('Sending message:', address1, address2, num) // ABI encode the string to match what the Solidity contract expects - const abiEncodedMessage = ethers.utils.defaultAbiCoder.encode(['string'], [message]) + const abiEncodedMessage = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'uint256'], + [address1, address2, num] + ) console.log('ABI encoded message:', abiEncodedMessage) // Convert to bytes array for Aptos diff --git a/examples/oapp-aptos-move/scripts/evm-get-received-message.ts b/examples/oapp-aptos-move/scripts/evm-get-received-message.ts index 37175cc604..2988a6aec5 100644 --- a/examples/oapp-aptos-move/scripts/evm-get-received-message.ts +++ b/examples/oapp-aptos-move/scripts/evm-get-received-message.ts @@ -1,12 +1,16 @@ import { ethers } from 'ethers' /** - * A utility script to verify cross-chain message delivery by checking the last received message. - * The lastMessage is updated each time a message is successfully received by the OApp, + * A utility script to verify cross-chain message delivery by checking the last received data. + * The address1, address2, and num fields are updated each time a message is successfully received by the OApp, * providing a simple way to confirm that cross-chain communication is working as expected. */ async function main() { - const abi = ['function lastMessage() view returns (string)'] + const abi = [ + 'function address1() view returns (address)', + 'function address2() view returns (address)', + 'function num() view returns (uint256)', + ] const contractAddress = 'your-EVM-OApp-contract-address' @@ -14,8 +18,14 @@ async function main() { const contract = new ethers.Contract(contractAddress, abi, provider) - const lastMessage = await contract.lastMessage() - console.log('Last received message:', lastMessage) + const address1 = await contract.address1() + const address2 = await contract.address2() + const num = await contract.num() + + console.log('Last received data:') + console.log(' Address 1:', address1) + console.log(' Address 2:', address2) + console.log(' Number:', num.toString()) } main() diff --git a/examples/oapp-aptos-move/scripts/evm-send.ts b/examples/oapp-aptos-move/scripts/evm-send.ts index d1000404cb..22a52df73f 100644 --- a/examples/oapp-aptos-move/scripts/evm-send.ts +++ b/examples/oapp-aptos-move/scripts/evm-send.ts @@ -1,6 +1,5 @@ import { ethers } from 'ethers' -import { makeBytes32 } from '@layerzerolabs/devtools' import { EndpointId, getNetworkForChainId } from '@layerzerolabs/lz-definitions' import { Options } from '@layerzerolabs/lz-v2-utilities' import 'dotenv/config' @@ -32,14 +31,11 @@ async function main() { const aptosMoveEid = EndpointId.APTOS_V2_TESTNET // Fill in the addresses and number to send to Aptos/Movement - const address1 = '' - const address2 = '' - const num = ethers.BigNumber.from('') - - const encodedMessage = ethers.utils.solidityPack( - ['bytes32', 'bytes32', 'uint256'], - [makeBytes32(address1), makeBytes32(address2), num] - ) + const address1 = '0x' // Aptos 32 byte address + const address2 = '0x' // Aptos 32 byte address + const num = ethers.BigNumber.from('0') + + const encodedMessage = ethers.utils.solidityPack(['bytes32', 'bytes32', 'uint256'], [address1, address2, num]) const hexString = ethers.utils.hexlify(encodedMessage) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f266807afc..384443c4bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,7 +42,7 @@ importers: version: 17.1.0(eslint-plugin-import@2.29.1)(eslint-plugin-n@16.6.2)(eslint-plugin-promise@6.1.1)(eslint@8.57.1) eslint-plugin-import: specifier: ^2.29.1 - version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + version: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1) eslint-plugin-jest: specifier: ^27.6.3 version: 27.6.3(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1)(typescript@5.5.3) @@ -623,9 +623,6 @@ importers: '@jest/globals': specifier: ^29.7.0 version: 29.7.0 - '@layerzerolabs/devtools': - specifier: ^1.0.0 - version: link:../../packages/devtools '@layerzerolabs/devtools-extensible-cli': specifier: ^0.0.7 version: link:../../packages/devtools-extensible-cli @@ -9063,13 +9060,13 @@ packages: resolution: {integrity: sha512-WlBSy47LGPILdrNgzPiRtQf/hAY62IN37ncUsQwcr8T7cyX1HZREx2qljuXpvduLDAKn5otsm0XIqHuCRUHEFg==} dependencies: '@typescript-eslint/eslint-plugin': 7.7.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1)(typescript@5.5.3) - '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) eslint: 8.57.1 eslint-config-prettier: 9.1.0(eslint@8.57.1) eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) eslint-plugin-autofix: 2.2.0(eslint@8.57.1) eslint-plugin-compat: 4.2.0(eslint@8.57.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) eslint-plugin-prettier: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.1)(prettier@3.2.5) eslint-plugin-unused-imports: 3.2.0(@typescript-eslint/eslint-plugin@7.7.1)(eslint@8.57.1) prettier: 3.2.5 @@ -13972,6 +13969,27 @@ packages: - supports-color dev: true + /@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.5.3): + resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 7.7.1 + '@typescript-eslint/types': 7.7.1 + '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.5.3) + '@typescript-eslint/visitor-keys': 7.7.1 + debug: 4.3.7 + eslint: 8.57.0 + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + dev: true + /@typescript-eslint/parser@7.7.1(eslint@8.57.1)(typescript@5.5.3): resolution: {integrity: sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -16764,7 +16782,7 @@ packages: eslint-plugin-promise: ^6.0.0 dependencies: eslint: 8.57.1 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1) eslint-plugin-n: 16.6.2(eslint@8.57.1) eslint-plugin-promise: 6.1.1(eslint@8.57.1) dev: true @@ -16790,7 +16808,7 @@ packages: enhanced-resolve: 5.16.0 eslint: 8.57.1 eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.3 is-core-module: 2.13.1 @@ -16802,7 +16820,36 @@ packages: - supports-color dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + dependencies: + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + debug: 3.2.7 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + dev: true + + /eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} engines: {node: '>=4'} peerDependencies: @@ -16827,7 +16874,6 @@ packages: debug: 3.2.7 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color dev: true @@ -16853,7 +16899,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 7.7.1(eslint@8.57.1)(typescript@5.5.3) + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) debug: 3.2.7 eslint: 8.57.1 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.7.1)(eslint-plugin-import@2.29.1)(eslint@8.57.1) @@ -16913,7 +16959,42 @@ packages: regexpp: 3.2.0 dev: true - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.0): + resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + dependencies: + '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.5.3) + array-includes: 3.1.7 + array.prototype.findlastindex: 1.2.3 + array.prototype.flat: 1.3.2 + array.prototype.flatmap: 1.3.2 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + hasown: 2.0.0 + is-core-module: 2.13.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.7 + object.groupby: 1.0.1 + object.values: 1.1.7 + semver: 6.3.1 + tsconfig-paths: 3.15.0 + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + dev: true + + /eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.7.1)(eslint@8.57.1): resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} engines: {node: '>=4'} peerDependencies: @@ -16932,7 +17013,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.7.1)(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) hasown: 2.0.0 is-core-module: 2.13.1 is-glob: 4.0.3