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

Deploy to tenderly #35

Merged
merged 18 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 16 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
25 changes: 19 additions & 6 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
ETHERSCAN_API_KEY=
PRIVATE_KEY=
SETTLEMENT=0x9008D19f58AAbD9eD0D60971565AA8510560ab41
# CLI
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
ETH_RPC_URL=http://erigon.dappnode:8545
SAFE=
TWAP=
COMPOSABLE_COW=
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
PRIVATE_KEY=

# Forge deployment scripts:
SETTLEMENT=0x9008D19f58AAbD9eD0D60971565AA8510560ab41 # GPv2Settlement contract to use for constructor initialisation.
# ETHERSCAN_API_KEY=
# Forge single order submission scripts:
# SAFE=
# TWAP=
# COMPOSABLE_COW=

# Tenderly Local Tests
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
NETWORK=5 # Network to test. 1: Mainnet, 5: Goerli, 100: xDai
# NODE_URL_1=
# NODE_URL_5=
# NODE_URL_100=
# NODE_USER_100=
# NODE_PASSWORD_100=

21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,3 +301,24 @@ For local integration testing, including the use of [Tenderly Actions](#Tenderly
source .env
SAFE="address here" forge script script/submit_SingleOrder.s.sol:SubmitSingleOrder --rpc-url http://127.0.0.1:8545 --broadcast
```

#### Local test for Tenderly Web3 Actions

Make sure you setup the environment (so you have your own `.env` file).

Decide in which network you want to run the actions and provide the additional parameters for that network. For example:

```ini
NETWORK=100
NODE_URL_100=https://your-rpc-endpoint
NODE_USER_100=optionally-provide-user-if-auth-is-required
NODE_PASSWORD_100=optionally-provide-password-if-auth-is-required
```

```bash
# Build Actions
yarn build:actions

# Run actions locally, so it starts to checking for new blocks, and executing the actions to create Composable Cow orders)
yarn start:actions
```
19 changes: 19 additions & 0 deletions actions/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions actions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@
"build": "tsc"
},
"devDependencies": {
"@tenderly/actions-test": "^0.0.13",
"@typechain/ethers-v5": "^10.2.0",
"@types/node": "^18.16.3",
"@tenderly/actions-test": "^0.0.13",
"ts-node": "^10.9.1",
"typechain": "^8.1.1"
"typechain": "^8.1.1",
"dotenv": "^16.3.1"
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
},
"dependencies": {
"@cowprotocol/contracts": "^1.4.0",
"@tenderly/actions": "^0.0.13",
"axios": "^1.4.0",
"axios": "^1.4.0",
"ethers": "^5.7.2",
"node-fetch": "2",
"typescript": "^5.0.4"
Expand Down
16 changes: 4 additions & 12 deletions actions/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ export const addContract: ActionFn = async (context: Context, event: Event) => {

// Load the registry
const registry = await Registry.load(context, transactionEvent.network);
console.log(
`Current registry: ${JSON.stringify(
Array.from(registry.ownerOrders.entries())
)}`
);

// Process the logs
transactionEvent.logs.forEach((log) => {
Expand Down Expand Up @@ -79,11 +74,6 @@ export const addContract: ActionFn = async (context: Context, event: Event) => {
}
}
});
console.log(
`Updated registry: ${JSON.stringify(
Array.from(registry.ownerOrders.entries())
)}`
);
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
await registry.write();
};

Expand All @@ -105,7 +95,7 @@ export const add = async (
if (registry.ownerOrders.has(owner)) {
const conditionalOrders = registry.ownerOrders.get(owner);
console.log(
`adding conditional order ${params} to already existing contract ${owner}`
`[register:add] Adding conditional order ${params} to already existing contract ${owner}`
);
let exists: boolean = false;
// Iterate over the conditionalOrders to make sure that the params are not already in the registry
Expand All @@ -127,7 +117,9 @@ export const add = async (
});
}
} else {
console.log(`adding conditional order ${params} to new contract ${owner}`);
console.log(
`[register:add] Adding conditional order ${params} to new contract ${owner}`
);
registry.ownerOrders.set(
owner,
new Set([{ params, proof, orders: new Map(), composableCow }])
Expand Down
27 changes: 21 additions & 6 deletions actions/test/run_local.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,34 @@ import {
import { checkForAndPlaceOrder } from "../watch";
import { addContract } from "../register";
import { ethers } from "ethers";
import assert = require("assert");
import { getProvider } from "../utils";

require("dotenv").config();
mfw78 marked this conversation as resolved.
Show resolved Hide resolved

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

const node_url = process.env["ETH_RPC_URL"];
if (!node_url) {
throw "Please specify your node url via the ETH_RPC_URL env variable";
// 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}`,
];
for (const name of envNames) {
const envValue = process.env[name];
if (envValue) {
await testRuntime.context.secrets.put(name, envValue);
}
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
}

// The web3 actions fetches the node url and computes the API based on the current chain id
const provider = new ethers.providers.JsonRpcProvider(node_url);
// Get provider
const provider = await getProvider(testRuntime.context, network);
const { chainId } = await provider.getNetwork();
await testRuntime.context.secrets.put(`NODE_URL_${chainId}`, node_url);

provider.on("block", async (blockNumber) => {
// Block watcher for creating new orders
Expand Down
72 changes: 72 additions & 0 deletions actions/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { Context } from "@tenderly/actions";
import assert = require("assert");

import { ethers } from "ethers";
import { ConnectionInfo, Logger } from "ethers/lib/utils";
import { OrderStatus } from "./register";

async function getSecret(key: string, context: Context): Promise<string> {
const value = await context.secrets.get(key);
assert(value, `${key} secret is required`);

return value;
}

export async function getProvider(
context: Context,
network: string
): Promise<ethers.providers.Provider> {
Logger.setLogLevel(Logger.levels.DEBUG);

const url = await getSecret(`NODE_URL_${network}`, context);
const user = await getSecret(`NODE_USER_${network}`, context).catch(
() => undefined
);
const password = await getSecret(`NODE_PASSWORD_${network}`, context).catch(
() => undefined
);
const providerConfig: ConnectionInfo =
user && password
? {
url,
// TODO: This is a hack to make it work for HTTP endpoints (while we don't have a HTTPS one for Gnosis Chain), however I will delete once we have it
headers: {
Authorization: getAuthHeader({ user, password }),
},
// user: await getSecret(`NODE_USER_${network}`, context),
// password: await getSecret(`NODE_PASSWORD_${network}`, context),
}
: { url };

return new ethers.providers.JsonRpcProvider(providerConfig);
}

function getAuthHeader({ user, password }: { user: string; password: string }) {
return "Basic " + Buffer.from(`${user}:${password}`).toString("base64");
}

export function apiUrl(network: string): string {
switch (network) {
case "1":
return "https://api.cow.fi/mainnet";
case "5":
return "https://api.cow.fi/goerli";
case "100":
return "https://api.cow.fi/xdai";
case "31337":
return "http://localhost:3000";
default:
throw "Unsupported network";
}
}

export function formatStatus(status: OrderStatus) {
mfw78 marked this conversation as resolved.
Show resolved Hide resolved
switch (status) {
case OrderStatus.FILLED:
return "FILLED";
case OrderStatus.SUBMITTED:
return "SUBMITTED";
default:
return `UNKNOWN (${status})`;
}
}
Loading