Skip to content

Commit

Permalink
chore: web3 functions (#3)
Browse files Browse the repository at this point in the history
* chore: add secrets management example

* chore: secrets management scripts

* chore: add example oracle using secrets

* chore: rename js resolver to web3 functions

* chore: bump sdks

* docs: web3 functions command updates

* chore: rename secret oracle example

* feat: add simple storage example

* chore: refactor sdk

Co-authored-by: Brandon Chuah <[email protected]>
  • Loading branch information
goums and brandonchuah authored Jan 26, 2023
1 parent fb645b4 commit 162ef5d
Show file tree
Hide file tree
Showing 19 changed files with 378 additions and 336 deletions.
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
cache/
dist/
node_modules
src/resolvers/fails
.tmp
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,5 @@ src/logs/

.eslintcache

# resolver builds
# web3 functions builds
.tmp
181 changes: 91 additions & 90 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
# JsResolver template <!-- omit in toc -->
Start building you JsResolvers to Automate on Gelato Network
# Web3 Functions template <!-- omit in toc -->
Start building your Web3 Functions to Automate on Gelato Network
<br /><br />


Example task automation using Gelato Ops SDK:
- [Project Setup](#project-setup)
- [Write a Js Resolver](#write-a-js-resolver)
- [Test your resolver](#test-your-resolver)
- [Write a Web3 Function](#write-a-web3-function)
- [Test your web3 function](#test-your-web3-function)
- [Use User arguments](#use-user-arguments)
- [Use State / Storage](#use-state--storage)
- [Upload your JsResolver on IPFS](#upload-your-jsresolver-on-ipfs)
- [Create your JsResolver task](#create-your-jsresolver-task)
- [Use user secrets](#use-user-secrets)
- [Upload your Web3Function on IPFS](#upload-your-web3function-on-ipfs)
- [Create your Web3Function task](#create-your-web3function-task)
- [More examples](#more-examples)
- [Coingecko oracle](#coingecko-oracle)
- [Event listener](#event-listener)
- [Secrets](#secrets)


## Project Setup
Expand All @@ -23,24 +23,24 @@ yarn install
```

2. Configure your local environment:
- Copy `.env_example` to init your own `.env` file
```
cp .env_example .env
```
- Complete your `.env` file with your private settings
```
PROVIDER_URL="" <= görli provider url
PRIVATE_KEY="" <= your deployer private key
```
- Copy `.env_example` to init your own `.env` file
```
cp .env_example .env
```
- Complete your `.env` file with your private settings
```
PROVIDER_URL="" <= görli provider url
PRIVATE_KEY="" <= your deployer private key
```


## Write a Js Resolver
## Write a Web3 Function

- Create a new file in `src/resolvers`
- Register your resolver main function using `JsResolverSdk.onChecker`
- Create a new file in `src/web3Functions`
- Register your main function using `Web3Function.onRun`
- Example:
```typescript
import { JsResolverSdk, JsResolverContext } from "@gelatonetwork/js-resolver-sdk";
import { Web3Function, Web3FunctionContext } from "@gelatonetwork/web3-functions-sdk";
import { Contract, ethers } from "ethers";
import ky from "ky"; // we recommend using ky as axios doesn't support fetch by default

Expand All @@ -49,7 +49,7 @@ const ORACLE_ABI = [
"function updatePrice(uint256)",
];

JsResolverSdk.onChecker(async (context: JsResolverContext) => {
Web3Function.onRun(async (context: Web3FunctionContext) => {
const { userArgs, gelatoArgs, provider } = context;

// Retrieve Last oracle update time
Expand Down Expand Up @@ -84,49 +84,49 @@ JsResolverSdk.onChecker(async (context: JsResolverContext) => {
};
});
```
- create your resolver `schema.json` to specify your runtime configuration:
- create your function `schema.json` to specify your runtime configuration:
```json
{
"jsResolverVersion": "1.0.0",
"web3FunctionVersion": "1.0.0",
"runtime": "js-1.0",
"memory": 128,
"timeout": 60,
"timeout": 30,
"userArgs": {}
}
```


## Test your resolver
## Test your web3 function

- Use `npx js-resolver test FILENAME` command to test your resolver
- Use `npx web3-function test FILENAME` command to test your function

- Options:
- `--show-logs` Show internal Resolver logs
- `--show-logs` Show internal Web3 Function logs
- `--debug` Show Runtime debug messages
- `--chain-id=[number]` Specify the chainId to be used for your Resolver (default: `5`)
- `--user-args=[key]:[value]` Set your Resolver user args
- `--chain-id=[number]` Specify the chainId to be used for your Web3 Function (default: `5`)
- `--user-args=[key]:[value]` Set your Web3 Function user args

- Example: `npx js-resolver test src/resolvers/oracle/index.ts --show-logs`
- Example:<br/> `npx web3-function test src/web3Functions/oracle/index.ts --show-logs`
- Output:
```
JsResolver Build result:
✓ File: ./.tmp/resolver.cjs
Web3Function Build result:
✓ File: ./.tmp/index.js
✓ File size: 1.70mb
✓ Build time: 109.93ms
JsResolver running logs:
Web3Function running logs:
> ChainId: 5
> Last oracle update: 1665512172
> Next oracle update: 1665512472
> Updating price: 1586
JsResolver Result:
Web3Function Result:
✓ Return value: {
canExec: true,
callData: '0x8d6cc56d0000000000000000000000000000000000000000000000000000000000000632'
}
JsResolver Runtime stats:
Web3Function Runtime stats:
✓ Duration: 5.41s
✓ Memory: 57.77mb
```
Expand All @@ -135,20 +135,20 @@ JsResolverSdk.onChecker(async (context: JsResolverContext) => {
1. Declare your expected `userArgs` in you schema, accepted types are 'string', 'string[]', 'number', 'number[]', 'boolean', 'boolean[]':
```json
{
"jsResolverVersion": "1.0.0",
"web3FunctionVersion": "1.0.0",
"runtime": "js-1.0",
"memory": 128,
"timeout": 60,
"timeout": 30,
"userArgs": {
"currency": "string",
"oracle": "string"
}
}
```

2. Access your `userArgs` from the JsResolver context:
2. Access your `userArgs` from the Web3Function context:
```typescript
JsResolverSdk.onChecker(async (context: JsResolverContext) => {
Web3Function.onRun(async (context: Web3FunctionContext) => {
const { userArgs, gelatoArgs, secrets } = context;

// User args:
Expand All @@ -158,9 +158,9 @@ JsResolverSdk.onChecker(async (context: JsResolverContext) => {
});
```

3. Pass `user-args` to the CLI to test your resolver:
3. Pass `user-args` to the CLI to test your web3 function:
```
npx js-resolver test src/resolvers/oracle/index.ts --show-logs --user-args=currency:ethereum --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a
npx web3-function test src/web3Functions/oracle/index.ts --show-logs --user-args=currency:ethereum --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a
```

To pass array argument (eg `string[]`), you can use:
Expand All @@ -170,18 +170,18 @@ To pass array argument (eg `string[]`), you can use:

## Use State / Storage

JsResolvers are stateless scripts, that will run in a new & empty memory context on every execution.
If you need to manage some state variable, we provide a simple key/value store that you can access from your resolver `context`.
Web3Functions are stateless scripts, that will run in a new & empty memory context on every execution.
If you need to manage some state variable, we provide a simple key/value store that you can access from your web3 function `context`.

See the above example to read & update values from your storage:

```typescript
import {
JsResolverSdk,
JsResolverContext,
} from "@gelatonetwork/js-resolver-sdk";
Web3Function,
Web3FunctionContext,
} from "@gelatonetwork/web3-functions-sdk";

JsResolverSdk.onChecker(async (context: JsResolverContext) => {
Web3Function.onRun(async (context: Web3FunctionContext) => {
const { storage, provider } = context;

// Use storage to retrieve previous state (stored values are always string)
Expand All @@ -203,14 +203,12 @@ JsResolverSdk.onChecker(async (context: JsResolverContext) => {
});
```

Test storage execution:
```
npx js-resolver test RESOLVER_FILE
```
Test storage execution:<br/>
`npx web3-function test src/web3Functions/storage/index.ts --show-logs`

You will see your updated key/values:
```
JsResolver Storage updated:
Web3Function Storage updated:
✓ lastBlockNumber: '8321923'
```

Expand All @@ -221,58 +219,61 @@ JsResolver Storage updated:
SECRETS_COINGECKO_API=https://api.coingecko.com/api/v3
```

2. Access your secrets from the JsResolver context:
2. Access your secrets from the Web3Function context:
```typescript
// Get api from secrets
const coingeckoApi = await context.secrets.get("COINGECKO_API");
if (!coingeckoApi)
return { canExec: false, message: `COINGECKO_API not set in secrets` };
```

3. Store your secrets by using `yarn set-secrets`. (Variables with the `SECRETS_` prefix in `.env` will be stored.)
3. Store your secrets by using (Variables with the `SECRETS_` prefix in `.env` will be stored):<br/> `yarn set-secrets`

4. Test your resolver using secrets:<br/>
`npx web3-function test src/web3Functions/secrets/index.ts --show-logs`

5. View complete list of your secrets by using:<br/> `yarn list-secrets`

4. View complete list of your secrets by using `yarn list-secrets`.
6. To delete secrets, use:<br/> `yarn delete-secrets SECRET_KEY SECRET_KEY2`

5. To delete secrets, use `yarn delete-secrets SECRET_KEY SECRET_KEY2`

## Upload your Web3Function on IPFS

## Upload your JsResolver on IPFS
Use `npx web3-function upload FILENAME` command to upload your web3 function.

Use `npx js-resolver upload FILENAME` command to upload your resolver.
Example:
```
npx js-resolver upload src/resolvers/oracle/index.ts
```
Example:<br/>
`npx web3-function upload src/web3Functions/oracle/index.ts`

The uploader will output your JsResolver IPFS CID, that you can use to create your task:
The uploader will output your Web3Function IPFS CID, that you can use to create your task:
```
JsResolver uploaded to ipfs. CID: QmUavazADkj9WL9uVJ7eYkoSybhBSsitEsWFNfVojMYJSk
Web3Function uploaded to ipfs. CID: QmUavazADkj9WL9uVJ7eYkoSybhBSsitEsWFNfVojMYJSk
```


## Create your JsResolver task
## Create your Web3Function task
Use the `ops-sdk` to easily create a new task:
```typescript
const { taskId, tx } = await opsSdk.createTask({
name: "JsResolver - ETH Oracle",
name: "Web3Function - ETH Oracle",
execAddress: oracleAddress,
execSelector: oracleInterface.getSighash("updatePrice"),
dedicatedMsgSender: true,
jsResolverHash: cid, // Pass your js resolver IPFS CID
jsResolverArgs: { // Set your JsResolver arguments
web3FunctionHash: cid, // Pass your js web3 function IPFS CID
web3FunctionArgs: { // Set your Web3Function arguments
oracle: oracleAddress,
currency: "ethereum",
},
});
await tx.wait();
```

Test it with our sample task creation script:
Test it with our sample task creation script:<br/>
`yarn create-task:oracle`

```
Deploying JsResolver on IPFS...
JsResolver IPFS CID: QmUavazADkj9WL9uVJ7eYkoSybhBSsitEsWFNfVojMYJSk
Deploying Web3Function on IPFS...
Web3Function IPFS CID: QmUavazADkj9WL9uVJ7eYkoSybhBSsitEsWFNfVojMYJSk
Creating automate task...
Task created, taskId: 0xedcc73b5cc1e7b3dc79cc899f239193791f6bb16dd2a67be1c0fdf3495533325
Expand All @@ -285,36 +286,36 @@ Task created, taskId: 0xedcc73b5cc1e7b3dc79cc899f239193791f6bb16dd2a67be1c0fdf34

Fetch price data from Coingecko API to update your on-chain Oracle

Source: [`src/resolvers/oracle/index.ts`](./src/resolvers/oracle/index.ts)
Source: [`src/web3Functions/oracle/index.ts`](./src/web3Functions/oracle/index.ts)

Run:
```
npx js-resolver test src/resolvers/oracle/index.ts --show-logs --user-args=currency:ethereum --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a
```
Run:<br/>
`npx web3-function test src/web3Functions/oracle/index.ts --show-logs --user-args=currency:ethereum --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a`

Create task:
```
yarn create-task:oracle
```
Create task: <br/>
`yarn create-task:oracle`


### Event listener

Listen to smart contract events and use storage context to maintain your execution state.

Source: [`src/resolvers/event-listener/index.ts`](./src/resolvers/event-listener/index.ts)
Source: [`src/web3Functions/event-listener/index.ts`](./src/web3Functions/event-listener/index.ts)

Run:
```
npx js-resolver test src/resolvers/event-listener/index.ts --show-logs --user-args=counter:0x8F143A5D62de01EAdAF9ef16d4d3694380066D9F --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a
```
Run:<br/>
`npx web3-function test src/web3Functions/event-listener/index.ts --show-logs --user-args=counter:0x8F143A5D62de01EAdAF9ef16d4d3694380066D9F --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a`

Create task:
```
yarn create-task:event
```
Create task: <br/>
`yarn create-task:event`

### Secrets

Fetch data from a private API to update your on-chain Oracle

Source: [`src/web3Functions/secrets/index.ts`](./src/web3Functions/secrets/index.ts)

Run:<br/>
`npx web3-function test src/web3Functions/secrets/index.ts --show-logs --user-args=currency:ethereum --user-args=oracle:0x6a3c82330164822A8a39C7C0224D20DB35DD030a`

Create task: <br/>
`yarn create-task:secrets`

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"name": "@gelatonetwork/js-resolver-template",
"name": "@gelatonetwork/web3-functions-template",
"version": "0.1.0",
"description": "Gelato Automate JS Resolvers template",
"url": "https://github.com/gelatodigital/js-resolver-template",
"description": "Gelato Web3 Functions template",
"url": "https://github.com/gelatodigital/web3-functions-template",
"scripts": {
"build": "rm -rf dist && tsc",
"format": "prettier --write '*/**/*.{js,json,md,ts}'",
Expand Down Expand Up @@ -31,8 +31,8 @@
"typescript": "^4.7.0"
},
"dependencies": {
"@gelatonetwork/js-resolver-sdk": "^0.1.6",
"@gelatonetwork/ops-sdk": "^2.0.18-alpha",
"@gelatonetwork/ops-sdk": "^2.1.0-alpha",
"@gelatonetwork/web3-functions-sdk": "^0.2.0",
"dotenv": "^16.0.3",
"ethers": "^5.7.1",
"ky": "^0.32.2"
Expand Down
Loading

0 comments on commit 162ef5d

Please sign in to comment.