Skip to content

Commit

Permalink
fix & Automation of script
Browse files Browse the repository at this point in the history
  • Loading branch information
marczeller committed Aug 12, 2024
1 parent 905e7a0 commit caf6b80
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 72 deletions.
25 changes: 7 additions & 18 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,13 @@ fetch-reserves: $(LOG_DIR)
@echo "Fetching reserves list for all networks..."
@forge script ${FETCH_RESERVES_SCRIPT} -vvvv

mint-to-treasury: $(LOG_DIR)
@echo "Minting to treasury on ${NETWORK}..."
@timeout $(TIMEOUT) bash -c "\
RESERVES=\$$(cat $(LOG_DIR)/reserves.json); \
NETWORK=${NETWORK} forge script ${MINT_TO_TREASURY_SCRIPT} \
--sig \"run(string)\" \"\$$RESERVES\" \
--rpc-url ${RPC_${NETWORK}} \
${PRIVATE_KEY_ARG} \
${EXTRA_ARGS}" \
|| echo "Transaction for ${NETWORK} timed out after $(TIMEOUT) seconds. Skipping to next network."

run-all: fetch-reserves
@echo "Running mint-to-treasury for all networks with reserves..."
@NETWORKS=$$(jq -r 'keys[]' $(LOG_DIR)/reserves.json); \
for network in $$NETWORKS; do \
echo "Processing $$network"; \
$(MAKE) mint-to-treasury NETWORK=$$network || true; \
done
mint:
@if [ -z "$(NETWORK)" ]; then \
echo "Error: NETWORK is not set. Use 'make mint NETWORK=<network_name>' or set NETWORK in .env file."; \
exit 1; \
fi
@echo "Minting for network: $(NETWORK)"
TARGET_NETWORK=$(NETWORK) forge script script/MintToTreasury.s.sol:MintToTreasuryScript --broadcast

clean:
@rm -rf $(LOG_DIR) broadcast cache out
66 changes: 61 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ACI's Dolce Vita Collector

This project automates the process of minting to treasury for various Aave Pools across multiple networks.
This project automates the process of minting to treasury for various Aave Pools across multiple networks. It includes a scheduled script that runs daily and weekly, with Telegram notifications for monitoring.

## Setup

Expand All @@ -26,6 +26,11 @@ This project automates the process of minting to treasury for various Aave Pools
forge build
```

5. Set up the automated script and Telegram notifications:
- Ensure the `dolce_vita_collector_with_notifications.sh` script is in place and executable.
- Set up systemd services and timers for daily and weekly runs (see "Automated Runs" section).
- Configure the Telegram bot token and chat ID in the script.

## Usage

- To fetch reserves for all networks:
Expand All @@ -38,10 +43,61 @@ This project automates the process of minting to treasury for various Aave Pools
make mint-to-treasury NETWORK=MAINNET POOL=MAIN
```

- To run the entire process (fetch reserves and mint for all networks):
```
make run-all
```
- The automated script runs daily (excluding MAINNET) and weekly (including MAINNET) at 8:00 AM UTC.

## Automated Runs

To set up automated runs, you need to create systemd service and timer files. Replace `/path/to/` with the actual path to your cloned repository.

1. Create service files:
```
sudo nano /etc/systemd/system/dolce-vita-daily.service
sudo nano /etc/systemd/system/dolce-vita-weekly.service
```

2. Create timer files:
```
sudo nano /etc/systemd/system/dolce-vita-daily.timer
sudo nano /etc/systemd/system/dolce-vita-weekly.timer
```

3. Enable and start the timers:
```
sudo systemctl daemon-reload
sudo systemctl enable dolce-vita-daily.timer dolce-vita-weekly.timer
sudo systemctl start dolce-vita-daily.timer dolce-vita-weekly.timer
```

Refer to the provided service and timer file templates in the `systemd` directory and adjust paths as necessary.

## Monitoring

The script sends Telegram notifications for:
- Start of each run
- Successful completion of each run
- Any errors encountered during the run

To manually trigger a run:
```
/path/to/dolce_vita_collector/dolce_vita_collector_with_notifications.sh
```

Add `--include-mainnet` for a run that includes MAINNET.

## Logs

Logs are stored in `/var/log/dolce_vita_collector/`:
- `daily.log`: For daily runs
- `weekly.log`: For weekly runs (including MAINNET)

Ensure the log directory exists and is writable by the user running the script.

## Customization

When setting up this project, make sure to:
1. Update all paths in the scripts and service files to match your system's directory structure.
2. Configure your own Telegram bot token and chat ID in the notification script.
3. Adjust the systemd service files to use the correct user and group for your system.

## License

Expand Down
95 changes: 95 additions & 0 deletions dolce_vita_collector.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/bin/bash

