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
127 changes: 127 additions & 0 deletions packages/horizon/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,130 @@ To verify contracts on a network, run the following commands:
npx hardhat ignition verify --network <network> --include-unrelated-contracts <ignition-deployment-id>
./scripts/post-verify
```

## Operational Tasks

Operational tasks for post-migration maintenance and fund recovery are located in `tasks/ops/`.

### Configuration

Set the subgraph API key for querying The Graph Network:

```bash
npx hardhat vars set SUBGRAPH_API_KEY
```

### Legacy Allocations

Force close legacy allocations that haven't been migrated to Horizon.

#### Query Legacy Allocations

Query and report active legacy allocations from the Graph Network subgraph:

```bash
npx hardhat ops:allocations:query --network arbitrumOne
```

Options:
- `--subgraph-api-key`: API key for The Graph Network gateway
- `--excluded-indexers`: Comma-separated indexer addresses to exclude (default: upgrade indexer)
- `--output-dir`: Output directory for reports (default: `./ops-output`)

#### Close Legacy Allocations

Force close legacy allocations:

```bash
# Generate calldata for external execution (Fireblocks, Safe, etc.)
npx hardhat ops:allocations:close --network arbitrumOne --calldata-only

# Execute directly with secure accounts
npx hardhat ops:allocations:close --network arbitrumOne

# Dry run to simulate without executing
npx hardhat ops:allocations:close --network arbitrumOne --dry-run
```

Options:
- `--input-file`: JSON file from query task (if not provided, queries subgraph)
- `--account-index`: Derivation path index for the account (default: 0)
- `--calldata-only`: Generate calldata without executing
- `--dry-run`: Simulate without executing

### TAP Escrow Recovery

Recover GRT funds from the TAP v1 Escrow contract.

#### Query Escrow Accounts

Query and report TAP escrow accounts:

```bash
npx hardhat ops:escrow:query --network arbitrumOne
```

Options:
- `--subgraph-api-key`: API key for The Graph Network gateway
- `--sender-addresses`: Comma-separated sender addresses to query
- `--output-dir`: Output directory for reports (default: `./ops-output`)

#### Thaw Escrow Funds

Initiate the 30-day thawing period for escrow funds:

```bash
# Generate calldata for external execution
npx hardhat ops:escrow:thaw --network arbitrumOne --calldata-only

# Execute directly
npx hardhat ops:escrow:thaw --network arbitrumOne

# Dry run
npx hardhat ops:escrow:thaw --network arbitrumOne --dry-run
```

Options:
- `--input-file`: JSON file from query task
- `--account-index`: Derivation path index for the gateway account (default: 0)
- `--escrow-address`: TAP Escrow contract address
- `--calldata-only`: Generate calldata without executing
- `--dry-run`: Simulate without executing

#### Withdraw Escrow Funds

Withdraw thawed funds after the 30-day thawing period:

```bash
# Generate calldata for external execution
npx hardhat ops:escrow:withdraw --network arbitrumOne --calldata-only

