-
Notifications
You must be signed in to change notification settings - Fork 2
Manual Liquidation forked Mainnet test #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jordanschalm
wants to merge
7
commits into
main
Choose a base branch
from
jord/liquidation-test-mainnet-fork
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
34058e3
Add manual liquidation smoke test for mainnet fork
jordanschalm a4c5863
Move fork test to integration/liquidation-fork-mainnet/ with isolated…
jordanschalm 920a2ff
Extract fork config from run.sh into committed flow.fork.json
jordanschalm 9f3364b
Protect flow.json from CLI write-back during fork tests
jordanschalm 0fe2be6
Copy flow.json to project root under different name for fork tests
jordanschalm 4a50576
Apply suggestion from @jordanschalm
jordanschalm 5dd2b7c
Merge branch 'main' into jord/liquidation-test-mainnet-fork
jordanschalm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,3 +7,4 @@ coverage.json | |
| /.pr-body.md | ||
| /.github/pr_bodies/ | ||
| lcov.info | ||
| flow.fork-test.json | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| tmp/ |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| { | ||
| "contracts": { | ||
| "FungibleTokenConnectors": { | ||
| "source": "__PROJECT_DIR__/FlowActions/cadence/contracts/connectors/FungibleTokenConnectors.cdc", | ||
| "aliases": { | ||
| "mainnet": "6d888f175c158410", | ||
| "testing": "0000000000000006" | ||
| } | ||
| } | ||
| }, | ||
| "networks": { | ||
| "mainnet-fork": { | ||
| "host": "127.0.0.1:3569", | ||
| "fork": "mainnet" | ||
| } | ||
| }, | ||
| "accounts": { | ||
| "mainnet-fork-deployer": { | ||
| "address": "6b00ff876c299c61", | ||
| "key": { | ||
| "type": "file", | ||
| "location": "__PROJECT_DIR__/emulator-account.pkey" | ||
| } | ||
| }, | ||
| "mainnet-fork-fyv-deployer": { | ||
| "address": "b1d63873c3cc9f79", | ||
| "key": { | ||
| "type": "file", | ||
| "location": "__PROJECT_DIR__/emulator-account.pkey" | ||
| } | ||
| }, | ||
| "fork-flow-source": { | ||
| "address": "92674150c9213fc9", | ||
| "key": { | ||
| "type": "file", | ||
| "location": "__PROJECT_DIR__/emulator-account.pkey" | ||
| } | ||
| }, | ||
| "mainnet-fork-defi-deployer": { | ||
| "address": "6d888f175c158410", | ||
| "key": { | ||
| "type": "file", | ||
| "location": "__PROJECT_DIR__/emulator-account.pkey" | ||
| } | ||
| } | ||
| }, | ||
| "deployments": { | ||
| "mainnet-fork": { | ||
| "mainnet-fork-deployer": [ | ||
| "FlowALPMath", | ||
| { | ||
| "name": "MOET", | ||
| "args": [{ "value": "0.00000000", "type": "UFix64" }] | ||
| }, | ||
| "FlowALPv0" | ||
| ], | ||
| "mainnet-fork-fyv-deployer": [ | ||
| "MockDexSwapper", | ||
| "MockOracle" | ||
| ] | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,310 @@ | ||
| #!/bin/bash | ||
| # | ||
| # Manual Liquidation Smoke Test on Mainnet Fork | ||
| # | ||
| # This test should be run from the repo root: `./integration/liquidation-fork-mainnet/run.sh` | ||
| # | ||
| # A mainnet fork must be running locally before running this test (ports 3569/8888/8080). | ||
| # - Run `flow init` to create a new project with default settings | ||
| # - Run `flow emulator --fork mainnet` within the new project directory | ||
| # | ||
| # This test: | ||
| # 1. Switches the pool to use MockOracle and MockDexSwapper | ||
| # 2. Creates user and liquidator accounts | ||
| # 3. User opens a position (deposit FLOW, borrow MOET) | ||
| # 4. Attempts manual liquidation (should FAIL - position is healthy) | ||
| # 5. Drops FLOW price to make position unhealthy | ||
| # 6. Attempts manual liquidation (should SUCCEED) | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| NETWORK="mainnet-fork" | ||
| DEPLOYER="mainnet-fork-deployer" # 6b00ff876c299c61 - FlowALP protocol account | ||
| FYV_DEPLOYER="mainnet-fork-fyv-deployer" # b1d63873c3cc9f79 - MockOracle/MockDex deployer | ||
| FLOW_SOURCE="fork-flow-source" # 92674150c9213fc9 - well-funded FLOW account | ||
|
|
||
| FLOW_TOKEN_ID="A.1654653399040a61.FlowToken.Vault" | ||
| MOET_TOKEN_ID="A.6b00ff876c299c61.MOET.Vault" | ||
| MOET_VAULT_STORAGE_PATH="/storage/moetTokenVault_0x6b00ff876c299c61" | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| PROJECT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" | ||
|
|
||
| # Working directory for this test - all temporary/generated files go here | ||
| WORK_DIR="$SCRIPT_DIR/tmp" | ||
| KEY_FILE="$WORK_DIR/test-key.pkey" | ||
| # Runtime fork config (expanded from committed flow.fork.json template) | ||
| FORK_CONFIG="$WORK_DIR/flow.fork.json" | ||
| # Runtime config for dynamically-created test accounts (fork-user, fork-liquidator) | ||
| DYNAMIC_CONFIG="$WORK_DIR/accounts.json" | ||
|
|
||
| # Clean previous run and create fresh working directory | ||
| rm -rf "$WORK_DIR" | ||
| mkdir -p "$WORK_DIR" | ||
|
|
||
| # Copy project flow.json to a sibling file at the project root so relative paths | ||
| # (./cadence/...) still resolve. Cleaned up on exit; gitignored in case of interruption. | ||
| BASE_CONFIG="$PROJECT_DIR/flow.fork-test.json" | ||
| cp "$PROJECT_DIR/flow.json" "$BASE_CONFIG" | ||
| trap 'rm -f "$BASE_CONFIG"' EXIT | ||
|
|
||
| # Expand __PROJECT_DIR__ placeholder in the committed config template | ||
| sed "s|__PROJECT_DIR__|$PROJECT_DIR|g" "$SCRIPT_DIR/flow.fork.json" > "$FORK_CONFIG" | ||
|
|
||
| # Initialize the dynamic accounts config (empty, populated later) | ||
| echo '{}' > "$DYNAMIC_CONFIG" | ||
|
|
||
| # Helper to attach config flags to flow command. | ||
| # Only references the copy and overlay configs — the original flow.json is never touched. | ||
| flow_cmd() { | ||
| flow "$@" -f "$BASE_CONFIG" -f "$FORK_CONFIG" -f "$DYNAMIC_CONFIG" | ||
| } | ||
|
|
||
| # Helper: send a transaction and assert success | ||
| send_tx() { | ||
| local description="$1" | ||
| shift | ||
| echo ">> $description" | ||
| if ! flow_cmd transactions send "$@" --network "$NETWORK"; then | ||
| echo "FAIL: $description" | ||
| exit 1 | ||
| fi | ||
| echo "" | ||
| } | ||
|
|
||
| # Helper: send a transaction and assert failure (reverted on-chain) | ||
| send_tx_expect_fail() { | ||
| local description="$1" | ||
| shift | ||
| echo ">> $description (expecting failure)" | ||
| # Capture output; the CLI may return non-zero for reverted txns | ||
| local result | ||
| result=$(flow_cmd transactions send "$@" --network "$NETWORK" --output json 2>&1) || true | ||
| # Check if the transaction had an error (statusCode != 0 means reverted) | ||
| local status_code | ||
| status_code=$(echo "$result" | jq -r '.statusCode // 0' 2>/dev/null || echo "1") | ||
| if [ "$status_code" = "0" ]; then | ||
| # Also check for explicit error field | ||
| local has_error | ||
| has_error=$(echo "$result" | jq -r 'if .error then "yes" else "no" end' 2>/dev/null || echo "no") | ||
| if [ "$has_error" = "no" ]; then | ||
| echo "FAIL: Expected transaction to fail but it succeeded: $description" | ||
| echo "$result" | jq . 2>/dev/null || echo "$result" | ||
| exit 1 | ||
| fi | ||
| fi | ||
| echo " (transaction failed as expected)" | ||
| echo "" | ||
| } | ||
|
|
||
| echo "============================================" | ||
| echo " Manual Liquidation Smoke Test (Fork)" | ||
| echo "============================================" | ||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 0: Generate a test key pair | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 0: Setup ---" | ||
|
|
||
| KEY_JSON=$(flow keys generate --output json --sig-algo ECDSA_P256) | ||
| PRIV_KEY=$(echo "$KEY_JSON" | jq -r '.private') | ||
| PUB_KEY=$(echo "$KEY_JSON" | jq -r '.public') | ||
| printf '%s' "$PRIV_KEY" > "$KEY_FILE" | ||
| echo "Generated test key pair" | ||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 0.5: Deploy FungibleTokenConnectors (not on mainnet, needed by create_position) | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 0.5: Deploy FungibleTokenConnectors ---" | ||
| echo ">> Deploying FungibleTokenConnectors to DeFi deployer account" | ||
| DEPLOY_OUTPUT=$(flow_cmd accounts add-contract \ | ||
| "$PROJECT_DIR/FlowActions/cadence/contracts/connectors/FungibleTokenConnectors.cdc" \ | ||
| --signer mainnet-fork-defi-deployer --network "$NETWORK" 2>&1) || { | ||
| if echo "$DEPLOY_OUTPUT" | grep -q "contract already exists"; then | ||
| echo " (contract already deployed, skipping)" | ||
| else | ||
| echo "FAIL: Deploy FungibleTokenConnectors" | ||
| echo "$DEPLOY_OUTPUT" | ||
| exit 1 | ||
| fi | ||
| } | ||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 1: Switch pool to MockOracle and MockDexSwapper | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 1: Configure pool with mock oracle and DEX ---" | ||
|
|
||
| send_tx "Set MockOracle on pool" \ | ||
| "$SCRIPT_DIR/transactions/set_mock_oracle.cdc" \ | ||
| --signer "$DEPLOYER" | ||
|
|
||
| send_tx "Set MockDexSwapper on pool" \ | ||
| "$SCRIPT_DIR/transactions/set_mock_dex.cdc" \ | ||
| --signer "$DEPLOYER" | ||
|
|
||
| # Set FLOW price = 1.0 MOET in the oracle | ||
| send_tx "Set oracle price: FLOW = 1.0" \ | ||
| "$PROJECT_DIR/cadence/tests/transactions/mock-oracle/set_price.cdc" \ | ||
| "$FLOW_TOKEN_ID" 1.0 \ | ||
| --signer "$FYV_DEPLOYER" | ||
|
|
||
| # Set DEX price: 1 FLOW -> 1 MOET (priceRatio = 1.0) | ||
| send_tx "Set DEX price: FLOW/MOET = 1.0" \ | ||
| "$PROJECT_DIR/cadence/tests/transactions/mock-dex-swapper/set_mock_dex_price_for_pair.cdc" \ | ||
| "$FLOW_TOKEN_ID" "$MOET_TOKEN_ID" "$MOET_VAULT_STORAGE_PATH" 1.0 \ | ||
| --signer "$DEPLOYER" | ||
|
|
||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 2: Create user and liquidator accounts | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 2: Create test accounts ---" | ||
|
|
||
| USER_RESULT=$(flow_cmd accounts create --key "$PUB_KEY" --signer "$DEPLOYER" --network "$NETWORK" --output json) | ||
| USER_ADDR=$(echo "$USER_RESULT" | jq -r '.address') | ||
| echo "Created user account: $USER_ADDR" | ||
|
|
||
| LIQUIDATOR_RESULT=$(flow_cmd accounts create --key "$PUB_KEY" --signer "$DEPLOYER" --network "$NETWORK" --output json) | ||
| LIQUIDATOR_ADDR=$(echo "$LIQUIDATOR_RESULT" | jq -r '.address') | ||
| echo "Created liquidator account: $LIQUIDATOR_ADDR" | ||
|
|
||
| # Add test accounts to our extra config (using the generated key) | ||
| jq --arg addr "${USER_ADDR#0x}" --arg keyfile "$KEY_FILE" \ | ||
| '.accounts["fork-user"] = {"address": $addr, "key": {"type": "file", "location": $keyfile}}' \ | ||
| "$DYNAMIC_CONFIG" > "$WORK_DIR/flow.json.tmp" && mv "$WORK_DIR/flow.json.tmp" "$DYNAMIC_CONFIG" | ||
|
|
||
| jq --arg addr "${LIQUIDATOR_ADDR#0x}" --arg keyfile "$KEY_FILE" \ | ||
| '.accounts["fork-liquidator"] = {"address": $addr, "key": {"type": "file", "location": $keyfile}}' \ | ||
| "$DYNAMIC_CONFIG" > "$WORK_DIR/flow.json.tmp" && mv "$WORK_DIR/flow.json.tmp" "$DYNAMIC_CONFIG" | ||
|
|
||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 3: Fund accounts | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 3: Fund accounts ---" | ||
|
|
||
| # Transfer FLOW to user from well-funded account | ||
| send_tx "Transfer 1000 FLOW to user" \ | ||
| "$PROJECT_DIR/cadence/transactions/flowtoken/transfer_flowtoken.cdc" \ | ||
| "$USER_ADDR" 1000.0 \ | ||
| --signer "$FLOW_SOURCE" | ||
|
|
||
| # Setup MOET vault for liquidator | ||
| send_tx "Setup MOET vault for liquidator" \ | ||
| "$PROJECT_DIR/cadence/transactions/moet/setup_vault.cdc" \ | ||
| --signer fork-liquidator | ||
|
|
||
| # Mint MOET to liquidator (signer must have MOET Minter - the deployer account) | ||
| send_tx "Mint 1000 MOET to liquidator" \ | ||
| "$PROJECT_DIR/cadence/transactions/moet/mint_moet.cdc" \ | ||
| "$LIQUIDATOR_ADDR" 1000.0 \ | ||
| --signer "$DEPLOYER" | ||
|
|
||
| # Also setup MOET vault for user (needed for receiving borrowed MOET) | ||
| send_tx "Setup MOET vault for user" \ | ||
| "$PROJECT_DIR/cadence/transactions/moet/setup_vault.cdc" \ | ||
| --signer fork-user | ||
|
|
||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 4: Grant beta access and create position | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 4: Grant beta access and open position ---" | ||
|
|
||
| # Grant beta pool access to user (requires both admin and user as signers) | ||
| send_tx "Grant beta pool access to user" \ | ||
| "$PROJECT_DIR/cadence/tests/transactions/flow-alp/pool-management/03_grant_beta.cdc" \ | ||
| --authorizer "$DEPLOYER",fork-user \ | ||
| --proposer "$DEPLOYER" \ | ||
| --payer "$DEPLOYER" | ||
|
|
||
| # User creates position: deposit 1000 FLOW, borrow MOET | ||
| echo ">> User creates position (deposit 1000 FLOW)" | ||
| CREATE_POS_RESULT=$(flow_cmd transactions send \ | ||
| "$PROJECT_DIR/cadence/transactions/flow-alp/position/create_position.cdc" \ | ||
| 1000.0 /storage/flowTokenVault true \ | ||
| --signer fork-user --network "$NETWORK" --output json) | ||
|
|
||
| if echo "$CREATE_POS_RESULT" | jq -e '.error' > /dev/null 2>&1; then | ||
| echo "FAIL: User creates position" | ||
| echo "$CREATE_POS_RESULT" | jq -r '.error' | ||
| exit 1 | ||
| fi | ||
| echo " Position created successfully" | ||
|
|
||
| # Extract position ID from the Opened event | ||
| PID=$(echo "$CREATE_POS_RESULT" | jq '[.events[] | select(.type | contains("FlowALPv0.Opened")) | .values.value.fields[] | select(.name == "pid") | .value.value | tonumber] | first') | ||
| echo "New position ID: $PID" | ||
| echo "" | ||
|
|
||
| # Check position health | ||
| echo ">> Check position health (should be healthy, >= 1.0)" | ||
| HEALTH_JSON=$(flow_cmd scripts execute "$PROJECT_DIR/cadence/scripts/flow-alp/position_health.cdc" "$PID" --network "$NETWORK" --output json) | ||
| HEALTH=$(echo "$HEALTH_JSON" | jq -r '.value') | ||
| echo "Position health: $HEALTH" | ||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 5: Attempt liquidation on healthy position (should FAIL) | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 5: Attempt liquidation on healthy position (expect failure) ---" | ||
|
|
||
| send_tx_expect_fail "Manual liquidation on healthy position" \ | ||
| "$PROJECT_DIR/cadence/transactions/flow-alp/pool-management/manual_liquidation.cdc" \ | ||
| "$PID" "$MOET_TOKEN_ID" "$FLOW_TOKEN_ID" 190.0 100.0 \ | ||
| --signer fork-liquidator | ||
|
|
||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 6: Drop FLOW price to make position unhealthy | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 6: Drop FLOW price to 0.5 ---" | ||
|
|
||
| send_tx "Set oracle price: FLOW = 0.5" \ | ||
| "$PROJECT_DIR/cadence/tests/transactions/mock-oracle/set_price.cdc" \ | ||
| "$FLOW_TOKEN_ID" 0.5 \ | ||
| --signer "$FYV_DEPLOYER" | ||
|
|
||
| send_tx "Set DEX price: FLOW/MOET = 0.5" \ | ||
| "$PROJECT_DIR/cadence/tests/transactions/mock-dex-swapper/set_mock_dex_price_for_pair.cdc" \ | ||
| "$FLOW_TOKEN_ID" "$MOET_TOKEN_ID" "$MOET_VAULT_STORAGE_PATH" 0.5 \ | ||
| --signer "$DEPLOYER" | ||
|
|
||
| # Check position health again | ||
| echo ">> Check position health (should be unhealthy, < 1.0)" | ||
| HEALTH_JSON=$(flow_cmd scripts execute "$PROJECT_DIR/cadence/scripts/flow-alp/position_health.cdc" "$PID" --network "$NETWORK" --output json) | ||
| HEALTH=$(echo "$HEALTH_JSON" | jq -r '.value') | ||
| echo "Position health after price drop: $HEALTH" | ||
| echo "" | ||
|
|
||
| # -------------------------------------------------------------------------- | ||
| # Step 7: Manual liquidation (should SUCCEED) | ||
| # -------------------------------------------------------------------------- | ||
| echo "--- Step 7: Manual liquidation on unhealthy position (expect success) ---" | ||
|
|
||
| # Liquidator repays 100 MOET, seizes 190 FLOW | ||
| # DEX quote for 100 MOET at price 0.5: inAmount = 100/0.5 = 200 FLOW | ||
| # seizeAmount (190) < dexQuote (200) ✓ | ||
| # Post-liquidation health will be < target (1.05) ✓ | ||
| send_tx "Manual liquidation: repay 100 MOET, seize 190 FLOW" \ | ||
| "$PROJECT_DIR/cadence/transactions/flow-alp/pool-management/manual_liquidation.cdc" \ | ||
| "$PID" "$MOET_TOKEN_ID" "$FLOW_TOKEN_ID" 190.0 100.0 \ | ||
| --signer fork-liquidator | ||
|
|
||
| # Check post-liquidation health | ||
| echo ">> Check position health after liquidation" | ||
| HEALTH_JSON=$(flow_cmd scripts execute "$PROJECT_DIR/cadence/scripts/flow-alp/position_health.cdc" "$PID" --network "$NETWORK" --output json) | ||
| HEALTH=$(echo "$HEALTH_JSON" | jq -r '.value') | ||
| echo "Position health after liquidation: $HEALTH" | ||
| echo "" | ||
|
|
||
| echo "============================================" | ||
| echo " ALL TESTS PASSED" | ||
| echo "============================================" | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.