# Get the directory where the script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"

# Log file
LOG_FILE="${SCRIPT_DIR}/logs/dolce_vita_collector_log.txt"

# Ensure the logs directory exists
mkdir -p "${SCRIPT_DIR}/logs"

# Function to log messages
log_message() {
echo "$(date +"%Y-%m-%d %H:%M:%S"): $1" | tee -a "$LOG_FILE"
}

# Function to execute and log a command with timeout
execute_command_with_timeout() {
local COMMAND="$1"
local TIMEOUT=180 # 180 seconds = 3 minutes

log_message "Executing with ${TIMEOUT}s timeout: $COMMAND"

timeout $TIMEOUT $COMMAND
local EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
log_message "$COMMAND successful"
return 0
elif [ $EXIT_CODE -eq 124 ]; then
log_message "$COMMAND timed out after ${TIMEOUT} seconds"
return 1
else
log_message "$COMMAND failed with exit code $EXIT_CODE"
return 1
fi
}

# Check if MAINNET should be included
INCLUDE_MAINNET=0
if [ "$1" == "--include-mainnet" ]; then
INCLUDE_MAINNET=1
fi

# Array of networks
NETWORKS=(
"AVALANCHE"
"OPTIMISM"
"POLYGON"
"ARBITRUM"
"METIS"
"BASE"
"GNOSIS"
"BNB"
"SCROLL"
)

# Add MAINNET if flag is set
if [ $INCLUDE_MAINNET -eq 1 ]; then
NETWORKS+=("MAINNET")
fi

# Main execution
log_message "Script execution started"

# Execute make clean
execute_command_with_timeout "make clean"

# Execute make fetch-reserves and wait for it to complete
if execute_command_with_timeout "make fetch-reserves"; then
log_message "make fetch-reserves completed successfully. Proceeding with minting."

log_message "Starting minting process for all networks"

for NETWORK in "${NETWORKS[@]}"; do
log_message "Processing $NETWORK"

COMMAND="make mint NETWORK=$NETWORK"
if execute_command_with_timeout "$COMMAND"; then
log_message "Minting successful for $NETWORK"
else
log_message "Minting failed or timed out for $NETWORK. Moving to next network."
fi

echo "----------------------------------------"
done

log_message "Minting process completed for all networks"
else
log_message "make fetch-reserves failed or timed out. Aborting minting process."
fi

log_message "Script execution completed"

exit 0
11 changes: 9 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./logs"}]
solc = "0.8.21" # Specify the Solidity version
fs_permissions = [
{ access = "read-write", path = "./logs"},
{ access = "read", path = "./.env" },
{ access = "read-write", path = "./logs/reserves.json" }
]
ffi = true
verbosity = 3

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
12 changes: 5 additions & 7 deletions script/FetchReserves.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ contract FetchReservesScript is Script {
}

