Skip to content
Draft
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
69 changes: 69 additions & 0 deletions script/pioneer/PioneerQA.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// 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 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.minAmount
}(_bridgeData, _pioneerData);
} else {
// Set allowance
LibAsset.approveERC20(
IERC20(param.sendingAssetId),
diamond,
param.minAmount,
param.minAmount
);

PioneerFacet(diamond).startBridgeTokensViaPioneer(
_bridgeData,
_pioneerData
);
}
}

vm.stopBroadcast();
}
}
32 changes: 32 additions & 0 deletions script/pioneer/pioneer-qa-full.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/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 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 NET in $(echo $SRC_NETWORKS | tr -d '[]"' | tr ',' '\n');
do
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
done;

echo "All QA processes completed."
129 changes: 129 additions & 0 deletions script/pioneer/pioneer-qa.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/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"
# Get to networks
TO_NETWORKS=$2 # ["optimism", "arbitrum", "polygon"]
# Get the token
TOKEN=$3 # 0x...
NUM_TRANSACTIONS=$4 # 10

Comment on lines +8 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add usage/help and validate required args early

Prevents divide-by-zero and empty inputs; improves UX.

+# Usage / args
+usage() {
+  echo "Usage: $(basename "$0") <NETWORK> <TO_NETWORKS_JSON> <TOKEN_ADDRESS> <NUM_TRANSACTIONS>" >&2
+  echo "Example: $(basename "$0") optimism '[\"arbitrum\",\"polygon\"]' 0x0000000000000000000000000000000000000000 100" >&2
+  exit 1
+}
+[ $# -ge 4 ] || usage
+[[ "$4" =~ ^[0-9]+$ ]] || { echo "NUM_TRANSACTIONS must be an integer" >&2; exit 1; }
+[[ "$4" -gt 0 ]] || { echo "NUM_TRANSACTIONS must be > 0" >&2; exit 1; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 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
# Usage / args
usage() {
echo "Usage: $(basename "$0") <NETWORK> <TO_NETWORKS_JSON> <TOKEN_ADDRESS> <NUM_TRANSACTIONS>" >&2
echo "Example: $(basename "$0") optimism '[\"arbitrum\",\"polygon\"]' 0x0000000000000000000000000000000000000000 100" >&2
exit 1
}
[ $# -ge 4 ] || usage
[[ "$4" =~ ^[0-9]+$ ]] || { echo "NUM_TRANSACTIONS must be an integer" >&2; exit 1; }
[[ "$4" -gt 0 ]] || { echo "NUM_TRANSACTIONS must be > 0" >&2; exit 1; }
# 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
🤖 Prompt for AI Agents
In script/pioneer/pioneer-qa.sh around lines 6 to 13, the script currently
assigns positional args without validation which can lead to divide-by-zero and
empty-input errors; add an early argument check that verifies required args are
present, NUM_TRANSACTIONS is a positive integer (>0) and TO_NETWORKS is
non-empty (and if expected as a list, validate basic format), and print a
concise usage/help message (showing required arguments and example) then exit
with a non-zero status when validation fails; implement these checks immediately
after the existing assignments so downstream code never runs with invalid or
missing inputs.

# 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/<network>.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"

Comment on lines +21 to +25
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Validate diamond address after jq

Catch missing/null address early.

 DIAMOND=$(jq -r '.LiFiDiamond' "$DEPLOY_FILE")
 echo "Using LiFi Diamond at $DIAMOND"
+[ -n "$DIAMOND" ] && [ "$DIAMOND" != "null" ] || { echo "LiFiDiamond is missing in $DEPLOY_FILE"; exit 1; }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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"
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"
[ -n "$DIAMOND" ] && [ "$DIAMOND" != "null" ] || { echo "LiFiDiamond is missing in $DEPLOY_FILE"; exit 1; }
🤖 Prompt for AI Agents
In script/pioneer/pioneer-qa.sh around lines 21 to 25, after extracting DIAMOND
with jq, add a validation that DIAMOND is non-empty and not the literal "null";
if the value is missing/empty/null print a clear error like "Missing LiFiDiamond
in $DEPLOY_FILE" and exit non‑zero so the script fails early. Ensure the check
handles both empty string and the "null" string returned by jq and runs
immediately after the echo assignment.

# Get the RPC_URL for the network ($ETH_NODE_URI_<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")

# Convert to_networks to array of ids.
TO_NETWORKS_IDS=()
for NET in $(echo $TO_NETWORKS | tr -d '[]"' | tr ',' '\n');
do
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)

# 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=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
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))
[ "$FROM_AMOUNT" -gt 0 ] || { echo "Per-tx FROM_AMOUNT computed as 0" >&2; exit 1; }
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
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")

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"
exit 1
fi

# Extract necessary fields from the response
TO_CHAIN_ID=$(echo "$RESPONSE" | jq -r '.toChainId')

RESPONSES+=("$(echo "$TRANSACTION_ID, $TOKEN, $USER_ADDRESS, $FROM_AMOUNT, $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%,}]"

# Execute transactions:
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