Skip to content

Commit

Permalink
Allow to define storage to debug tenderly actions
Browse files Browse the repository at this point in the history
  • Loading branch information
anxolin committed Aug 20, 2023
1 parent b3fab14 commit c7e3d4b
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 40 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ NETWORK=5 # Network to test. 1: Mainnet, 5: Goerli, 100: xDai
#BLOCK_NUMBER=9500767
BLOCK_NUMBER=

# Optionally, specify the storage process locally
# - useful for debugging a failed execution in Tenderly
# - you can pass the value of the "storage" field from production to try to reproduce the error locally
STORAGE=

# Slack
SLACK_WEBHOOK_URL=
NOTIFICATIONS_ENABLED=false
Expand Down
134 changes: 94 additions & 40 deletions actions/test/run_local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,55 +8,43 @@ import { addContract } from "../addContract";
import { ethers } from "ethers";
import assert = require("assert");
import { getProvider } from "../utils";
import { getOrdersStorageKey } from "../model";
import { exit } from "process";

require("dotenv").config();

const main = async () => {
const testRuntime = new TestRuntime();

// The web3 actions fetches the node url and computes the API based on the current chain id
const network = process.env.NETWORK;
assert(network, "network is required");

// Add secrets from local env (.env) for current network
const envNames = [
`NODE_URL_${network}`,
`NODE_USER_${network}`,
`NODE_PASSWORD_${network}`,
"SLACK_WEBHOOK_URL",
"NOTIFICATIONS_ENABLED",
"SENTRY_DSN",
];
for (const name of envNames) {
const envValue = process.env[name];
if (envValue) {
await testRuntime.context.secrets.put(name, envValue);
}
}
const testRuntime = await _getRunTime(network);

// Get provider
const provider = await getProvider(testRuntime.context, network);
const { chainId } = await provider.getNetwork();

const onNewBlock = async (blockNumber: number) => {
try {
processBlock(provider, blockNumber, chainId, testRuntime);
} catch (error) {
console.error("[run_local] Error in processBlock", error);
}
};

// Run one of the 2 Execution modes (single block, or watch mode)
if (process.env.BLOCK_NUMBER) {
// Execute once, for a specific block
const blockNumber = Number(process.env.BLOCK_NUMBER);
console.log(`[run_local] Processing specific block ${blockNumber}...`);
await onNewBlock(blockNumber).catch(console.error);
await processBlock(provider, blockNumber, chainId, testRuntime).catch(
() => {
exit(100);
}
);
console.log(`[run_local] Block ${blockNumber} has been processed.`);
} else {
// Watch for new blocks
console.log(`[run_local] Subscribe to new blocks for network ${network}`);
provider.on("block", onNewBlock);
provider.on("block", async (blockNumber: number) => {
try {
await processBlock(provider, blockNumber, chainId, testRuntime);
} catch (error) {
console.error("[run_local] Error in processBlock", error);
}
});
}
};

Expand All @@ -72,41 +60,57 @@ async function processBlock(
const blockWithTransactions = await provider.getBlockWithTransactions(
blockNumber
);
let hasErrors = false;
for (const transaction of blockWithTransactions.transactions) {
const receipt = await provider.getTransactionReceipt(transaction.hash);
if (receipt) {
const {
hash,
from,
value,
nonce,
gasLimit,
maxPriorityFeePerGas,
maxFeePerGas,
} = transaction;

const testTransactionEvent: TestTransactionEvent = {
blockHash: block.hash,
blockNumber: block.number,
from: transaction.from,
hash: transaction.hash,
from,
hash,
network: chainId.toString(),
logs: receipt.logs,
input: "",
value: transaction.value.toString(),
nonce: transaction.nonce.toString(),
gas: transaction.gasLimit.toString(),
value: value.toString(),
nonce: nonce.toString(),
gas: gasLimit.toString(),
gasUsed: receipt.gasUsed.toString(),
cumulativeGasUsed: receipt.cumulativeGasUsed.toString(),
gasPrice: receipt.effectiveGasPrice.toString(),
gasTipCap: transaction.maxPriorityFeePerGas
? transaction.maxPriorityFeePerGas.toString()
: "",
gasFeeCap: transaction.maxFeePerGas
? transaction.maxFeePerGas.toString()
: "",
gasTipCap: maxPriorityFeePerGas ? maxPriorityFeePerGas.toString() : "",
gasFeeCap: maxFeePerGas ? maxFeePerGas.toString() : "",
transactionHash: transaction.hash,
};

// run action
await testRuntime
console.log(`[run_local] AddContract for TX ${hash}`);
const result = await testRuntime
.execute(addContract, testTransactionEvent)
.then(() => true)
.catch((e) => {
hasErrors = true;
console.error(
"[run_local] Error in addContract processing transaction:",
e
);
return false;
});
console.log(
`[run_local] AddContract for TX ${hash}. Result ${_formatResult(
result
)}`
);
}
}

Expand All @@ -118,14 +122,64 @@ async function processBlock(
testBlockEvent.network = chainId.toString();

// run action
await testRuntime
console.log(`[run_local] checkForAndPlaceOrder for block ${blockNumber}`);
const result = await testRuntime
.execute(checkForAndPlaceOrder, testBlockEvent)
.then(() => true)
.catch((e) => {
hasErrors = true;
console.log(
"[run_local] Error in checkForAndPlaceOrder processing block",
e
);
return false;
});
console.log(
`[run_local] checkForAndPlaceOrder for block ${blockNumber}. Result ${_formatResult(
result
)}`
);

if (hasErrors) {
throw new Error("[run_local] Errors found in processing block");
}
}

async function _getRunTime(network: string): Promise<TestRuntime> {
const testRuntime = new TestRuntime();

// Add secrets from local env (.env) for current network
const envNames = [
`NODE_URL_${network}`,
`NODE_USER_${network}`,
`NODE_PASSWORD_${network}`,
"SLACK_WEBHOOK_URL",
"NOTIFICATIONS_ENABLED",
"SENTRY_DSN",
];
for (const name of envNames) {
const envValue = process.env[name];
if (envValue) {
await testRuntime.context.secrets.put(name, envValue);
}
}

// Load storage from env
const storage = process.env.STORAGE;
if (storage) {
const storageFormatted = JSON.stringify(JSON.parse(storage), null, 2);
console.log("[run_local] Loading storage from env", storageFormatted);
await testRuntime.context.storage.putStr(
getOrdersStorageKey(network),
storage
);
}

return testRuntime;
}

function _formatResult(result: boolean) {
return result ? "✅" : "❌";
}

(async () => await main())();

0 comments on commit c7e3d4b

Please sign in to comment.