Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/weak-ravens-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@layerzerolabs/script-devtools-evm-foundry": patch
"@layerzerolabs/toolbox-foundry": patch
"@layerzerolabs/oft-example": patch
---

Added useful forge scripts and updated forge-std to latest commit
1 change: 1 addition & 0 deletions examples/oft/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ remappings = [
'forge-std/=node_modules/@layerzerolabs/toolbox-foundry/lib/forge-std',
'@layerzerolabs/=node_modules/@layerzerolabs/',
'@openzeppelin/=node_modules/@openzeppelin/',
'@layerzerolabs/script-devtools-evm-foundry/=node_modules/@layerzerolabs/script-devtools-evm-foundry/'
]

[fuzz]
Expand Down
1 change: 1 addition & 0 deletions examples/oft/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@layerzerolabs/oapp-evm": "^0.3.2",
"@layerzerolabs/oft-evm": "^3.1.3",
"@layerzerolabs/prettier-config-next": "^2.3.39",
"@layerzerolabs/script-devtools-evm-foundry": "^0.0.1",
"@layerzerolabs/solhint-config": "^3.0.12",
"@layerzerolabs/test-devtools-evm-foundry": "~6.0.3",
"@layerzerolabs/toolbox-foundry": "~0.1.12",
Expand Down
12 changes: 12 additions & 0 deletions examples/oft/script/LZScript.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import { console } from "forge-std/console.sol";

import { LZScripts } from "@layerzerolabs/script-devtools-evm-foundry/scripts/LZScripts.s.sol";

