From fd2e690e034e632adda27a8646527732dc2bd98b Mon Sep 17 00:00:00 2001 From: koteld Date: Wed, 10 Jan 2024 23:37:45 +0100 Subject: [PATCH] run functions simulations as purely a deno-runtime execution --- README.md | 15 +--- package.json | 4 +- src/HardhatChainlink.ts | 44 ++++------ src/index.ts | 9 +- src/sandbox/functionsSimulations/index.ts | 86 ++----------------- .../sandbox/functionsSimulation/index.ts | 8 +- .../hardhat/hardhat.config.ts | 6 +- test/functionsSimulation.test.ts | 26 ++++-- 8 files changed, 54 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 1b580ef..c533e2b 100644 --- a/README.md +++ b/README.md @@ -224,20 +224,7 @@ It is useful for debugging and for checking whether the source code you supply t > **Note** Install [Deno](https://deno.com/) and add it to PATH, run ```deno --version``` to verify installation. Instructions: [https://deno.com/#installation](https://deno.com/#installation). -Before you run Functions request simulations, you can configure it. -To achieve this, additional parameters have been included in the `chainlink` group of `hardhat.config.ts`: -```ts -module.exports = { - chainlink: { - functions_simulation: { - port, // Ganache local blockchain port, default: "8546" - } - }, - ... -} -``` - -Once these parameters are specified, Functions requests simulations could be performed following the [sandbox documentation](SANDBOX.md#service-alias-functionssimulation). +Functions' requests simulations could be performed following the [sandbox documentation](SANDBOX.md#service-alias-functionssimulation). ### Local testing > **Note** diff --git a/package.json b/package.json index c80aca7..3c1117b 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,9 @@ "test:vrfCoordinator": "mocha --exit 'test/vrfCoordinator.test.ts'", "test:automationRegistrar": "mocha --exit 'test/automationRegistrar.test.ts'", "test:automationRegistry": "mocha --exit 'test/automationRegistry.test.ts'", - "test:functionsRouter": "mocha --exit 'test/functionsRouter.test.ts'", + "test:functions": "mocha --exit 'test/functionsRouter.test.ts'", "test:functionsSimulation": "mocha --exit 'test/functionsSimulation.test.ts'", - "test": "yarn test:prepare && yarn test:dataFeed && yarn test:dataFeedProxy && yarn test:ensFeedsResolver && yarn test:feedRegistry && yarn test:l2FeedUptimeSequencer && yarn test:vrfCoordinator && yarn test:automationRegistrar && yarn test:automationRegistry && yarn test:functionsRouter", + "test": "yarn test:prepare && yarn test:dataFeed && yarn test:dataFeedProxy && yarn test:ensFeedsResolver && yarn test:feedRegistry && yarn test:l2FeedUptimeSequencer && yarn test:vrfCoordinator && yarn test:automationRegistrar && yarn test:automationRegistry && yarn test:functions", "copyArtifacts": "copyfiles -a -f ./node_modules/@chainlink/contracts/abi/**/* chainlink-artifacts", "removeArtifacts": "rm -rf chainlink-artifacts", "compile": "hardhat compile", diff --git a/src/HardhatChainlink.ts b/src/HardhatChainlink.ts index c0bf2bd..1d8a052 100644 --- a/src/HardhatChainlink.ts +++ b/src/HardhatChainlink.ts @@ -1,13 +1,4 @@ -import { DecodedResult } from "@chainlink/functions-toolkit/dist/decodeResult"; -import { - CodeLanguage, - FunctionsResponse, - GatewayResponse, - Location, - RequestCommitment, - ReturnType, - ThresholdPublicKey, -} from "@chainlink/functions-toolkit/dist/types"; +import * as functionsToolkit from "@chainlink/functions-toolkit"; import "@nomiclabs/hardhat-ethers"; import { BigNumber, BigNumberish, BytesLike } from "ethers"; import { HardhatRuntimeEnvironment } from "hardhat/types"; @@ -1184,7 +1175,7 @@ class Functions { public timeoutRequests( functionsRouterAddress: string, - requestCommitments: RequestCommitment[], + requestCommitments: functionsToolkit.RequestCommitment[], overrides?: Overrides ): Promise<{ transactionHash: string }> { return functions.timeoutRequests( @@ -1220,7 +1211,7 @@ class Functions { functionsRouterAddress: string, requestId: string, timeout?: number - ): Promise { + ): Promise { return functions.listenForResponse( this.hre, functionsRouterAddress, @@ -1235,7 +1226,7 @@ class Functions { timeout?: number, confirmations?: number, checkInterval?: number - ): Promise { + ): Promise { return functions.listenForResponseFromTransaction( this.hre, functionsRouterAddress, @@ -1249,7 +1240,7 @@ class Functions { public listenForResponses( functionsRouterAddress: string, subscriptionId: string, - callback: (functionsResponse: FunctionsResponse) => any + callback: (functionsResponse: functionsToolkit.FunctionsResponse) => any ): Promise { return functions.listenForResponses( this.hre, @@ -1274,7 +1265,7 @@ class Functions { functionsRouterAddress: string, donId: string ): Promise<{ - thresholdPublicKey: ThresholdPublicKey; + thresholdPublicKey: functionsToolkit.ThresholdPublicKey; donPublicKey: string; }> { return functions.fetchKeys(this.hre, functionsRouterAddress, donId); @@ -1348,7 +1339,7 @@ class Functions { donId: string, gatewayUrls: string[] ): Promise<{ - result: GatewayResponse; + result: functionsToolkit.GatewayResponse; error?: string; }> { return functions.listDONHostedEncryptedSecrets( @@ -1382,7 +1373,7 @@ class Functions { toBlock?: number | "latest", pastBlocksToSearch?: number, overrides?: Overrides - ): Promise { + ): Promise { return functions.fetchRequestCommitment( this.hre, functionsRouterAddress, @@ -1440,10 +1431,10 @@ class Utils { } public async buildFunctionsRequestCBOR( - codeLocation: Location, - codeLanguage: CodeLanguage, + codeLocation: functionsToolkit.Location, + codeLanguage: functionsToolkit.CodeLanguage, source: string, - secretsLocation?: Location, + secretsLocation?: functionsToolkit.Location, encryptedSecretsReference?: string, args?: string[], bytesArgs?: string[] @@ -1461,8 +1452,8 @@ class Utils { public async decodeHexString( resultHexstring: string, - expectedReturnType: ReturnType - ): Promise { + expectedReturnType: functionsToolkit.ReturnType + ): Promise { return utils.decodeHexString(resultHexstring, expectedReturnType); } } @@ -1633,12 +1624,13 @@ class FunctionsSimulation { public async simulateRequest( source: string, - args?: string[], - bytesArgs?: string[] - ): Promise { + secrets: Record | {}, + args: string[] | [], + bytesArgs: string[] | [] + ): Promise { return functionsSimulations.simulateRequest( - this.hre, source, + secrets, args, bytesArgs ); diff --git a/src/index.ts b/src/index.ts index 594fa79..f025138 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,9 +27,6 @@ export interface ChainlinkUserConfig { pg_password?: string; pg_db?: string; }; - functions_simulation: { - port?: number; - }; } // Add our types to the Hardhat config @@ -45,8 +42,7 @@ declare module "hardhat/types/config" { extendConfig( (config: HardhatConfig, userConfig: Readonly) => { - const { confirmations, node, functions_simulation } = - userConfig.chainlink ?? {}; + const { confirmations, node } = userConfig.chainlink ?? {}; config.chainlink = { confirmations: confirmations || 1, node: { @@ -61,9 +57,6 @@ extendConfig( pg_password: node?.pg_password, pg_db: node?.pg_db, }, - functions_simulation: { - port: functions_simulation?.port, - }, }; } ); diff --git a/src/sandbox/functionsSimulations/index.ts b/src/sandbox/functionsSimulations/index.ts index ffe7390..ed70750 100644 --- a/src/sandbox/functionsSimulations/index.ts +++ b/src/sandbox/functionsSimulations/index.ts @@ -1,87 +1,15 @@ import * as functionsToolkit from "@chainlink/functions-toolkit"; -import { DecodedResult } from "@chainlink/functions-toolkit/dist/decodeResult"; -import { ReturnType } from "@chainlink/functions-toolkit/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; - -import { DEFAULT_PORT } from "../../shared/constants"; -import * as functionsConsumer from "../functionsConsumer"; export const simulateRequest = async ( - hre: HardhatRuntimeEnvironment, source: string, - args?: string[], - bytesArgs?: string[] -): Promise => { - const simulationDeployment = - await functionsToolkit.startLocalFunctionsTestnet( - undefined, - {}, - hre.config.chainlink.functions_simulation?.port || DEFAULT_PORT - ); - - const provider = new hre.ethers.providers.JsonRpcProvider( - `http://localhost:${ - hre.config.chainlink.functions_simulation?.port || DEFAULT_PORT - }/` - ); - const admin = new hre.ethers.Wallet( - simulationDeployment.adminWallet.privateKey, - provider - ); - const functionsConsumerAddress = await functionsConsumer.deploy( - hre, - simulationDeployment.functionsRouterContract.address, - simulationDeployment.donId, - { - signer: admin, - provider, - } - ); - - const subscriptionManager = - await hre.chainlink.functions.initializeSubscriptionManager( - simulationDeployment.functionsRouterContract.address, - simulationDeployment.linkTokenContract.address, - { - signer: admin, - provider, - } - ); - - const responseListener = - await hre.chainlink.functions.initializeResponseListener( - simulationDeployment.functionsRouterContract.address, - { - signer: admin, - provider, - } - ); - - const { subscriptionId } = await subscriptionManager.createSubscription( - functionsConsumerAddress - ); - - const juelsAmount = hre.ethers.utils.parseUnits("100", "ether"); - await subscriptionManager.fundSubscription(juelsAmount, subscriptionId); - - const { transactionHash } = await functionsConsumer.sendRequest( - hre, - functionsConsumerAddress, - subscriptionId, + secrets: Record, + args: string[], + bytesArgs: string[] +): Promise => { + return functionsToolkit.simulateScript({ source, - "0xabcd", - functionsToolkit.Location.Remote, + secrets, args, bytesArgs, - 100_000 - ); - - const receipt = await provider.getTransactionReceipt(transactionHash); - const requestId = receipt.logs[0].topics[1]; - const response = await responseListener.listenForResponse(requestId); - - return hre.chainlink.utils.decodeHexString( - response.responseBytesHexstring, - ReturnType.string - ); + }); }; diff --git a/src/tasks/sandbox/functionsSimulation/index.ts b/src/tasks/sandbox/functionsSimulation/index.ts index 31abf13..145de32 100644 --- a/src/tasks/sandbox/functionsSimulation/index.ts +++ b/src/tasks/sandbox/functionsSimulation/index.ts @@ -4,20 +4,22 @@ import * as functionsSimulations from "../../../sandbox/functionsSimulations"; export const simulateRequest: ActionType<{ source: string; + secrets?: string; args?: string; bytesArgs?: string; }> = async (taskArgs, hre): Promise => { + const secrets = taskArgs.secrets ? JSON.parse(taskArgs.secrets) : {}; const args = taskArgs.args ? taskArgs.args.split(",").map((value) => value.trim()) : []; const bytesArgs = taskArgs.bytesArgs ? taskArgs.bytesArgs.split(",").map((value) => value.trim()) : []; - const decodedResult = await functionsSimulations.simulateRequest( - hre, + const simulationResult = await functionsSimulations.simulateRequest( taskArgs.source, + secrets, args, bytesArgs ); - return decodedResult.toString(); + return JSON.stringify(simulationResult); }; diff --git a/test/fixture-projects/hardhat/hardhat.config.ts b/test/fixture-projects/hardhat/hardhat.config.ts index bad1a77..d6cd2ef 100644 --- a/test/fixture-projects/hardhat/hardhat.config.ts +++ b/test/fixture-projects/hardhat/hardhat.config.ts @@ -4,11 +4,7 @@ import { HardhatUserConfig } from "hardhat/types"; import "../../../src/index"; const config: HardhatUserConfig = { - chainlink: { - functions_simulation: { - secrets: { test: "hello world" }, - }, - }, + chainlink: {}, }; export default config; diff --git a/test/functionsSimulation.test.ts b/test/functionsSimulation.test.ts index ffcfa22..d291d29 100644 --- a/test/functionsSimulation.test.ts +++ b/test/functionsSimulation.test.ts @@ -17,11 +17,15 @@ describe("Test chainlink:sandbox:functionsSimulation module [SKIP FOR GITHUB ACT const result = await this.hre.chainlink.sandbox.functionsSimulation.simulateRequest( 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])', + { test: "hello" }, ["hello", "world"], ["0x1234", "0x5678"] ); - expect(result).to.eq("hello world hello world0x12340x5678"); + expect(result.capturedTerminalOutput).to.eq(""); + expect(result.responseBytesHexstring).to.eq( + "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738" + ); }); }); @@ -29,17 +33,21 @@ describe("Test chainlink:sandbox:functionsSimulation module [SKIP FOR GITHUB ACT it("Run functions simulation", async function () { if (isGithubActions) this.skip(); - const result = await this.hre.run( + const resultJSON = await this.hre.run( `${PACKAGE_NAME}:${Task.functionsSimulation}:${FunctionsSimulationSubtask.simulateRequest}`, { source: 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])', + secrets: '{"test":"hello"}', args: "hello, world", bytesArgs: "0x1234, 0x5678", } ); - - expect(result).to.eq("hello world hello world0x12340x5678"); + const result = JSON.parse(resultJSON); + expect(result.capturedTerminalOutput).to.eq(""); + expect(result.responseBytesHexstring).to.eq( + "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738" + ); }); }); @@ -47,20 +55,24 @@ describe("Test chainlink:sandbox:functionsSimulation module [SKIP FOR GITHUB ACT it("Run functions simulation", async function () { if (isGithubActions) this.skip(); - const result = await this.hre.run( + const resultJSON = await this.hre.run( `${PACKAGE_NAME}:${Task.functionsSimulation}`, { subtask: FunctionsSimulationSubtask.simulateRequest, args: JSON.stringify({ source: 'return Functions.encodeString(secrets.test + " " + args[0] + " " + args[1] + bytesArgs[0] + bytesArgs[1])', + secrets: '{"test":"hello"}', args: "hello, world", bytesArgs: "0x1234, 0x5678", }), } ); - - expect(result).to.eq("hello world hello world0x12340x5678"); + const result = JSON.parse(resultJSON); + expect(result.capturedTerminalOutput).to.eq(""); + expect(result.responseBytesHexstring).to.eq( + "0x68656c6c6f2068656c6c6f20776f726c64307831323334307835363738" + ); }); }); });