diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5dc6e1c..736e4b2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: foundry-rs/foundry-toolchain@v1 - uses: oven-sh/setup-bun@v2 - run: bun install - run: bun run lint:ci + - run: bun test - run: bun run test diff --git a/bun.lockb b/bun.lockb index af256e0..e9de063 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/hardhat.config.ts b/hardhat.config.ts index 399aec8..fdd673c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -3,7 +3,9 @@ import "@gelatonetwork/web3-functions-sdk/hardhat-plugin"; import "@nomiclabs/hardhat-ethers"; import "@foundry-rs/hardhat-anvil"; -const PRIVATE_KEY = process.env.PRIVATE_KEY; +const PRIVATE_KEY = + process.env.PRIVATE_KEY ?? + "0x0000000000000000000000000000000000000000000000000000000000000001"; const config = { w3f: { @@ -30,57 +32,57 @@ const config = { launch: true, chainId: 1, forkUrl: "https://rpc.ankr.com/eth", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, ethereum: { chainId: 1, url: "https://rpc.ankr.com/eth", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, avalanche: { url: "https://api.avax.network/ext/bc/C/rpc", chainId: 43114, - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, arbitrum: { chainId: 42161, url: "https://arb1.arbitrum.io/rpc", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, fantom: { chainId: 250, url: "https://rpc2.fantom.network", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, optimism: { chainId: 10, url: "https://mainnet.optimism.io", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, polygon: { chainId: 137, url: "https://rpc-mainnet.maticvigil.com", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, bsc: { chainId: 56, url: "https://bsc.publicnode.com", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, kava: { chainId: 2222, url: "https://kava-evm.publicnode.com", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, base: { chainId: 8453, url: "https://base.meowrpc.com", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, linea: { chainId: 59144, url: "https://rpc.linea.build", - accounts: PRIVATE_KEY ? [PRIVATE_KEY] : [], + accounts: [PRIVATE_KEY], }, }, // biome-ignore lint/complexity/noBannedTypes: Improve auto-completion diff --git a/package.json b/package.json index 81bc4d8..3146600 100644 --- a/package.json +++ b/package.json @@ -64,11 +64,11 @@ }, "devDependencies": { "@biomejs/biome": "^1.8.3", - "@foundry-rs/easy-foundryup": "^0.1.3", "@foundry-rs/hardhat-anvil": "^0.1.7", "@nomiclabs/hardhat-ethers": "^2.2.3", "@tsconfig/recommended": "^1.0.7", "@types/bun": "latest", + "@viem/anvil": "^0.0.10", "concurrently": "^8.2.2", "ethereum-waffle": "^3.4.4", "ethers": "^5.7.2", diff --git a/test/reward-distributor.test.ts b/test/reward-distributor.test.ts new file mode 100644 index 0000000..f86f272 --- /dev/null +++ b/test/reward-distributor.test.ts @@ -0,0 +1,70 @@ +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import path from "node:path"; +import { JsonRpcProvider } from "@ethersproject/providers"; +import { checkAnvil, getAnvilCommand, run } from "@foundry-rs/easy-foundryup"; +import { Web3FunctionResultV2 } from "@gelatonetwork/web3-functions-sdk"; +import { type Anvil, createAnvil } from "@viem/anvil"; +import { runWeb3Function } from "./utils"; + +const w3fName = "reward-distributor"; +const w3fRootDir = path.join("web3-functions"); +const w3fPath = path.join(w3fRootDir, w3fName, "index.ts"); + +describe("Reward Distributor Web3 Function test", () => { + let anvil: Anvil; + beforeAll(async () => { + if (!(await checkAnvil())) { + await run(); + } + anvil = createAnvil({ + // All anvil options are supported & typed. + forkUrl: + "https://rpc.ankr.com/arbitrum/665a6078efb46adbd3eb6b831180fab7d7007c48590f2517e2bce900ba31e8ca", + forkBlockNumber: 234134609n, + anvilBinary: await getAnvilCommand(), + }); + await anvil.start(); + }); + afterAll(async () => { + await anvil.stop(); + }); + test("canExec: true - Multiple calls ", async () => { + const provider = new JsonRpcProvider(`http://${anvil.host}:${anvil.port}`); + const { result } = await runWeb3Function( + w3fPath, + { + gelatoArgs: { + chainId: 42161, + gasPrice: (await provider.getGasPrice()).toString(), + }, + userArgs: { + multiRewardDistributorAddress: + "0xbF5DC3f598AFA173135160CDFce6BFeE45c912eF", + multiRewardStakingAddresses: [ + "0x280c64c4C4869CF2A6762EaDD4701360C1B11F97", + "0xc30911b52b5752447aB08615973e434c801CD652", + ], + epochBasedDistributorAddress: + "0x111AbF466654c166Ee4AC15d6A29a3e0625533db", + epochBasedStakingAddresses: [], + }, + secrets: {}, + storage: {}, + }, + [provider], + ); + expect(result).toEqual({ + canExec: true, + callData: [ + { + data: "0x63453ae1000000000000000000000000280c64c4c4869cf2a6762eadd4701360c1b11f97", + to: "0xbF5DC3f598AFA173135160CDFce6BFeE45c912eF", + }, + { + data: "0x63453ae1000000000000000000000000c30911b52b5752447ab08615973e434c801cd652", + to: "0xbF5DC3f598AFA173135160CDFce6BFeE45c912eF", + }, + ], + }); + }); +}); diff --git a/test/utils/index.ts b/test/utils/index.ts new file mode 100644 index 0000000..603e439 --- /dev/null +++ b/test/utils/index.ts @@ -0,0 +1,68 @@ +import type { JsonRpcProvider } from "@ethersproject/providers"; +import type { + MultiChainProviderConfig, + Web3FunctionContextData, + Web3FunctionRunnerOptions, +} from "@gelatonetwork/web3-functions-sdk"; +import { Web3FunctionBuilder } from "@gelatonetwork/web3-functions-sdk/builder"; +import { Web3FunctionRunner } from "@gelatonetwork/web3-functions-sdk/runtime"; + +export const MAX_RPC_LIMIT = 100; +export const MAX_DOWNLOAD_LIMIT = 10 * 1024 * 1024; +export const MAX_UPLOAD_LIMIT = 5 * 1024 * 1024; +export const MAX_REQUEST_LIMIT = 100; +export const MAX_STORAGE_LIMIT = 1 * 1024 * 1024; + +export const runWeb3Function = async ( + web3FunctionPath: string, + context: Web3FunctionContextData<"onRun">, + providers: JsonRpcProvider[], +) => { + const buildRes = await Web3FunctionBuilder.build(web3FunctionPath, { + debug: false, + }); + + if (!buildRes.success) + throw new Error(`Fail to build web3Function: ${buildRes.error}`); + + const runner = new Web3FunctionRunner(false); + const runtime: "docker" | "thread" = "thread"; + const memory = buildRes.schema.memory; + const rpcLimit = MAX_RPC_LIMIT; + const timeout = buildRes.schema.timeout * 1000; + const version = buildRes.schema.web3FunctionVersion; + + const options: Web3FunctionRunnerOptions = { + runtime, + showLogs: true, + memory, + downloadLimit: MAX_DOWNLOAD_LIMIT, + uploadLimit: MAX_UPLOAD_LIMIT, + requestLimit: MAX_REQUEST_LIMIT, + rpcLimit, + timeout, + storageLimit: MAX_STORAGE_LIMIT, + }; + const script = buildRes.filePath; + + const multiChainProviderConfig: MultiChainProviderConfig = {}; + + for (const provider of providers) { + const chainId = (await provider.getNetwork()).chainId; + + multiChainProviderConfig[chainId] = provider; + } + + const res = await runner.run("onRun", { + script, + context, + options, + version, + multiChainProviderConfig, + }); + + if (!res.success) + throw new Error(`Fail to run web3 function: ${res.error.message}`); + + return res; +};