diff --git a/contracts/wxdai.json b/contracts/wxdai.json new file mode 100644 index 00000000..e171f9ec --- /dev/null +++ b/contracts/wxdai.json @@ -0,0 +1,279 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "guy", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "src", + "type": "address" + }, + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "wad", + "type": "uint256" + } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "dst", + "type": "address" + }, + { + "name": "wad", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "deposit", + "outputs": [], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + }, + { + "name": "", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "guy", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "dst", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "src", + "type": "address" + }, + { + "indexed": false, + "name": "wad", + "type": "uint256" + } + ], + "name": "Withdrawal", + "type": "event" + } +] diff --git a/run_service.sh b/run_service.sh index df07b562..e38f5120 100755 --- a/run_service.sh +++ b/run_service.sh @@ -45,23 +45,31 @@ ensure_minimum_balance() { local address="$1" local minimum_balance="$2" local address_description="$3" + local wxdai="${4:-false}" + + wxdai_balance=0 + if [ "$wxdai" = "true" ] + then + wxdai_balance=$(poetry run python "../scripts/wxdai_balance.py" "$safe" "$rpc") + fi balance_hex=$(get_balance "$address") balance=$(hex_to_decimal "$balance_hex") + balance=$((wxdai_balance+balance)) - echo "Checking balance of $address_description (minimum required $(wei_to_dai $minimum_balance) DAI):" + echo "Checking balance of $address_description (minimum required $(wei_to_dai "$minimum_balance") DAI):" echo " - Address: $address" - echo " - Balance: $(wei_to_dai $balance) DAI" + echo " - Balance: $(wei_to_dai "$balance") DAI" if [ "$($PYTHON_CMD -c "print($balance < $minimum_balance)")" == "True" ]; then echo "" - echo " Please, fund address $address with at least $(wei_to_dai $minimum_balance) DAI." + echo " Please, fund address $address with at least $(wei_to_dai "$minimum_balance") DAI." local spin='-\|/' local i=0 local cycle_count=0 while [ "$($PYTHON_CMD -c "print($balance < $minimum_balance)")" == "True" ]; do - printf "\r Waiting... ${spin:$i:1} " + printf "\r Waiting... %s" "${spin:$i:1} " i=$(((i + 1) % 4)) sleep .1 @@ -70,13 +78,14 @@ ensure_minimum_balance() { if [ "$cycle_count" -eq 100 ]; then balance_hex=$(get_balance "$address") balance=$(hex_to_decimal "$balance_hex") + balance=$((wxdai_balance+balance)) cycle_count=0 fi done printf "\r Waiting... \n" echo "" - echo " - Updated balance: $(wei_to_dai $balance) DAI" + echo " - Updated balance: $(wei_to_dai "$balance") DAI" fi echo " OK." @@ -175,6 +184,92 @@ get_on_chain_service_state() { echo "$state" } +store=".trader_runner" +rpc_path="$store/rpc.txt" +operator_keys_file="$store/operator_keys.json" +keys_json="keys.json" +keys_json_path="$store/$keys_json" +agent_address_path="$store/agent_address.txt" +service_id_path="$store/service_id.txt" +service_safe_address_path="$store/service_safe_address.txt" +store_readme_path="$store/README.txt" + +# Function to create the .trader_runner storage +create_storage() { + echo "This is the first run of the script. The script will generate new operator and agent instance addresses." + echo "" + # Generate the operator's key + address_start_position=17 + pkey_start_position=21 + poetry run autonomy generate-key -n1 ethereum + mv "$keys_json" "../$keys_json_path" + operator_address=$(sed -n 3p "../$keys_json_path") + operator_address=$(echo "$operator_address" | \ + awk '{ print substr( $0, '$address_start_position', length($0) - '$address_start_position' - 1 ) }') + echo "Your operator's autogenerated public address: $operator_address" + echo "(The same address will be used as the service owner.)" + operator_pkey=$(sed -n 4p "../$keys_json_path") + operator_pkey_file="operator_pkey.txt" + echo -n "$operator_pkey" | awk '{ printf substr( $0, '$pkey_start_position', length($0) - '$pkey_start_position' ) }' > $operator_pkey_file + mv "../$keys_json_path" "../$operator_keys_file" + + # Generate the agent's key + poetry run autonomy generate-key -n1 ethereum + mv "$keys_json" "../$keys_json_path" + agent_address=$(sed -n 3p "../$keys_json_path") + agent_address=$(echo "$agent_address" | \ + awk '{ print substr( $0, '$address_start_position', length($0) - '$address_start_position' - 1 ) }') + private_key=$(sed -n 4p "../$keys_json_path") + private_key=$(echo "$private_key" | \ + awk '{ print substr( $0, '$pkey_start_position', length($0) - '$pkey_start_position' ) }') + echo "Your agent instance's autogenerated public address: $agent_address" + echo -n "$agent_address" > "../$agent_address_path" + echo "" + + # generate private key files in the format required by the CLI tool + agent_pkey_file="agent_pkey.txt" + agent_pkey=$(get_private_key "../$keys_json_path") + agent_pkey="${agent_pkey#0x}" + echo -n "$agent_pkey" >"$agent_pkey_file" + + operator_pkey_file="operator_pkey.txt" + operator_pkey=$(get_private_key "../$operator_keys_file") + operator_pkey="${operator_pkey#0x}" + echo -n "$operator_pkey" >"$operator_pkey_file" +} + +# Function to read and load the .trader_runner storage information if it exists. +# Also sets `first_run` flag to identify whether we are running the script for the first time. +try_read_storage() { + if [ -d $store ]; then + first_run=false + paths="$rpc_path $operator_keys_file $keys_json_path $agent_address_path $service_id_path" + + for file in $paths; do + if ! [ -f "$file" ]; then + if [ "$file" == $service_id_path ]; then + first_run=true + elif [ "$file" != $service_safe_address_path ]; then + echo "The runner's store is corrupted!" + echo "Please manually investigate the $store folder" + echo "Make sure that you do not lose your keys or any other important information!" + exit 1 + fi + fi + done + + rpc=$(cat $rpc_path) + agent_address=$(cat $agent_address_path) + service_id=$(cat $service_id_path) + else + first_run=true + mkdir "$store" + + echo -e 'IMPORTANT:\n\n' \ + ' This folder contains crucial configuration information and autogenerated keys for your Trader agent.\n' \ + ' Please back up this folder and be cautious if you are modifying or sharing these files to avoid potential asset loss.' > "$store_readme_path" + fi +} # ------------------ # Script starts here @@ -231,40 +326,7 @@ docker rm -f abci0 node0 trader_abci_0 trader_tm_0 &> /dev/null || exit 1 } -store=".trader_runner" -rpc_path="$store/rpc.txt" -operator_keys_file="$store/operator_keys.json" -keys_json="keys.json" -keys_json_path="$store/$keys_json" -agent_address_path="$store/agent_address.txt" -service_id_path="$store/service_id.txt" -service_safe_address_path="$store/service_safe_address.txt" -store_readme_path="$store/README.txt" - -if [ -d $store ]; then - first_run=false - paths="$rpc_path $operator_keys_file $keys_json_path $agent_address_path $service_id_path" - - for file in $paths; do - if ! [ -f "$file" ]; then - echo "The runner's store is corrupted!" - echo "Please manually investigate the $store folder" - echo "Make sure that you do not lose your keys or any other important information!" - exit 1 - fi - done - - rpc=$(cat $rpc_path) - agent_address=$(cat $agent_address_path) - service_id=$(cat $service_id_path) -else - first_run=true - mkdir "$store" - - echo -e 'IMPORTANT:\n\n' \ - ' This folder contains crucial configuration information and autogenerated keys for your Trader agent.\n' \ - ' Please back up this folder and be cautious if you are modifying or sharing these files to avoid potential asset loss.' > "$store_readme_path" -fi +try_read_storage # Prompt for RPC [[ -z "${rpc}" ]] && read -rsp "Enter a Gnosis RPC that supports eth_newFilter [hidden input]: " rpc && echo || rpc="${rpc}" @@ -310,6 +372,11 @@ else exit 1 fi +if [ "$first_run" = "true" ] +then + create_storage +fi + echo "" echo "-----------------------------------------" echo "Checking Autonolas Protocol service state" @@ -331,36 +398,6 @@ export AGENT_ID=12 if [ "$first_run" = "true" ] then - echo "This is the first run of the script. The script will generate new operator and agent instance addresses." - echo "" - # Generate the operator's key - address_start_position=17 - pkey_start_position=21 - poetry run autonomy generate-key -n1 ethereum - mv "$keys_json" "../$keys_json_path" - operator_address=$(sed -n 3p "../$keys_json_path") - operator_address=$(echo "$operator_address" | \ - awk '{ print substr( $0, '$address_start_position', length($0) - '$address_start_position' - 1 ) }') - echo "Your operator's autogenerated public address: $operator_address" - echo "(The same address will be used as the service owner.)" - operator_pkey=$(sed -n 4p "../$keys_json_path") - operator_pkey_file="operator_pkey.txt" - echo -n "$operator_pkey" | awk '{ printf substr( $0, '$pkey_start_position', length($0) - '$pkey_start_position' ) }' > $operator_pkey_file - mv "../$keys_json_path" "../$operator_keys_file" - - # Generate the agent's key - poetry run autonomy generate-key -n1 ethereum - mv "$keys_json" "../$keys_json_path" - agent_address=$(sed -n 3p "../$keys_json_path") - agent_address=$(echo "$agent_address" | \ - awk '{ print substr( $0, '$address_start_position', length($0) - '$address_start_position' - 1 ) }') - private_key=$(sed -n 4p "../$keys_json_path") - private_key=$(echo "$private_key" | \ - awk '{ print substr( $0, '$pkey_start_position', length($0) - '$pkey_start_position' ) }') - echo "Your agent instance's autogenerated public address: $agent_address" - echo -n "$agent_address" > "../$agent_address_path" - echo "" - # Check balances suggested_amount=50000000000000000 ensure_minimum_balance "$operator_address" $suggested_amount "operator's address" @@ -393,17 +430,6 @@ then echo -n "$service_id" > "../$service_id_path" fi -# generate private key files in the format required by the CLI tool -agent_pkey_file="agent_pkey.txt" -agent_pkey=$(get_private_key "../$keys_json_path") -agent_pkey="${agent_pkey#0x}" -echo -n "$agent_pkey" >"$agent_pkey_file" - -operator_pkey_file="operator_pkey.txt" -operator_pkey=$(get_private_key "../$operator_keys_file") -operator_pkey="${operator_pkey#0x}" -echo -n "$operator_pkey" >"$operator_pkey_file" - # Update the on-chain service if outdated packages="packages/packages.json" local_service_hash="$(grep 'service' $packages | awk -F: '{print $2}' | tr -d '", ' | head -n 1)" @@ -418,7 +444,7 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then echo "" echo "This is most likely caused due to an update of the trader service code." echo "The script will proceed now to update the on-chain service." - echo "The operator and agent addressess need to have enough funds so that the process is not interrupted." + echo "The operator and agent addresses need to have enough funds so that the process is not interrupted." echo "" # Check balances @@ -439,7 +465,7 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then # TODO this condition should be increased to be service_state=DEPLOYED && current_safe_owner=agent_address. # Otherwise the script will not recover the on-chain state in the (rare) case where this transaction succeeds but terminating transaction fails. - if [ $(get_on_chain_service_state $service_id) == "DEPLOYED" ]; then + if [ "$(get_on_chain_service_state "$service_id")" == "DEPLOYED" ]; then # transfer the ownership of the Safe from the agent to the service owner # (in a live service, this should be done by sending a 0 DAI transfer to its Safe) service_safe_address=$(<"../$service_safe_address_path") @@ -455,7 +481,7 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then fi # terminate current service - if [ $(get_on_chain_service_state $service_id) == "DEPLOYED" ]; then + if [ "$(get_on_chain_service_state "$service_id")" == "DEPLOYED" ]; then echo "[Service owner] Terminating on-chain service $service_id..." output=$( poetry run autonomy service \ @@ -473,7 +499,7 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then fi # unbond current service - if [ $(get_on_chain_service_state $service_id) == "TERMINATED_BONDED" ]; then + if [ "$(get_on_chain_service_state "$service_id")" == "TERMINATED_BONDED" ]; then echo "[Operator] Unbonding on-chain service $service_id..." output=$( poetry run autonomy service \ @@ -491,7 +517,7 @@ if [ "$local_service_hash" != "$remote_service_hash" ]; then fi # update service - if [ $(get_on_chain_service_state $service_id) == "PRE_REGISTRATION" ]; then + if [ "$(get_on_chain_service_state "$service_id")" == "PRE_REGISTRATION" ]; then echo "[Service owner] Updating on-chain service $service_id..." cost_of_bonding=10000000000000000 nft="bafybeig64atqaladigoc3ds4arltdu63wkdrk3gesjfvnfdmz35amv7faq" @@ -524,13 +550,13 @@ fi echo "" echo "Ensuring on-chain service $service_id is in DEPLOYED state..." -if [ $(get_on_chain_service_state $service_id) != "DEPLOYED" ]; then +if [ "$(get_on_chain_service_state "$service_id")" != "DEPLOYED" ]; then suggested_amount=25000000000000000 ensure_minimum_balance "$operator_address" $suggested_amount "operator's address" fi # activate service -if [ $(get_on_chain_service_state $service_id) == "PRE_REGISTRATION" ]; then +if [ "$(get_on_chain_service_state "$service_id")" == "PRE_REGISTRATION" ]; then echo "[Service owner] Activating registration for on-chain service $service_id..." output=$(poetry run autonomy service --use-custom-chain activate --key "$operator_pkey_file" "$service_id") if [[ $? -ne 0 ]]; then @@ -543,7 +569,7 @@ if [ $(get_on_chain_service_state $service_id) == "PRE_REGISTRATION" ]; then fi # register agent instance -if [ $(get_on_chain_service_state $service_id) == "ACTIVE_REGISTRATION" ]; then +if [ "$(get_on_chain_service_state "$service_id")" == "ACTIVE_REGISTRATION" ]; then echo "[Operator] Registering agent instance for on-chain service $service_id..." output=$(poetry run autonomy service --use-custom-chain register --key "$operator_pkey_file" "$service_id" -a $AGENT_ID -i "$agent_address") if [[ $? -ne 0 ]]; then @@ -556,7 +582,7 @@ if [ $(get_on_chain_service_state $service_id) == "ACTIVE_REGISTRATION" ]; then fi # deploy on-chain service -service_state=$(get_on_chain_service_state $service_id) +service_state="$(get_on_chain_service_state "$service_id")" if [ "$service_state" == "FINISHED_REGISTRATION" ] && [ "$first_run" = "true" ]; then echo "[Service owner] Deploying on-chain service $service_id..." output=$(poetry run autonomy service --use-custom-chain deploy "$service_id" --key "$operator_pkey_file") @@ -584,7 +610,7 @@ rm -f $agent_pkey_file rm -f $operator_pkey_file # check state -service_state=$(get_on_chain_service_state $service_id) +service_state="$(get_on_chain_service_state "$service_id")" if [ "$service_state" != "DEPLOYED" ]; then echo "Something went wrong while deploying on-chain service. The service's state is $service_state." echo "Please check the output of the script and the on-chain registry for more information." @@ -640,10 +666,10 @@ build_dir="abci_build" directory="$service_dir/$build_dir" suggested_amount=50000000000000000 -ensure_minimum_balance $agent_address $suggested_amount "agent instance's address" +ensure_minimum_balance "$agent_address" $suggested_amount "agent instance's address" suggested_amount=500000000000000000 -ensure_minimum_balance $SAFE_CONTRACT_ADDRESS $suggested_amount "service Safe's address" +ensure_minimum_balance "$SAFE_CONTRACT_ADDRESS" $suggested_amount "service Safe's address" "true" if [ -d $directory ] then diff --git a/scripts/wxdai_balance.py b/scripts/wxdai_balance.py new file mode 100644 index 00000000..16239d34 --- /dev/null +++ b/scripts/wxdai_balance.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ------------------------------------------------------------------------------ +# +# Copyright 2022-2023 Valory AG +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ------------------------------------------------------------------------------ + +"""This script prints the wxDAI balance of an address in WEI.""" + +import sys + +from web3 import Web3, HTTPProvider + +WXDAI_CONTRACT_ADDRESS = "0xe91D153E0b41518A2Ce8Dd3D7944Fa863463a97d" +WXDAI_ABI_PATH = "../contracts/wxdai.json" + + +def get_balance() -> int: + """Get the wxDAI balance of an address in WEI.""" + w3 = Web3(HTTPProvider(rpc)) + contract_instance = w3.eth.contract(address=WXDAI_CONTRACT_ADDRESS, abi=abi) + return contract_instance.functions.balanceOf(address).call() + + +def read_abi() -> str: + """Read and return the wxDAI contract's ABI.""" + with open(WXDAI_ABI_PATH) as f: + return f.read() + + +if __name__ == "__main__": + if len(sys.argv) != 3: + raise ValueError("Expected the address and the rpc as positional arguments.") + else: + address = sys.argv[1] + rpc = sys.argv[2] + abi = read_abi() + print(get_balance())