- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control. - Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities. - Created .gitmodules to include OpenZeppelin contracts as a submodule. - Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment. - Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks. - Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring. - Created scripts for resource import and usage validation across non-US regions. - Added tests for CCIP error handling and integration to ensure robust functionality. - Included various new files and directories for the orchestration portal and deployment scripts.
273 lines
8.9 KiB
Bash
Executable File
273 lines
8.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# CCIP Send Script
|
|
# Sends tokens via CCIP bridge to a destination chain
|
|
#
|
|
# Usage:
|
|
# ./ccip-send.sh --selector <chain-selector> --receiver <address> --amount <wei> [--dry-run]
|
|
#
|
|
# Example:
|
|
# export BRIDGE_ADDRESS=$CCIPWETH9_BRIDGE_CHAIN138
|
|
# ./ccip-send.sh --selector 5009297550715157269 --receiver 0xYourMainnetAddress --amount 100000000000000000
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
|
|
|
# Load environment variables
|
|
ENV_FILE="${ENV_FILE:-$PROJECT_ROOT/.env}"
|
|
if [ -f "$ENV_FILE" ]; then
|
|
export $(grep -v '^#' "$ENV_FILE" | grep -E "^(RPC_URL|PRIVATE_KEY|CHAIN_ID|CCIP_ROUTER|LINK_TOKEN|WETH9_ADDRESS|WETH10_ADDRESS|FEE_TOKEN)" | xargs)
|
|
fi
|
|
|
|
# Default values
|
|
DRY_RUN=false
|
|
SELECTOR=""
|
|
RECEIVER=""
|
|
AMOUNT=""
|
|
|
|
# Parse arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--selector)
|
|
SELECTOR="$2"
|
|
shift 2
|
|
;;
|
|
--receiver)
|
|
RECEIVER="$2"
|
|
shift 2
|
|
;;
|
|
--amount)
|
|
AMOUNT="$2"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
*)
|
|
echo "Unknown argument: $1"
|
|
echo ""
|
|
echo "Usage: $0 --selector <chain-selector> --receiver <address> --amount <wei> [--dry-run]"
|
|
echo ""
|
|
echo "Arguments:"
|
|
echo " --selector <chain-selector> CCIP chain selector (required)"
|
|
echo " --receiver <address> Receiver address on destination chain (required)"
|
|
echo " --amount <wei> Amount to bridge in wei (required)"
|
|
echo " --dry-run Simulate transaction without sending (optional)"
|
|
echo ""
|
|
echo "Environment variables:"
|
|
echo " BRIDGE_ADDRESS Bridge contract address (required)"
|
|
echo " RPC_URL RPC endpoint URL (required)"
|
|
echo " PRIVATE_KEY Private key for signing (required)"
|
|
echo " FEE_TOKEN Fee token address (0x0 for native, or LINK)"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " # Dry run: estimate without sending"
|
|
echo " export BRIDGE_ADDRESS=\$CCIPWETH9_BRIDGE_CHAIN138"
|
|
echo " $0 --selector 5009297550715157269 --receiver 0xYourAddress --amount 100000000000000000 --dry-run"
|
|
echo ""
|
|
echo " # Actual send"
|
|
echo " $0 --selector 5009297550715157269 --receiver 0xYourAddress --amount 100000000000000000"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Validate required arguments
|
|
if [ -z "$SELECTOR" ] || [ -z "$RECEIVER" ] || [ -z "$AMOUNT" ]; then
|
|
echo "Error: Missing required arguments"
|
|
echo "Usage: $0 --selector <chain-selector> --receiver <address> --amount <wei> [--dry-run]"
|
|
exit 1
|
|
fi
|
|
|
|
# Validate required environment variables
|
|
if [ -z "${BRIDGE_ADDRESS:-}" ]; then
|
|
echo "Error: BRIDGE_ADDRESS not set"
|
|
echo "Set it to the bridge contract address:"
|
|
echo " export BRIDGE_ADDRESS=\$CCIPWETH9_BRIDGE_CHAIN138"
|
|
exit 1
|
|
fi
|
|
|
|
if [ -z "${RPC_URL:-}" ]; then
|
|
echo "Error: RPC_URL not set"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$DRY_RUN" = false ] && [ -z "${PRIVATE_KEY:-}" ]; then
|
|
echo "Error: PRIVATE_KEY not set (required for actual send)"
|
|
exit 1
|
|
fi
|
|
|
|
# Default to LINK token if FEE_TOKEN not set
|
|
FEE_TOKEN="${FEE_TOKEN:-${LINK_TOKEN:-0x0000000000000000000000000000000000000000}}"
|
|
|
|
# Validate cast is available
|
|
if ! cast --version &>/dev/null; then
|
|
echo "Error: cast (Foundry) not found. Please install Foundry:"
|
|
echo " curl -L https://foundry.paradigm.xyz | bash"
|
|
exit 1
|
|
fi
|
|
|
|
BRIDGE_ADDRESS=$(cast --to-checksum-address "$BRIDGE_ADDRESS")
|
|
RECEIVER=$(cast --to-checksum-address "$RECEIVER")
|
|
|
|
echo "=========================================="
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "CCIP Send (DRY RUN)"
|
|
else
|
|
echo "CCIP Send"
|
|
fi
|
|
echo "=========================================="
|
|
echo "Bridge Address: $BRIDGE_ADDRESS"
|
|
echo "Chain Selector: $SELECTOR"
|
|
echo "Receiver: $RECEIVER"
|
|
echo "Amount: $AMOUNT wei"
|
|
echo "Fee Token: $FEE_TOKEN"
|
|
echo ""
|
|
|
|
# Estimate fee first
|
|
echo "Estimating fee..."
|
|
FEE=$(cast call "$BRIDGE_ADDRESS" \
|
|
"estimateCcipFee(uint64,address,uint256)(uint256)" \
|
|
"$SELECTOR" \
|
|
"$RECEIVER" \
|
|
"$AMOUNT" \
|
|
--rpc-url "$RPC_URL" 2>/dev/null || echo "0")
|
|
|
|
if [ "$FEE" = "0" ] || [ -z "$FEE" ]; then
|
|
echo "⚠ Warning: Could not estimate fee. Proceeding anyway..."
|
|
FEE="0"
|
|
else
|
|
FEE_DECIMAL=$(cast --to-dec "$FEE" 2>/dev/null || echo "$FEE")
|
|
echo "Estimated fee: $FEE wei ($FEE_DECIMAL)"
|
|
fi
|
|
|
|
# Check sender balance and approval
|
|
if [ "$DRY_RUN" = false ]; then
|
|
SENDER_ADDRESS=$(cast wallet address --private-key "$PRIVATE_KEY" 2>/dev/null || echo "")
|
|
if [ -z "$SENDER_ADDRESS" ]; then
|
|
echo "Error: Could not derive address from private key"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
echo "Checking sender balance and approvals..."
|
|
echo "Sender: $SENDER_ADDRESS"
|
|
|
|
# Determine which token (WETH9 or WETH10) based on bridge address
|
|
# This is a heuristic - you may need to adjust based on your setup
|
|
if [[ "$BRIDGE_ADDRESS" == *"WETH9"* ]] || [[ "${BRIDGE_ADDRESS:-}" == "${CCIPWETH9_BRIDGE_CHAIN138:-}" ]] || [[ "${BRIDGE_ADDRESS:-}" == "${CCIPWETH9_BRIDGE_MAINNET:-}" ]]; then
|
|
TOKEN_ADDRESS="${WETH9_ADDRESS:-0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2}"
|
|
TOKEN_NAME="WETH9"
|
|
else
|
|
TOKEN_ADDRESS="${WETH10_ADDRESS:-0xf4BB2e28688e89fCcE3c0580D37d36A7672E8A9f}"
|
|
TOKEN_NAME="WETH10"
|
|
fi
|
|
|
|
# Check token balance
|
|
BALANCE=$(cast call "$TOKEN_ADDRESS" \
|
|
"balanceOf(address)(uint256)" \
|
|
"$SENDER_ADDRESS" \
|
|
--rpc-url "$RPC_URL" 2>/dev/null || echo "0")
|
|
|
|
if [ "$(cast --to-dec "$BALANCE" 2>/dev/null || echo "0")" -lt "$(cast --to-dec "$AMOUNT" 2>/dev/null || echo "0")" ]; then
|
|
echo "⚠ Warning: Insufficient $TOKEN_NAME balance"
|
|
echo " Required: $AMOUNT wei"
|
|
echo " Available: $BALANCE wei"
|
|
else
|
|
echo "✓ Sufficient $TOKEN_NAME balance"
|
|
fi
|
|
|
|
# Check approval
|
|
ALLOWANCE=$(cast call "$TOKEN_ADDRESS" \
|
|
"allowance(address,address)(uint256)" \
|
|
"$SENDER_ADDRESS" \
|
|
"$BRIDGE_ADDRESS" \
|
|
--rpc-url "$RPC_URL" 2>/dev/null || echo "0")
|
|
|
|
if [ "$(cast --to-dec "$ALLOWANCE" 2>/dev/null || echo "0")" -lt "$(cast --to-dec "$AMOUNT" 2>/dev/null || echo "0")" ]; then
|
|
echo "⚠ Warning: Insufficient approval"
|
|
echo " Required: $AMOUNT wei"
|
|
echo " Approved: $ALLOWANCE wei"
|
|
echo ""
|
|
echo "Approve the bridge to spend tokens:"
|
|
echo " cast send $TOKEN_ADDRESS \\"
|
|
echo " \"approve(address,uint256)(bool)\" \\"
|
|
echo " $BRIDGE_ADDRESS $AMOUNT \\"
|
|
echo " --private-key \$PRIVATE_KEY \\"
|
|
echo " --rpc-url \$RPC_URL"
|
|
if [ "$DRY_RUN" = false ]; then
|
|
read -p "Continue anyway? [y/N]: " confirm
|
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted. Please approve first."
|
|
exit 1
|
|
fi
|
|
fi
|
|
else
|
|
echo "✓ Sufficient approval"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
|
|
if [ "$DRY_RUN" = true ]; then
|
|
echo "DRY RUN: Simulating bridgeOut call..."
|
|
cast call "$BRIDGE_ADDRESS" \
|
|
"bridgeOut(uint64,address,uint256,address)(bytes32)" \
|
|
"$SELECTOR" \
|
|
"$RECEIVER" \
|
|
"$AMOUNT" \
|
|
"$FEE_TOKEN" \
|
|
--rpc-url "$RPC_URL" || {
|
|
echo "Error: Dry run failed"
|
|
exit 1
|
|
}
|
|
echo "✓ Dry run successful (transaction would succeed)"
|
|
else
|
|
echo "Sending transaction..."
|
|
|
|
# Determine value to send (if using native fees)
|
|
VALUE="0"
|
|
if [ "$FEE_TOKEN" = "0x0000000000000000000000000000000000000000" ]; then
|
|
VALUE="$FEE"
|
|
fi
|
|
|
|
TX_HASH=$(cast send "$BRIDGE_ADDRESS" \
|
|
"bridgeOut(uint64,address,uint256,address)(bytes32)" \
|
|
"$SELECTOR" \
|
|
"$RECEIVER" \
|
|
"$AMOUNT" \
|
|
"$FEE_TOKEN" \
|
|
--private-key "$PRIVATE_KEY" \
|
|
--rpc-url "$RPC_URL" \
|
|
--value "$VALUE" \
|
|
--gas-limit 500000 2>&1 | grep -oP '0x[a-fA-F0-9]{64}' || echo "")
|
|
|
|
if [ -z "$TX_HASH" ]; then
|
|
echo "Error: Transaction failed or hash not found"
|
|
exit 1
|
|
fi
|
|
|
|
echo "✓ Transaction sent!"
|
|
echo " Transaction Hash: $TX_HASH"
|
|
echo ""
|
|
echo "Waiting for confirmation..."
|
|
sleep 5
|
|
|
|
# Try to get receipt
|
|
RECEIPT=$(cast receipt "$TX_HASH" --rpc-url "$RPC_URL" 2>/dev/null || echo "")
|
|
if [ -n "$RECEIPT" ]; then
|
|
echo "✓ Transaction confirmed"
|
|
else
|
|
echo "⚠ Transaction may still be pending. Check with:"
|
|
echo " cast receipt $TX_HASH --rpc-url $RPC_URL"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo "Complete"
|
|
echo "=========================================="
|
|
|