Skip to content
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

feat: allow to define storage to debug tenderly actions #54

Merged
merged 1 commit into from
Aug 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
12 changes: 11 additions & 1 deletion actions/checkForAndPlaceOrder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ const _checkForAndPlaceOrder: ActionFn = async (
context
);

console.log(
`[checkForAndPlaceOrder] Check conditional order result: ${
error ? "❌" : "✅"
}`
);

hasErrors ||= error;

if (deleteConditionalOrder) {
Expand Down Expand Up @@ -189,7 +195,11 @@ async function _processConditionalOrder(
);
}
} catch (e: any) {
console.error(`Unexpected error while processing order:`, e);
error = true;
console.error(
`[_processConditionalOrder] Unexpected error while processing order:`,
e
);
}

return { deleteConditionalOrder: false, error };
Expand Down
138 changes: 96 additions & 42 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] Run "addContract" action 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:",
`[run_local] Error running "addContract" action for TX:`,
e
);
return false;
});
console.log(
`[run_local] Result of "addContract" action for TX ${hash}: ${_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",
`[run_local] Error running "checkForAndPlaceOrder" action`,
e
);
return false;
});
console.log(
`[run_local] Result of "checkForAndPlaceOrder" action for block ${blockNumber}: ${_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())();