contract LZScript is LZScripts {
function run() public pure override {
console.log("Hello, World!");
}
}
5 changes: 5 additions & 0 deletions packages/script-devtools-evm-foundry/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.turbo
dist
node_modules
out
cache
3 changes: 3 additions & 0 deletions packages/script-devtools-evm-foundry/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../.eslintrc.json"
}
7 changes: 7 additions & 0 deletions packages/script-devtools-evm-foundry/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cache
# Ignore everything in the artifacts folder
artifacts/*

# Do not ignore the directory structure up to the specific file
!artifacts/
!artifacts/*.s.sol/
20 changes: 20 additions & 0 deletions packages/script-devtools-evm-foundry/CHANGELOG.md
Comment thread
ravinagill15 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# @layerzerolabs/script-devtools-evm-foundry

## 0.0.1

### Minor Changes

- Initial release with foundry scripts for LayerZero EVM development
- Add SimulateReceive script for simulating message reception on EVM chains
- Add GasProfiler script for profiling gas usage of OFT transfers on mainnet networks
- Add LZScripts.s.sol bundle for easy importing in custom foundry scripts
- Include foundry configuration and build artifacts for immediate use

### Patch Changes

- @layerzerolabs/lz-evm-messagelib-v2@3.0.12
- @layerzerolabs/lz-evm-protocol-v2@3.0.12
- @layerzerolabs/lz-evm-v1-0.7@3.0.12
- @layerzerolabs/toolbox-foundry@0.1.12
- @layerzerolabs/oapp-evm@0.3.1
- @layerzerolabs/oft-evm@3.1.2
55 changes: 55 additions & 0 deletions packages/script-devtools-evm-foundry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<p align="center">
<a href="https://layerzero.network">
<img alt="LayerZero" style="max-width: 500px" src="https://d3a2dpnnrypp5h.cloudfront.net/bridge-app/lz.png"/>
</a>
</p>

<h1 align="center">@layerzerolabs/script-devtools-evm-foundry</h1>

<!-- The badges section -->
<p align="center">
<!-- Shields.io NPM published package version -->
<a href="https://www.npmjs.com/package/@layerzerolabs/script-devtools-evm-foundry"><img alt="NPM Version" src="https://img.shields.io/npm/v/@layerzerolabs/script-devtools-evm-foundry"/></a>
<!-- Shields.io NPM downloads -->
<a href="https://www.npmjs.com/package/@layerzerolabs/script-devtools-evm-foundry"><img alt="Downloads" src="https://img.shields.io/npm/dm/@layerzerolabs/script-devtools-evm-foundry"/></a>
<!-- Shields.io license badge -->
<a href="https://www.npmjs.com/package/@layerzerolabs/script-devtools-evm-foundry"><img alt="NPM License" src="https://img.shields.io/npm/l/@layerzerolabs/script-devtools-evm-foundry"/></a>
</p>

## Installation

```sh
$ npm install @layerzerolabs/script-devtools-evm-foundry
```

## Usage

This package not only exports a bunch of scripts for you to use in your foundry projects. But since they compile into your `artifacts` folder, you can run them from your command line.

```sh
forge script SimulateReceive --rpc-url YOUR_DESTINATION_CHAIN_RPC_URL --ffi
```

Since all the scripts are bundled into `LZScripts.s.sol`, you can import them in your own scripts like this:

```solidity
import { LZUtils } from "@layerzerolabs/script-devtools-evm-foundry/scripts/LZScripts.s.sol";
```

## List of scripts

### 1. SimulateReceive

Simulate receiving a message on an EVM chain - [Read more](./script/SimulateReceive/README.md)
Source code: [SimulateReceive.s.sol](./script/SimulateReceive/SimulateReceive.s.sol)
Shell command:

```sh
forge script SimulateReceive --rpc-url $DESTINATION_CHAIN_RPC_URL --ffi
```

### 2. GasProfiler

Profile the gas usage of OFT transfers on multiple mainnet networks - [Read more](./script/GasProfiling/README.md)
Source code: [OFTProfilerExample.s.sol](./script/GasProfiling/OFTProfilerExample.s.sol)
Shell command: Not available

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions packages/script-devtools-evm-foundry/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[profile.default]
solc-version = '0.8.22'
src = 'contracts'
out = 'artifacts'
test = 'test/foundry'
cache_path = 'cache'
optimizer = true
optimizer_runs = 20_000

libs = [
# We provide a set of useful contract utilities
# in the lib directory of @layerzerolabs/toolbox-foundry:
#
# - forge-std
# - ds-test
# - solidity-bytes-utils
'node_modules/@layerzerolabs/toolbox-foundry/lib',
'node_modules',
]

remappings = [
# Due to a misconfiguration of solidity-bytes-utils, an outdated version
# of forge-std is being dragged in
#
# To remedy this, we'll remap the ds-test and forge-std imports to our own versions
'ds-test/=node_modules/@layerzerolabs/toolbox-foundry/src/ds-test/src/',
'forge-std/=node_modules/@layerzerolabs/toolbox-foundry/src/forge-std/src/',
'@layerzerolabs/=node_modules/@layerzerolabs/',
'@openzeppelin/=node_modules/@openzeppelin/',
]
46 changes: 46 additions & 0 deletions packages/script-devtools-evm-foundry/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@layerzerolabs/script-devtools-evm-foundry",
"version": "0.0.1",
"description": "Scripts to help develop LayerZero EVM contracts using foundry",
"repository": {
"type": "git",
"url": "git+https://github.com/LayerZero-Labs/devtools.git",
"directory": "packages/script-devtools-evm-foundry"
},
"license": "MIT",
"files": [
"contracts/",
"artifacts/",
"scripts/"
],
"scripts": {
"build": "forge build",
"coverage": "forge coverage --ir-minimum",
"lint": "$npm_execpath eslint '**/*.{js,ts,json}'",
"lint:fix": "eslint --fix '**/*.{js,ts,json}'"
},
"devDependencies": {
"@layerzerolabs/lz-evm-messagelib-v2": "^3.0.12",
"@layerzerolabs/lz-evm-protocol-v2": "^3.0.12",
"@layerzerolabs/lz-evm-v1-0.7": "^3.0.12",
"@layerzerolabs/toolbox-foundry": "^0.1.12",
"@openzeppelin/contracts": "^5.0.2",
"@openzeppelin/contracts-upgradeable": "^5.0.2",
"@types/node": "~18.18.14",
"solidity-bytes-utils": "^0.8.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.4"
},
"peerDependencies": {
"@layerzerolabs/lz-evm-messagelib-v2": "^3.0.12",
"@layerzerolabs/lz-evm-protocol-v2": "^3.0.12",
"@layerzerolabs/lz-evm-v1-0.7": "^3.0.12",
"@layerzerolabs/oapp-evm": "^0.3.1",
"@layerzerolabs/oft-evm": "^3.1.2",
"@openzeppelin/contracts": "^4.9.5 || ^5.0.0",
"@openzeppelin/contracts-upgradeable": "^4.9.5 || ^5.0.0"
},
"publishConfig": {
"access": "public"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ contract OFTProfilerExample is Script {

/// @notice Entry point for the script. Generates fuzzed payloads and profiles gas usage on multiple networks.
/// @param numberOfPayloads The number of payloads to generate and profile.
function run(uint256 numberOfPayloads) external {
function run(uint256 numberOfPayloads) external virtual {
// Step 1: Deploy the GasProfilerScript contract which handles gas profiling.
profiler = new GasProfilerScript();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# GasProfiling

Can be used to profile the gas usage of an OFT for its `lzReceive` and `lzCompose` functions.

Refer: [OFTProfilerExample.s.sol](./OFTProfilerExample.s.sol)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { GasProfilerScript } from "./GasProfiler.s.sol";

abstract contract GasProfilingScript is GasProfilerScript {}
11 changes: 11 additions & 0 deletions packages/script-devtools-evm-foundry/scripts/LZScripts.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { SimulateScript } from "./SimulateReceive/utils.sol";
import { GasProfilingScript } from "./GasProfiling/utils.sol";

abstract contract LZScripts is SimulateScript, GasProfilingScript {
function IS_WORKING() public pure returns (bool) {
return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SimulateReceive

## Usage

```shell
yarn
forge build
```

1. Find the source transaction hash of the message that has failed on [LayerZero Scan](https://layerzeroscan.com)
2. Paste the transaction hash into `string memory txHash = "0x0c61c4db115f57b2fba78df78879f9e8230e67d75e78ac2782a3b4c929b5d12f";` in [script/SimulateReceive.s.sol](./script/SimulateReceive.s.sol) and set the mainnet boolean to `true` or `false` (for the scan API)
4. Run: `forge script script/SimulateReceive.s.sol --rpc-url YOUR_DESTINATION_CHAIN_RPC_URL --ffi`
5. (Optional) Add `--broadcast` and `--private-key YOUR_PRIVATE_KEY` to broadcast the transaction to the destination chain

If you get an error from SimulateReceive script eg. `script failed: custom error 7182306f` you can do:

```shell
cast 4byte 7182306f
```

Example result:

```solidity
LZ_PayloadHashNotFound(bytes32,bytes32)
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.22;

import { Script, console } from "forge-std/Script.sol";
import "forge-std/StdJson.sol";
import { Base58Decoder } from "./base58decoder.sol"; // Import the new library

import { IOAppCore } from "@layerzerolabs/oapp-evm/contracts/oapp/interfaces/IOAppCore.sol";
import { SendParam, OFTReceipt } from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
import { MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OApp.sol";
import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";

// This is an exact copy of Dan Kmak's https://github.com/DanL0/integrations-foundry-tooling script that helps simulate receiving a message on an EVM chain
contract SimulateReceive is Script {
using stdJson for string;
using Base58Decoder for string; // Use the library

function run() public virtual {
// -- New mainnet/testnet toggle added here --
bool mainnet = true; // Set to false for testnet
string memory txHash = "0x88a79d59222628aba22c5c616580212c80beae353aaa8ffbd3976dd5aa5d12f8";

string[] memory curlCommand = new string[](5);
curlCommand[0] = "curl";
curlCommand[1] = "-X";
curlCommand[2] = "GET";
curlCommand[3] = string(
abi.encodePacked(
mainnet
? "https://scan.layerzero-api.com" // Mainnet
: "https://scan-testnet.layerzero-api.com", // Testnet
"/v1/messages/tx/",
txHash
)
);
curlCommand[4] = "-H 'accept: application/json'";

// Execute the curl command
bytes memory result = vm.ffi(curlCommand);

// Convert the result to a string
string memory json = string(result);

// Read the sender chain
string memory senderChain = json.readString(".data[0].pathway.sender.chain");

// Read the destination chain
string memory destinationChain = json.readString(".data[0].pathway.receiver.chain");

// Read the sender address
bytes32 senderBytes32;
string memory senderAddressStr = json.readString(".data[0].pathway.sender.address");
if (keccak256(bytes(senderChain)) == keccak256(bytes("solana"))) {
// If the chain is Solana, decode the Base58 address
bytes memory decodedAddress = senderAddressStr.base58ToHex();

// Ensure the decoded address is 32 bytes long
require(decodedAddress.length == 32, "Decoded address must be 32 bytes");
senderBytes32 = bytes32(decodedAddress);
} else {
// Otherwise, read the address directly and convert to bytes32
address senderAddress = json.readAddress(".data[0].pathway.sender.address");
senderBytes32 = addressToBytes32(senderAddress);
}
console.log("Sender: %s (%s)", senderAddressStr, senderChain);

// Read the receiver address
address receiver = json.readAddress(".data[0].pathway.receiver.address");
console.log("Receiver: %s (%s)", receiver, destinationChain);

// Read the nonce
uint64 nonce = uint64(json.readUint(".data[0].pathway.nonce"));
console.log("Nonce:", nonce);

// Read the transaction hash
string memory fetchedTxHash = json.readString(".data[0].source.tx.txHash");
console.log("Source Transaction Hash:", fetchedTxHash);

// Read other fields from the JSON
uint32 srcEid = uint32(json.readUint(".data[0].pathway.srcEid"));
bytes32 guid = json.readBytes32(".data[0].guid");
bytes memory payload = json.readBytes(".data[0].source.tx.payload");

// Construct the Origin struct
Origin memory origin = Origin({
srcEid: srcEid,
sender: senderBytes32, // Use the bytes32 sender
nonce: nonce
});
bytes memory extraData = "";

// Simulate the lzReceive function
vm.startBroadcast();
IOAppCore(receiver).endpoint().lzReceive(origin, receiver, guid, payload, extraData);
vm.stopBroadcast();
}

// Helper function to convert address to bytes32
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
}
Loading