From 27357228d164d3724713d6aac7324f449bde7e22 Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 2 Sep 2025 14:31:48 +0200 Subject: [PATCH 1/3] Pioneer throughput testing script --- script/pioneer/PioneerQA.sol | 70 ++++++++++++++++++ script/pioneer/pioneer-qa-full.sh | 29 ++++++++ script/pioneer/pioneer-qa.sh | 119 ++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+) create mode 100644 script/pioneer/PioneerQA.sol create mode 100755 script/pioneer/pioneer-qa-full.sh create mode 100755 script/pioneer/pioneer-qa.sh diff --git a/script/pioneer/PioneerQA.sol b/script/pioneer/PioneerQA.sol new file mode 100644 index 000000000..c30882ebc --- /dev/null +++ b/script/pioneer/PioneerQA.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: LGPL-3.0-only +pragma solidity ^0.8.17; + +import { Script } from "forge-std/Script.sol"; +import { PioneerFacet } from "lifi/Facets/PioneerFacet.sol"; +import { ILiFi } from "../../src/Interfaces/ILiFi.sol"; +import { LibAsset, IERC20 } from "../../src/Libraries/LibAsset.sol"; + +contract PioneerQA is Script { + error DiamondNotContract(); + + struct Params { + bytes32 transactionId; + address sendingAssetId; + address receiver; + uint256 inputAmount; + uint256 minAmount; + uint256 destinationChainId; + } + + function run( + address diamond, + address payable refundAddress, + Params[] calldata params + ) public { + if (!LibAsset.isContract(diamond)) revert DiamondNotContract(); + + vm.startBroadcast(); + + uint256 numParams = params.length; + for (uint256 i; i < numParams; ++i) { + Params calldata param = params[i]; + ILiFi.BridgeData memory _bridgeData = ILiFi.BridgeData({ + transactionId: param.transactionId, + bridge: "Pioneer", + integrator: "ACME Devs", + referrer: address(0), + sendingAssetId: param.sendingAssetId, + receiver: param.receiver, + minAmount: param.minAmount, + destinationChainId: param.destinationChainId, + hasSourceSwaps: false, + hasDestinationCall: false + }); + PioneerFacet.PioneerData memory _pioneerData = PioneerFacet + .PioneerData(refundAddress); + + if (LibAsset.isNativeAsset(param.sendingAssetId)) { + PioneerFacet(diamond).startBridgeTokensViaPioneer{ + value: param.inputAmount + }(_bridgeData, _pioneerData); + } else { + // Set allowance + LibAsset.approveERC20( + IERC20(param.sendingAssetId), + diamond, + param.inputAmount, + param.inputAmount + ); + + PioneerFacet(diamond).startBridgeTokensViaPioneer( + _bridgeData, + _pioneerData + ); + } + } + + vm.stopBroadcast(); + } +} diff --git a/script/pioneer/pioneer-qa-full.sh b/script/pioneer/pioneer-qa-full.sh new file mode 100755 index 000000000..f15d8fed9 --- /dev/null +++ b/script/pioneer/pioneer-qa-full.sh @@ -0,0 +1,29 @@ +## Load env +source .env + +# This script is created to assist with throughput testing of Pioneer through the LI.FI diamond. + +# Get from network. +NETWORK=$1 # ["optimism", "arbitrum", "polygon"] +# Get to networks +TO_NETWORKS=$2 # ["optimism", "arbitrum", "polygon"] +# Get the token +TOKEN=$3 # 0x... +NUM_TRANSACTIONS=$4 # 10 +NUM_RUNS=5 # 5 + + +# For each network provided, run the qa script in parallel and then wait for all to finish. +for RUN in $(seq 1 $NUM_RUNS); +do + echo "Starting run $RUN of $NUM_RUNS" + for NETWORK in $(echo $NETWORK | tr -d '[]"' | tr ',' '\n'); + do + echo "Starting QA for network: $NETWORK" + bash script/pioneer/pioneer-qa.sh "$NETWORK" "$TO_NETWORKS" "$TOKEN" "$NUM_TRANSACTIONS" & + done + # Wait for all background processes to finish + wait +done; + +echo "All QA processes completed." diff --git a/script/pioneer/pioneer-qa.sh b/script/pioneer/pioneer-qa.sh new file mode 100755 index 000000000..a5cb1fd78 --- /dev/null +++ b/script/pioneer/pioneer-qa.sh @@ -0,0 +1,119 @@ +## Load env +source .env + +# This script is created to assist with throughput testing of Pioneer through the LI.FI diamond. + +# Get from network. +NETWORK=$1 # "optimism" +# Get to networks +TO_NETWORKS=$2 # ["optimism", "arbitrum", "polygon"] +# Get the token +TOKEN=$3 # 0x... +NUM_TRANSACTIONS=$4 # 10 + +# Convert network to lowercase +NETWORK=$(echo $NETWORK | tr '[:upper:]' '[:lower:]') +UPPER_NETWORK=$(echo $NETWORK | tr '[:lower:]' '[:upper:]') + +# Get the diamond address for the network (stored at ./deployments/.json) +DIAMOND=$(jq -r '.LiFiDiamond' ./deployments/$NETWORK.staging.json) +echo "Using LiFi Diamond at $DIAMOND" + +# Get the RPC_URL for the network ($ETH_NODE_URI_) +RPC_URL=$(eval echo \$ETH_NODE_URI_$UPPER_NETWORK) + +# Get from network id. +FROM_NETWORK_ID=$(cast chain-id --rpc-url $RPC_URL) + +# Convert to_networks to array of ids. +TO_NETWORKS_IDS=() +for NET in $(echo $TO_NETWORKS | tr -d '[]"' | tr ',' '\n'); +do + NET_ID=$(cast chain-id --rpc-url $(eval echo \$ETH_NODE_URI_$(echo $NET | tr '[:lower:]' '[:upper:]'))) + TO_NETWORKS_IDS+=($NET_ID) +done + +# Compute the user's address +USER_ADDRESS=$(cast wallet address $PRIVATE_KEY) + +# Assert that the token is address(0) +ADDRESS_0=$(cast address-zero) +if [ "$TOKEN" != "$ADDRESS_0" ]; then + echo "This script only supports the native token (address(0))." + exit 1 +fi + +# # Get the balance of the user of the tokens. +USER_BALANCE=$(cast balance $USER_ADDRESS --rpc-url $RPC_URL) +# Divide user balance by 10 +USER_BALANCE=$((USER_BALANCE / 10)) + +# Compute the amount we want to use, 10000000000000000 or user balance / 10 whatever is smallest. +OP_AMOUNT=10000000000000000 +if [ "$USER_BALANCE" -lt "$OP_AMOUNT" ]; then + OP_AMOUNT=$USER_BALANCE +fi +echo "Operating Amount: $OP_AMOUNT" + +# Collect quotes from Pioneer +# Initialize an empty array to hold responses +RESPONSES=() +# Enter a for loop to perform multiple transactions +for i in $(seq 1 $NUM_TRANSACTIONS); +do + echo "Processing transaction $i of $NUM_TRANSACTIONS..." + + # Generate a random 32-byte transaction ID (hex string) + TRANSACTION_ID="0x$(openssl rand -hex 32)" + + # Prepare query parameters + FROM_CHAIN="$FROM_NETWORK_ID" + FROM_TOKEN="$TOKEN" + TO_TOKEN="$TOKEN" + TO_ADDRESS="$USER_ADDRESS" + # Divide the OP_AMOUNT by number of operations + FROM_AMOUNT=$(($OP_AMOUNT / $NUM_TRANSACTIONS)) + SLIPPAGE="0" + EXTERNAL_ID="$TRANSACTION_ID" + + # Select random to_chain + TO_CHAIN=${TO_NETWORKS_IDS[$(($RANDOM % ${#TO_NETWORKS_IDS[@]}))]} + + # Construct query string + QUERY_STRING="fromChain=$FROM_CHAIN&toChain=$TO_CHAIN&fromToken=$FROM_TOKEN&toToken=$TO_TOKEN&toAddress=$TO_ADDRESS&fromAmount=$FROM_AMOUNT&slippage=$SLIPPAGE&externalId=$EXTERNAL_ID" + + echo $QUERY_STRING + + # Set the Pioneer endpoint (replace with actual endpoint if needed) + PIONEER_ENDPOINT="https://solver-dev.li.fi" + + # Fetch quote from Pioneer + RESPONSE=$(curl -s -G "$PIONEER_ENDPOINT/quote?$QUERY_STRING" -H "Content-Type: application/json") + + # # Check if the response is valid + if [ -z "$RESPONSE" ] || echo "$RESPONSE" | grep -q '"error"'; then + echo "Quote request failed: $RESPONSE" + exit 1 + fi + +# # Print the quote response + echo "Quote response:" + echo "$RESPONSE" + # Extract necessary fields from the response + TO_CHAIN_ID=$(echo "$RESPONSE" | jq -r '.toChainId') + TO_AMOUNT_MIN=$(echo "$RESPONSE" | jq -r '.toAmountMin') + + RESPONSES+=("$(echo "$TRANSACTION_ID, $TOKEN, $USER_ADDRESS, $OP_AMOUNT, $TO_AMOUNT_MIN, $TO_CHAIN_ID")") +done + +echo "All quote responses collected." +echo "${RESPONSES[@]}"; + +# Convert RESPONSES array into a flattened string for the script +FLATTENED_RESPONSES=$(printf "(%s)," "${RESPONSES[@]}") +FLATTENED_RESPONSES="[${FLATTENED_RESPONSES%,}]" +echo "Formatted responses for script:" +echo "$FLATTENED_RESPONSES" + +# # Execute transactions: +forge script PioneerQA --sig "run(address,address,(bytes32,address,address,uint256,uint256,uint256)[])" "$DIAMOND" "$USER_ADDRESS" "$FLATTENED_RESPONSES" --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast -vvvv \ No newline at end of file From a9ba88818c310320e64f788fe861d741a4a9f5df Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 2 Sep 2025 15:56:54 +0200 Subject: [PATCH 2/3] Remove testing comments --- script/pioneer/pioneer-qa.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/script/pioneer/pioneer-qa.sh b/script/pioneer/pioneer-qa.sh index a5cb1fd78..cd39f8e74 100755 --- a/script/pioneer/pioneer-qa.sh +++ b/script/pioneer/pioneer-qa.sh @@ -84,7 +84,7 @@ do echo $QUERY_STRING - # Set the Pioneer endpoint (replace with actual endpoint if needed) + # Set the Pioneer endpoint PIONEER_ENDPOINT="https://solver-dev.li.fi" # Fetch quote from Pioneer @@ -96,9 +96,6 @@ do exit 1 fi -# # Print the quote response - echo "Quote response:" - echo "$RESPONSE" # Extract necessary fields from the response TO_CHAIN_ID=$(echo "$RESPONSE" | jq -r '.toChainId') TO_AMOUNT_MIN=$(echo "$RESPONSE" | jq -r '.toAmountMin') @@ -112,8 +109,6 @@ echo "${RESPONSES[@]}"; # Convert RESPONSES array into a flattened string for the script FLATTENED_RESPONSES=$(printf "(%s)," "${RESPONSES[@]}") FLATTENED_RESPONSES="[${FLATTENED_RESPONSES%,}]" -echo "Formatted responses for script:" -echo "$FLATTENED_RESPONSES" -# # Execute transactions: +# Execute transactions: forge script PioneerQA --sig "run(address,address,(bytes32,address,address,uint256,uint256,uint256)[])" "$DIAMOND" "$USER_ADDRESS" "$FLATTENED_RESPONSES" --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast -vvvv \ No newline at end of file From c8555b8fe179675cc9abc12b2cd95284f5280b0c Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 3 Sep 2025 14:24:31 +0200 Subject: [PATCH 3/3] Update with testing fixes and code rabbit suggestions --- script/pioneer/PioneerQA.sol | 7 +++---- script/pioneer/pioneer-qa-full.sh | 25 ++++++++++++----------- script/pioneer/pioneer-qa.sh | 33 ++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 24 deletions(-) diff --git a/script/pioneer/PioneerQA.sol b/script/pioneer/PioneerQA.sol index c30882ebc..2cf67047c 100644 --- a/script/pioneer/PioneerQA.sol +++ b/script/pioneer/PioneerQA.sol @@ -13,7 +13,6 @@ contract PioneerQA is Script { bytes32 transactionId; address sendingAssetId; address receiver; - uint256 inputAmount; uint256 minAmount; uint256 destinationChainId; } @@ -47,15 +46,15 @@ contract PioneerQA is Script { if (LibAsset.isNativeAsset(param.sendingAssetId)) { PioneerFacet(diamond).startBridgeTokensViaPioneer{ - value: param.inputAmount + value: param.minAmount }(_bridgeData, _pioneerData); } else { // Set allowance LibAsset.approveERC20( IERC20(param.sendingAssetId), diamond, - param.inputAmount, - param.inputAmount + param.minAmount, + param.minAmount ); PioneerFacet(diamond).startBridgeTokensViaPioneer( diff --git a/script/pioneer/pioneer-qa-full.sh b/script/pioneer/pioneer-qa-full.sh index f15d8fed9..f1fc67adc 100755 --- a/script/pioneer/pioneer-qa-full.sh +++ b/script/pioneer/pioneer-qa-full.sh @@ -1,26 +1,29 @@ +#!/usr/bin/env bash + ## Load env source .env # This script is created to assist with throughput testing of Pioneer through the LI.FI diamond. -# Get from network. -NETWORK=$1 # ["optimism", "arbitrum", "polygon"] -# Get to networks -TO_NETWORKS=$2 # ["optimism", "arbitrum", "polygon"] -# Get the token -TOKEN=$3 # 0x... -NUM_TRANSACTIONS=$4 # 10 -NUM_RUNS=5 # 5 +# Get from networks (JSON-like list or comma-separated) +SRC_NETWORKS=${1:?Usage: $0 SRC_NETWORKS TO_NETWORKS TOKEN NUM_TRANSACTIONS NUM_RUNS} + +# Get to networks "[optimism, arbitrum, polygon]" +TO_NETWORKS=${2:?missing TO_NETWORKS} + +TOKEN=${3:?missing TOKEN} +NUM_TRANSACTIONS=${4:?missing NUM_TRANSACTIONS} +NUM_RUNS=${5:-5} # For each network provided, run the qa script in parallel and then wait for all to finish. for RUN in $(seq 1 $NUM_RUNS); do echo "Starting run $RUN of $NUM_RUNS" - for NETWORK in $(echo $NETWORK | tr -d '[]"' | tr ',' '\n'); + for NET in $(echo $SRC_NETWORKS | tr -d '[]"' | tr ',' '\n'); do - echo "Starting QA for network: $NETWORK" - bash script/pioneer/pioneer-qa.sh "$NETWORK" "$TO_NETWORKS" "$TOKEN" "$NUM_TRANSACTIONS" & + echo "Starting QA for network: $NET" + bash script/pioneer/pioneer-qa.sh "$NET" "$TO_NETWORKS" "$TOKEN" "$NUM_TRANSACTIONS" & done # Wait for all background processes to finish wait diff --git a/script/pioneer/pioneer-qa.sh b/script/pioneer/pioneer-qa.sh index cd39f8e74..932f17c5e 100755 --- a/script/pioneer/pioneer-qa.sh +++ b/script/pioneer/pioneer-qa.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + ## Load env source .env @@ -16,23 +18,32 @@ NETWORK=$(echo $NETWORK | tr '[:upper:]' '[:lower:]') UPPER_NETWORK=$(echo $NETWORK | tr '[:lower:]' '[:upper:]') # Get the diamond address for the network (stored at ./deployments/.json) -DIAMOND=$(jq -r '.LiFiDiamond' ./deployments/$NETWORK.staging.json) +DEPLOY_FILE="./deployments/${NETWORK}.staging.json" +test -f "$DEPLOY_FILE" || { echo "Missing $DEPLOY_FILE"; exit 1; } +DIAMOND=$(jq -r '.LiFiDiamond' "$DEPLOY_FILE") echo "Using LiFi Diamond at $DIAMOND" # Get the RPC_URL for the network ($ETH_NODE_URI_) -RPC_URL=$(eval echo \$ETH_NODE_URI_$UPPER_NETWORK) +RPC_ENV="ETH_NODE_URI_${UPPER_NETWORK}" +RPC_URL=$(eval "echo \${$RPC_ENV:-}") +[ -n "$RPC_URL" ] || { echo "Missing $RPC_ENV"; exit 1; } # Get from network id. -FROM_NETWORK_ID=$(cast chain-id --rpc-url $RPC_URL) +FROM_NETWORK_ID=$(cast chain-id --rpc-url "$RPC_URL") # Convert to_networks to array of ids. TO_NETWORKS_IDS=() for NET in $(echo $TO_NETWORKS | tr -d '[]"' | tr ',' '\n'); do - NET_ID=$(cast chain-id --rpc-url $(eval echo \$ETH_NODE_URI_$(echo $NET | tr '[:lower:]' '[:upper:]'))) - TO_NETWORKS_IDS+=($NET_ID) + UPPER_NET=$(echo "$NET" | tr '[:lower:]' '[:upper:]') + NET_RPC_ENV="ETH_NODE_URI_${UPPER_NET}" + NET_RPC=$(eval "echo \${$NET_RPC_ENV:-}") + [ -n "$NET_RPC" ] || { echo "Missing $NET_RPC_ENV"; exit 1; } + NET_ID=$(cast chain-id --rpc-url "$NET_RPC") + TO_NETWORKS_IDS+=("$NET_ID") done +[[ -n "${PRIVATE_KEY:-}" ]] || { echo "Missing PRIVATE_KEY in env" >&2; exit 1; } # Compute the user's address USER_ADDRESS=$(cast wallet address $PRIVATE_KEY) @@ -49,11 +60,12 @@ USER_BALANCE=$(cast balance $USER_ADDRESS --rpc-url $RPC_URL) USER_BALANCE=$((USER_BALANCE / 10)) # Compute the amount we want to use, 10000000000000000 or user balance / 10 whatever is smallest. -OP_AMOUNT=10000000000000000 +OP_AMOUNT=100000000000000000 if [ "$USER_BALANCE" -lt "$OP_AMOUNT" ]; then OP_AMOUNT=$USER_BALANCE fi echo "Operating Amount: $OP_AMOUNT" +[ "$OP_AMOUNT" -gt 0 ] || { echo "OP_AMOUNT computed as 0"; exit 1; } # Collect quotes from Pioneer # Initialize an empty array to hold responses @@ -73,6 +85,7 @@ do TO_ADDRESS="$USER_ADDRESS" # Divide the OP_AMOUNT by number of operations FROM_AMOUNT=$(($OP_AMOUNT / $NUM_TRANSACTIONS)) + [ "$FROM_AMOUNT" -gt 0 ] || { echo "Per-tx FROM_AMOUNT computed as 0" >&2; exit 1; } SLIPPAGE="0" EXTERNAL_ID="$TRANSACTION_ID" @@ -90,6 +103,9 @@ do # Fetch quote from Pioneer RESPONSE=$(curl -s -G "$PIONEER_ENDPOINT/quote?$QUERY_STRING" -H "Content-Type: application/json") + echo "$PIONEER_ENDPOINT/quote?$QUERY_STRING" + echo "Response: $RESPONSE" + # # Check if the response is valid if [ -z "$RESPONSE" ] || echo "$RESPONSE" | grep -q '"error"'; then echo "Quote request failed: $RESPONSE" @@ -98,9 +114,8 @@ do # Extract necessary fields from the response TO_CHAIN_ID=$(echo "$RESPONSE" | jq -r '.toChainId') - TO_AMOUNT_MIN=$(echo "$RESPONSE" | jq -r '.toAmountMin') - RESPONSES+=("$(echo "$TRANSACTION_ID, $TOKEN, $USER_ADDRESS, $OP_AMOUNT, $TO_AMOUNT_MIN, $TO_CHAIN_ID")") + RESPONSES+=("$(echo "$TRANSACTION_ID, $TOKEN, $USER_ADDRESS, $FROM_AMOUNT, $TO_CHAIN_ID")") done echo "All quote responses collected." @@ -111,4 +126,4 @@ FLATTENED_RESPONSES=$(printf "(%s)," "${RESPONSES[@]}") FLATTENED_RESPONSES="[${FLATTENED_RESPONSES%,}]" # Execute transactions: -forge script PioneerQA --sig "run(address,address,(bytes32,address,address,uint256,uint256,uint256)[])" "$DIAMOND" "$USER_ADDRESS" "$FLATTENED_RESPONSES" --private-key $PRIVATE_KEY --rpc-url $RPC_URL --broadcast -vvvv \ No newline at end of file +forge script PioneerQA --sig "run(address,address,(bytes32,address,address,uint256,uint256)[])" "$DIAMOND" "$USER_ADDRESS" "$FLATTENED_RESPONSES" --private-key "$PRIVATE_KEY" --rpc-url "$RPC_URL" --broadcast -vvvv \ No newline at end of file