diff --git a/service_contracts/Makefile b/service_contracts/Makefile index 0b04f7ab..b0856aeb 100644 --- a/service_contracts/Makefile +++ b/service_contracts/Makefile @@ -5,6 +5,7 @@ RPC_URL ?= KEYSTORE ?= PASSWORD ?= CHALLENGE_FINALITY ?= +USDFC_PROXY_ADDRESS ?= 0x80B98d3aa09ffff255c3ba4A241111Ff1262F045 # Default target .PHONY: default @@ -134,7 +135,7 @@ ABI_CONTRACTS := \ SessionKeyRegistry # Generate ABI file targets -ABI_FILES := $(addprefix abi/,$(addsuffix .abi.json,$(ABI_CONTRACTS))) +ABI_FILES := $(addprefix abi/,$(addsuffix .abi.json,$(ABI_CONTRACTS))) abi/Usdfc.abi.json # Define a template for ABI extraction; we use a template approach instead of # defining a global pattern rule because patterns for files that don't exist @@ -153,6 +154,10 @@ endef # Generate rules for each contract using the template above $(foreach contract,$(ABI_CONTRACTS),$(eval $(call ABI_RULE,$(contract)))) +abi/Usdfc.abi.json: + @echo "Extracting ABI for Usdfc..." + @bash tools/fetch-usdfc-abi.sh $(USDFC_PROXY_ADDRESS) abi/Usdfc.abi.json + # Directory for ABIs abi: @mkdir -p abi diff --git a/service_contracts/abi/Usdfc.abi.json b/service_contracts/abi/Usdfc.abi.json new file mode 100644 index 00000000..19e1377f --- /dev/null +++ b/service_contracts/abi/Usdfc.abi.json @@ -0,0 +1,805 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + } + ], + "name": "AuthorizationCanceled", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "authorizer", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "nonce", + "type": "bytes32" + } + ], + "name": "AuthorizationUsed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_newBorrowerOperationsAddress", + "type": "address" + } + ], + "name": "BorrowerOperationsAddressChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_newStabilityPoolAddress", + "type": "address" + } + ], + "name": "StabilityPoolAddressChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "_troveManagerAddress", + "type": "address" + } + ], + "name": "TroveManagerAddressChanged", + "type": "event" + }, + { + "inputs": [], + "name": "CANCEL_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "RECEIVE_WITH_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "TRANSFER_WITH_AUTHORIZATION_TYPEHASH", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_authorizer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_nonce", + "type": "bytes32" + } + ], + "name": "authorizationState", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "borrowerOperationsAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_authorizer", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "_nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "_v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "cancelAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "domainSeparator", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_troveManagerAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_stabilityPoolAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_borrowerOperationsAddress", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + } + ], + "name": "nonces", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "address", + "name": "_spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "_v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "permit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "permitTypeHash", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validAfter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validBefore", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "_v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "receiveWithAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_poolAddress", + "type": "address" + }, + { + "internalType": "address", + "name": "_receiver", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "returnFromPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_poolAddress", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "sendToPool", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stabilityPoolAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_sender", + "type": "address" + }, + { + "internalType": "address", + "name": "_recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_from", + "type": "address" + }, + { + "internalType": "address", + "name": "_to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "_value", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validAfter", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_validBefore", + "type": "uint256" + }, + { + "internalType": "bytes32", + "name": "_nonce", + "type": "bytes32" + }, + { + "internalType": "uint8", + "name": "_v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "_r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_s", + "type": "bytes32" + } + ], + "name": "transferWithAuthorization", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "troveManagerAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "pure", + "type": "function" + } +] diff --git a/service_contracts/tools/fetch-usdfc-abi.sh b/service_contracts/tools/fetch-usdfc-abi.sh new file mode 100755 index 00000000..71cceadf --- /dev/null +++ b/service_contracts/tools/fetch-usdfc-abi.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash + +# +# Fetches the ABI for a Filecoin proxy contract's implementation and saves it. +# +# Usage: +# ./fetch-usdfc-abi.sh [OUTPUT_PATH] +# +# Example: +# ./fetch-usdfc-abi.sh 0x80B98d3aa09ffff255c3ba4A241111Ff1262F045 abi/Usdfc.abi.json +# + +set -euo pipefail + +# --- Helper Functions --- + +# Function to exit with a formatted error message. +die() { + echo "❌ ERROR: $1" >&2 + exit 1 +} + +# --- Dependency Checks --- + +# Ensure required command-line tools are installed. +for cmd in curl jq; do + if ! command -v "$cmd" &> /dev/null; then + die "'$cmd' is not installed. Please install it to continue." + fi +done + +# --- Argument Parsing --- + +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + echo "Usage: $0 [OUTPUT_PATH]" + exit 0 +fi + +# Check for required PROXY_ADDRESS argument. +if [[ -z "${1-}" ]]; then + die "Missing required argument: PROXY_ADDRESS.\nUsage: $0 [OUTPUT_PATH]" +fi + +PROXY_ADDRESS=$1 +# Use the second argument as the output path, or fall back to a default. +DEFAULT_OUTPUT_PATH="abi/Usdfc.abi.json" +OUTPUT_PATH=${2:-$DEFAULT_OUTPUT_PATH} + +# --- Main Logic --- + +# Create the output directory if it doesn't exist. +OUTPUT_DIR=$(dirname "$OUTPUT_PATH") +if ! mkdir -p "$OUTPUT_DIR"; then + die "Could not create output directory: $OUTPUT_DIR" +fi + +# 1. Fetch the implementation address from the proxy contract. +API_URL="https://filfox.info/api/v1/address/${PROXY_ADDRESS}/contract" + +# Use --fail to exit on HTTP errors (like 404), and a timeout to prevent hangs. +PROXY_RESPONSE=$(curl --silent --show-error --fail --connect-timeout 5 --max-time 15 "$API_URL") || + die "Failed to fetch data for proxy contract." + +IMPLEMENTATION_ADDRESS=$(echo "$PROXY_RESPONSE" | jq -r '.proxyImpl') + +if [[ -z "$IMPLEMENTATION_ADDRESS" || "$IMPLEMENTATION_ADDRESS" == "null" ]]; then + die "Could not find implementation address ('proxyImpl') in API response." +fi + +# 2. Fetch the ABI from the implementation contract. +IMPL_API_URL="https://filfox.info/api/v1/address/${IMPLEMENTATION_ADDRESS}/contract" + +IMPL_RESPONSE=$(curl --silent --show-error --fail --connect-timeout 5 --max-time 15 "$IMPL_API_URL") || + die "Failed to fetch data for implementation contract." + +# 3. Extract, parse, and save the ABI. +# This single command extracts the .abi string, parses it from a string into +# clean JSON, and saves it. It will fail if any step is unsuccessful. +echo "$IMPL_RESPONSE" | jq '.abi | fromjson' > "$OUTPUT_PATH" || { + # Clean up the potentially empty/corrupt file on failure. + rm -f "$OUTPUT_PATH" 2>/dev/null + die "Failed to parse ABI. The '.abi' field may be missing, null, or not valid JSON." +} + +echo "USDFC ABI saved successfully to $OUTPUT_PATH" \ No newline at end of file