# Execute directly
npx hardhat ops:escrow:withdraw --network arbitrumOne
```

Options:
- `--input-file`: JSON file from query task
- `--account-index`: Derivation path index for the gateway account (default: 0)
- `--escrow-address`: TAP Escrow contract address
- `--calldata-only`: Generate calldata without executing
- `--dry-run`: Simulate without executing

### Output Files

All operational task outputs are saved to `ops-output/` (configurable via `--output-dir`):

```
ops-output/
├── allocations-YYYY-MM-DD-HHMMSS.json # Legacy allocation data
├── allocations-YYYY-MM-DD-HHMMSS.csv # CSV for spreadsheet review
├── escrow-accounts-YYYY-MM-DD-HHMMSS.json # Escrow account data
├── escrow-accounts-YYYY-MM-DD-HHMMSS.csv # CSV for spreadsheet review
├── close-allocations-results-*.json # Allocation closing results
├── thaw-escrow-results-*.json # Thaw transaction results
├── withdraw-escrow-results-*.json # Withdraw transaction results
└── calldata/
├── close-allocations-*.json # Calldata for allocation closing
├── thaw-escrow-*.json # Calldata for thawing
└── withdraw-escrow-*.json # Calldata for withdrawal
```
1 change: 1 addition & 0 deletions packages/horizon/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"test:self": "forge test",
"test:deployment": "SECURE_ACCOUNTS_DISABLE_PROVIDER=true hardhat test test/deployment/*.ts",
"test:integration": "./scripts/integration",
"test:ops": "./scripts/ops-test",
"test:coverage": "pnpm build && pnpm test:coverage:self",
"test:coverage:self": "forge coverage",
"prepublishOnly": "pnpm run build"
Expand Down
55 changes: 55 additions & 0 deletions packages/horizon/scripts/ops-poc
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash

set -eo pipefail

# Add foundry to PATH
export PATH="$HOME/.foundry/bin:$PATH"

# Proof-of-concept script for fork testing with impersonated accounts
# This validates that we can:
# 1. Fork Arbitrum One
# 2. Impersonate accounts
# 3. Send transactions from impersonated accounts

export SECURE_ACCOUNTS_DISABLE_PROVIDER=true

# Alchemy RPC for Arbitrum One
BLOCKCHAIN_RPC=${BLOCKCHAIN_RPC:-"https://arb-mainnet.g.alchemy.com/v2/yRreth_oJQQeP_80z8dn-yR1LoxDKK_G"}

# Function to cleanup resources
cleanup() {
if [ ! -z "$NODE_PID" ] && [ "$STARTED_NODE" = true ]; then
echo "Cleaning up Hardhat node (PID: $NODE_PID)..."
kill -TERM -- -$NODE_PID 2>/dev/null || true
fi
}

trap cleanup EXIT

echo "=== Ops Fork Testing POC ==="
echo ""

# Check if hardhat node is already running on port 8545
STARTED_NODE=false
if lsof -i:8545 > /dev/null 2>&1; then
echo "Hardhat node already running on port 8545, using existing node"
NODE_PID=$(lsof -t -i:8545)
else
echo "Starting local hardhat node forked from Arbitrum One..."
npx hardhat node --fork $BLOCKCHAIN_RPC > node.log 2>&1 &
NODE_PID=$!
STARTED_NODE=true

# Wait for node to start
echo "Waiting for node to start..."
sleep 10
fi

echo "Node ready. Running POC task..."
echo ""

# Run the POC task
npx hardhat ops:poc --network localhost

echo ""
echo "=== POC Complete ==="
200 changes: 200 additions & 0 deletions packages/horizon/scripts/ops-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
#!/bin/bash
#
# Fork Test Script for TAP Escrow Recovery & Legacy Allocation Closure
#
# This script orchestrates a full test flow against a forked Arbitrum One chain
# to validate all operational tasks before running on mainnet.
#
# Prerequisites:
# - SUBGRAPH_API_KEY must be set: npx hardhat vars set SUBGRAPH_API_KEY
# - Foundry must be installed: curl -L https://foundry.paradigm.xyz | bash && foundryup
#
# Usage:
# ./scripts/ops-test # Run with defaults
# TEST_LIMIT=5 ./scripts/ops-test # Process 5 entities per task
# BLOCKCHAIN_RPC=<url> ./scripts/ops-test # Use custom RPC endpoint
#

set -eo pipefail

# ============================================
# Configuration
# ============================================

export SECURE_ACCOUNTS_DISABLE_PROVIDER=true
export PATH="$HOME/.foundry/bin:$PATH"

BLOCKCHAIN_RPC=${BLOCKCHAIN_RPC:-"https://arb-mainnet.g.alchemy.com/v2/yRreth_oJQQeP_80z8dn-yR1LoxDKK_G"}
TEST_LIMIT=${TEST_LIMIT:-3}
OUTPUT_DIR="./ops-test-output"
NODE_LOG="$OUTPUT_DIR/node.log"

# ============================================
# Helper Functions
# ============================================

cleanup() {
if [ "$STARTED_NODE" = true ] && [ ! -z "$NODE_PID" ]; then
echo ""
echo "Cleaning up Hardhat node (PID: $NODE_PID)..."
kill -TERM $NODE_PID 2>/dev/null || true
wait $NODE_PID 2>/dev/null || true
fi
}

trap cleanup EXIT

wait_for_node() {
local max_attempts=30
local attempt=0
while ! curl -s http://localhost:8545 -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' > /dev/null 2>&1; do
attempt=$((attempt + 1))
if [ $attempt -ge $max_attempts ]; then
echo "ERROR: Hardhat node failed to start"
cat $NODE_LOG
exit 1
fi
sleep 1
done
}

# ============================================
# Main Script
# ============================================

echo "=============================================="
echo "=== Ops Fork Test ==="
echo "=============================================="
echo ""
echo "Configuration:"
echo " BLOCKCHAIN_RPC: $BLOCKCHAIN_RPC"
echo " TEST_LIMIT: $TEST_LIMIT"
echo " OUTPUT_DIR: $OUTPUT_DIR"
echo ""

# Create output directory
rm -rf $OUTPUT_DIR
mkdir -p $OUTPUT_DIR

# ============================================
# Phase 0: Start Fork
# ============================================

echo "=== Phase 0: Starting Arbitrum One Fork ==="
STARTED_NODE=false
NODE_PID=""

if lsof -i:8545 > /dev/null 2>&1; then
echo "Hardhat node already running on port 8545, using existing node"
NODE_PID=$(lsof -t -i:8545 | head -1)
else
echo "Starting local hardhat node forked from Arbitrum One..."
npx hardhat node --fork $BLOCKCHAIN_RPC > $NODE_LOG 2>&1 &
NODE_PID=$!
STARTED_NODE=true
echo "Waiting for node to start (PID: $NODE_PID)..."
wait_for_node
fi
echo "Node ready!"
echo ""

# ============================================
# Phase 1: Query (BEFORE any state changes)
# ============================================

echo "=== Phase 1: Query Data from Subgraph ==="
echo "Note: Queries must happen before fork state changes (subgraph reflects mainnet, not fork)"
echo ""

echo "Querying legacy allocations..."
npx hardhat ops:allocations:query --network localhost --output-dir $OUTPUT_DIR

echo ""
echo "Querying TAP escrow accounts..."
npx hardhat ops:escrow:query --network localhost --output-dir $OUTPUT_DIR

echo ""

# ============================================
# Phase 2: Close Allocations
# ============================================

echo "=== Phase 2: Close Legacy Allocations ==="
ALLOC_FILE=$(ls -t $OUTPUT_DIR/allocations-*.json 2>/dev/null | head -1)
if [ -z "$ALLOC_FILE" ]; then
echo "No allocations file found, skipping allocation closing"
else
echo "Using allocations from: $ALLOC_FILE"
npx hardhat ops:allocations:close --network localhost --input-file $ALLOC_FILE --limit $TEST_LIMIT --output-dir $OUTPUT_DIR

CLOSE_RESULTS=$(ls -t $OUTPUT_DIR/close-allocations-results-*.json 2>/dev/null | head -1)
if [ ! -z "$CLOSE_RESULTS" ]; then
echo ""
echo "Verifying closed allocations..."
npx hardhat ops:verify --type allocations --input-file $CLOSE_RESULTS --network localhost
fi
fi

echo ""

# ============================================
# Phase 3: Thaw Escrow
# ============================================

echo "=== Phase 3: Thaw TAP Escrow ==="
ESCROW_FILE=$(ls -t $OUTPUT_DIR/escrow-accounts-*.json 2>/dev/null | head -1)
if [ -z "$ESCROW_FILE" ]; then
echo "No escrow file found, skipping escrow operations"
else
echo "Using escrow accounts from: $ESCROW_FILE"
npx hardhat ops:escrow:thaw --network localhost --input-file $ESCROW_FILE --limit $TEST_LIMIT --output-dir $OUTPUT_DIR

THAW_RESULTS=$(ls -t $OUTPUT_DIR/thaw-escrow-results-*.json 2>/dev/null | head -1)
if [ ! -z "$THAW_RESULTS" ]; then
echo ""
echo "Verifying thawed accounts..."
npx hardhat ops:verify --type escrow-thaw --input-file $THAW_RESULTS --network localhost
fi
fi

echo ""

# ============================================
# Phase 4: Time Skip + Withdraw
# ============================================

echo "=== Phase 4: Fast-forward 30 Days & Withdraw ==="

if [ ! -z "$ESCROW_FILE" ]; then
echo "Advancing blockchain time by 30 days..."
npx hardhat ops:time-skip --days 30 --network localhost

echo ""
echo "Withdrawing thawed escrow accounts..."
npx hardhat ops:escrow:withdraw --network localhost --input-file $ESCROW_FILE --limit $TEST_LIMIT --output-dir $OUTPUT_DIR

WITHDRAW_RESULTS=$(ls -t $OUTPUT_DIR/withdraw-escrow-results-*.json 2>/dev/null | head -1)
if [ ! -z "$WITHDRAW_RESULTS" ]; then
echo ""
echo "Verifying withdrawals..."
npx hardhat ops:verify --type escrow-withdraw --input-file $WITHDRAW_RESULTS --original-file $ESCROW_FILE --network localhost
fi
else
echo "No escrow file found, skipping withdraw phase"
fi

echo ""

# ============================================
# Summary
# ============================================

echo "=============================================="
echo "=== All Tests Passed! ==="
echo "=============================================="
echo ""
echo "Output files:"
ls -la $OUTPUT_DIR/
echo ""
echo "Test completed successfully."
Loading
Loading