function getNetworkConfigs() internal pure returns (NetworkConfig[] memory) {
NetworkConfig[] memory configs = new NetworkConfig[](11);
NetworkConfig[] memory configs = new NetworkConfig[](9);

string[] memory mainnetPools = new string[](2);
mainnetPools[0] = "MAIN";
Expand All @@ -95,12 +95,10 @@ contract FetchReservesScript is Script {
configs[2] = NetworkConfig("OPTIMISM", singlePool);
configs[3] = NetworkConfig("POLYGON", singlePool);
configs[4] = NetworkConfig("ARBITRUM", singlePool);
configs[5] = NetworkConfig("FANTOM", singlePool);
configs[6] = NetworkConfig("HARMONY", singlePool);
configs[7] = NetworkConfig("METIS", singlePool);
configs[8] = NetworkConfig("BASE", singlePool);
configs[9] = NetworkConfig("GNOSIS", singlePool);
configs[10] = NetworkConfig("BNB", singlePool);
configs[5] = NetworkConfig("METIS", singlePool);
configs[6] = NetworkConfig("BASE", singlePool);
configs[7] = NetworkConfig("GNOSIS", singlePool);
configs[8] = NetworkConfig("BNB", singlePool);

return configs;
}
Expand Down
107 changes: 67 additions & 40 deletions script/MintToTreasury.s.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
pragma solidity ^0.8.13;

import "forge-std/Script.sol";
import "forge-std/console.sol";
Expand All @@ -9,54 +9,81 @@ interface IPool {
}

contract MintToTreasuryScript is Script {
function setUp() public {}
mapping(string => address) pools;
mapping(string => string) rpcUrls;
string[] networkNames = [
"MAINNET", "AVALANCHE", "OPTIMISM", "POLYGON",
"ARBITRUM", "METIS", "BASE", "GNOSIS", "BNB", "SCROLL"
];
string constant RESERVES_PATH = "./logs/reserves.json";

function run(string memory reservesJson) public {
string memory network = vm.envString("NETWORK");
string memory pool = vm.envString("POOL");
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address poolAddress = vm.envAddress(string(abi.encodePacked(network, "_", pool, "_POOL")));

string memory logContent = string(abi.encodePacked(
"Network: ", network, "\n",
"Pool: ", pool, "\n",
"Pool address: ", vm.toString(poolAddress), "\n"
));

// Check if the network and pool exist in the JSON
bytes memory poolData = vm.parseJson(reservesJson, string(abi.encodePacked(".", network, ".", pool)));
if (poolData.length == 0) {
logContent = string(abi.encodePacked(logContent, "No reserves found for this network and pool. Skipping mintToTreasury call.\n"));
} else {
// Parse reserves from JSON string
address[] memory assets = abi.decode(poolData, (address[]));
function setUp() public {
for (uint i = 0; i < networkNames.length; i++) {
string memory networkName = networkNames[i];
string memory poolEnvVar = string(abi.encodePacked(networkName, "_MAIN_POOL"));
string memory rpcEnvVar = string(abi.encodePacked("RPC_", networkName));

logContent = string(abi.encodePacked(logContent, "Number of assets: ", vm.toString(assets.length), "\n\n"));
pools[networkName] = vm.envAddress(poolEnvVar);
rpcUrls[networkName] = vm.envString(rpcEnvVar);
}
// Special case for MAINNET_LIDO_POOL
pools["MAINNET_LIDO"] = vm.envAddress("MAINNET_LIDO_POOL");
}

vm.startBroadcast(deployerPrivateKey);
function run() public {
string memory targetNetwork = vm.envOr("TARGET_NETWORK", string(""));
if (bytes(targetNetwork).length > 0) {
runForNetwork(targetNetwork);
} else {
runForAllNetworks();
}
}

IPool poolContract = IPool(poolAddress);
function runForNetwork(string memory networkName) internal {
require(pools[networkName] != address(0), "Invalid network name");

uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.createSelectFork(rpcUrls[networkName]);
vm.startBroadcast(deployerPrivateKey);

mintToTreasuryForPool(networkName, "MAIN", pools[networkName]);

if (keccak256(abi.encodePacked(networkName)) == keccak256(abi.encodePacked("MAINNET"))) {
mintToTreasuryForPool("MAINNET", "LIDO", pools["MAINNET_LIDO"]);
}

vm.stopBroadcast();
}

function runForAllNetworks() internal {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

for (uint i = 0; i < networkNames.length; i++) {
string memory networkName = networkNames[i];
vm.createSelectFork(rpcUrls[networkName]);
vm.startBroadcast(deployerPrivateKey);

// Call mintToTreasury with all assets
try poolContract.mintToTreasury(assets) {
logContent = string(abi.encodePacked(logContent, "Successfully minted to treasury\n"));
for (uint i = 0; i < assets.length; i++) {
logContent = string(abi.encodePacked(logContent, "Minted asset: ", vm.toString(assets[i]), "\n"));
}
} catch Error(string memory reason) {
logContent = string(abi.encodePacked(logContent, "Failed to mint to treasury. Reason: ", reason, "\n"));
} catch (bytes memory lowLevelData) {
logContent = string(abi.encodePacked(logContent, "Failed to mint to treasury. Low-level error: ", vm.toString(lowLevelData), "\n"));
mintToTreasuryForPool(networkName, "MAIN", pools[networkName]);

if (keccak256(abi.encodePacked(networkName)) == keccak256(abi.encodePacked("MAINNET"))) {
mintToTreasuryForPool("MAINNET", "LIDO", pools["MAINNET_LIDO"]);
}

vm.stopBroadcast();
}
}

// Write log to file
string memory filename = string(abi.encodePacked("./logs/mint_to_treasury_", network, "_", pool, "_", vm.toString(block.timestamp), ".log"));
vm.writeFile(filename, logContent);

console.log("Mint to treasury operation completed for", network, pool);
console.log("Log written to:", filename);
function mintToTreasuryForPool(string memory network, string memory poolType, address poolAddress) internal {
address[] memory reserves = getReservesForPool(network, poolType);
if (reserves.length == 0) {
return; // Skip if reserves array is empty
}
IPool(poolAddress).mintToTreasury(reserves);
}

function getReservesForPool(string memory network, string memory poolType) internal view returns (address[] memory) {
string memory json = vm.readFile(RESERVES_PATH);
bytes memory parseJson = vm.parseJson(json, string(abi.encodePacked(".", network, ".", poolType)));
return abi.decode(parseJson, (address[]));
}
}

0 comments on commit caf6b80

Please sign in to comment.