Skip to content

Commit 19186d8

Browse files
Http Trigger Proxy (#125)
* Propose proxy * Improve path resolution inside generate-chain selectors script
1 parent 06eac99 commit 19186d8

File tree

14 files changed

+493
-16
lines changed

14 files changed

+493
-16
lines changed

bun.lock

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,21 @@
1010
"typescript": "5.9.2",
1111
},
1212
},
13+
"packages/cre-http-trigger": {
14+
"name": "@chainlink/cre-http-trigger",
15+
"version": "0.0.8-alpha",
16+
"dependencies": {
17+
"uuid": "13.0.0",
18+
"viem": "2.38.0",
19+
"zod": "3.25.76",
20+
},
21+
"devDependencies": {
22+
"@types/bun": "1.2.21",
23+
},
24+
},
1325
"packages/cre-sdk": {
1426
"name": "@chainlink/cre-sdk",
15-
"version": "0.0.5-alpha",
27+
"version": "0.0.8-alpha",
1628
"bin": {
1729
"cre-compile": "bin/cre-compile.ts",
1830
},
@@ -37,7 +49,7 @@
3749
},
3850
"packages/cre-sdk-examples": {
3951
"name": "@chainlink/cre-sdk-examples",
40-
"version": "0.0.5-alpha",
52+
"version": "0.0.8-alpha",
4153
"dependencies": {
4254
"@bufbuild/protobuf": "2.6.3",
4355
"@chainlink/cre-sdk": "workspace:*",
@@ -50,7 +62,7 @@
5062
},
5163
"packages/cre-sdk-javy-plugin": {
5264
"name": "@chainlink/cre-sdk-javy-plugin",
53-
"version": "0.0.4-alpha",
65+
"version": "0.0.6-alpha",
5466
"bin": {
5567
"cre-setup": "bin/setup.ts",
5668
"cre-compile-workflow": "bin/compile-workflow.ts",
@@ -100,6 +112,8 @@
100112

101113
"@bufbuild/protoplugin": ["@bufbuild/[email protected]", "", { "dependencies": { "@bufbuild/protobuf": "2.6.3", "@typescript/vfs": "^1.5.2", "typescript": "5.4.5" } }, "sha512-VceMuxeRukxGeABfo34SXq0VqY1MU+mzS+PBf0HAWo97ylFut8F6sQ3mV0tKiM08UQ/xQco7lxCn83BkoxrWrA=="],
102114

115+
"@chainlink/cre-http-trigger": ["@chainlink/cre-http-trigger@workspace:packages/cre-http-trigger"],
116+
103117
"@chainlink/cre-sdk": ["@chainlink/cre-sdk@workspace:packages/cre-sdk"],
104118

105119
"@chainlink/cre-sdk-examples": ["@chainlink/cre-sdk-examples@workspace:packages/cre-sdk-examples"],
@@ -108,7 +122,7 @@
108122

109123
"@noble/ciphers": ["@noble/[email protected]", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
110124

111-
"@noble/curves": ["@noble/[email protected].6", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="],
125+
"@noble/curves": ["@noble/[email protected].1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="],
112126

113127
"@noble/hashes": ["@noble/[email protected]", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
114128

@@ -134,7 +148,7 @@
134148

135149
"@typescript/vfs": ["@typescript/[email protected]", "", { "dependencies": { "debug": "^4.1.1" }, "peerDependencies": { "typescript": "*" } }, "sha512-JwoxboBh7Oz1v38tPbkrZ62ZXNHAk9bJ7c9x0eI5zBfBnBYGhURdbnh7Z4smN/MV48Y5OCcZb58n972UtbazsA=="],
136150

137-
"abitype": ["abitype@1.0.8", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="],
151+
"abitype": ["abitype@1.1.0", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A=="],
138152

139153
"braces": ["[email protected]", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
140154

@@ -176,7 +190,7 @@
176190

177191
"ms": ["[email protected]", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
178192

179-
"ox": ["ox@0.8.7", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw=="],
193+
"ox": ["ox@0.9.6", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.9", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg=="],
180194

181195
"picomatch": ["[email protected]", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
182196

@@ -212,7 +226,9 @@
212226

213227
"undici-types": ["[email protected]", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
214228

215-
"viem": ["[email protected]", "", { "dependencies": { "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.7", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA=="],
229+
"uuid": ["[email protected]", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w=="],
230+
231+
"viem": ["[email protected]", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.1.0", "isows": "1.0.7", "ox": "0.9.6", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-YU5TG8dgBNeYPrCMww0u9/JVeq2ZCk9fzk6QybrPkBooFysamHXL1zC3ua10aLPt9iWoA/gSVf1D9w7nc5B1aA=="],
216232

217233
"ws": ["[email protected]", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
218234

@@ -221,5 +237,23 @@
221237
"zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
222238

223239
"@bufbuild/protoplugin/typescript": ["[email protected]", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ=="],
240+
241+
"@chainlink/cre-sdk/viem": ["[email protected]", "", { "dependencies": { "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.7", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA=="],
242+
243+
"@chainlink/cre-sdk-examples/viem": ["[email protected]", "", { "dependencies": { "@noble/curves": "1.9.6", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.0.8", "isows": "1.0.7", "ox": "0.8.7", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-HJZG9Wt0DLX042MG0PK17tpataxtdAEhpta9/Q44FqKwy3xZMI5Lx4jF+zZPuXFuYjZ68R0PXqRwlswHs6r4gA=="],
244+
245+
"@scure/bip32/@noble/curves": ["@noble/[email protected]", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="],
246+
247+
"@chainlink/cre-sdk-examples/viem/@noble/curves": ["@noble/[email protected]", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="],
248+
249+
"@chainlink/cre-sdk-examples/viem/abitype": ["[email protected]", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="],
250+
251+
"@chainlink/cre-sdk-examples/viem/ox": ["[email protected]", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw=="],
252+
253+
"@chainlink/cre-sdk/viem/@noble/curves": ["@noble/[email protected]", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-GIKz/j99FRthB8icyJQA51E8Uk5hXmdyThjgQXRKiv9h0zeRlzSCLIzFw6K1LotZ3XuB7yzlf76qk7uBmTdFqA=="],
254+
255+
"@chainlink/cre-sdk/viem/abitype": ["[email protected]", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3 >=3.22.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-ZeiI6h3GnW06uYDLx0etQtX/p8E24UaHHBj57RSjK7YBFe7iuVn07EDpOeP451D06sF27VOz9JJPlIKJmXgkEg=="],
256+
257+
"@chainlink/cre-sdk/viem/ox": ["[email protected]", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "^1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.0.8", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-W1f0FiMf9NZqtHPEDEAEkyzZDwbIKfmH2qmQx8NNiQ/9JhxrSblmtLJsSfTtQG5YKowLOnBlLVguCyxm/7ztxw=="],
224258
}
225259
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# MAKE A COPY OF THIS FILE AND NAME IT .env
2+
# Fill your .env variables with your desired values.
3+
# NEVER commit your actual .env file to the repository.
4+
#
5+
###############################################################################
6+
### REQUIRED ENVIRONMENT VARIABLES - SENSITIVE INFORMATION ###
7+
### DO NOT STORE RAW SECRETS HERE IN PLAINTEXT IF AVOIDABLE ###
8+
### DO NOT UPLOAD OR SHARE THIS FILE UNDER ANY CIRCUMSTANCES ###
9+
###############################################################################
10+
#
11+
# Private key that is allowed to trigger your Http Trigger based workflows
12+
PRIVATE_KEY=0x
13+
# CRE Gateway url to use, starts with https
14+
GATEWAY_URL=http://localhost:5002/user
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# CRE HTTP Trigger Proxy
2+
3+
Utility app that allows for easy triggering of workflows via HTTP.
4+
5+
## Setup
6+
7+
First you need to prepare your environment variables. You can do this by copying the `.env.example` file to `.env` and filling in the values.
8+
9+
`PRIVATE_KEY=0x...` - This is the private key corresponding with your workflow `authorizedKeys`.
10+
`GATEWAY_URL=http://localhost:5002/user` - This is the URL of the gateway you are using. You can use https:// and point to the published gateway.
11+
12+
Next you would need to install the dependencies, for that run: `bun install`.
13+
14+
Next you would need to run the app with `bun start`. This will spin tiny http server, that would validate the env variables and will prepare the request that will trigger the workflow. Basically it's your **proxy** now.
15+
16+
## Usage
17+
18+
You can use postman or any other http client to send a request.
19+
20+
```http
21+
POST http://localhost:3000/trigger?workflowID=0xYourWorkflowID
22+
Content-Type: application/json
23+
24+
{
25+
"input": {
26+
"anyInput": "your workflow expects"
27+
}
28+
}
29+
```
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
3+
"vcs": {
4+
"enabled": false,
5+
"clientKind": "git",
6+
"useIgnoreFile": false
7+
},
8+
"files": {
9+
"ignoreUnknown": false,
10+
"includes": ["src/**/*", "**/*.json"]
11+
},
12+
"formatter": {
13+
"enabled": true,
14+
"indentStyle": "tab",
15+
"lineWidth": 100
16+
},
17+
"assist": {
18+
"enabled": true
19+
},
20+
"linter": {
21+
"enabled": true,
22+
"includes": ["!src/generated/**"],
23+
"rules": {
24+
"recommended": true,
25+
"correctness": {
26+
"noUnusedVariables": "error"
27+
},
28+
"suspicious": {
29+
"noExplicitAny": "warn"
30+
},
31+
"style": {
32+
"useConst": "error"
33+
}
34+
}
35+
},
36+
"javascript": {
37+
"formatter": {
38+
"quoteStyle": "single",
39+
"semicolons": "asNeeded"
40+
}
41+
}
42+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "@chainlink/cre-http-trigger",
3+
"private": true,
4+
"version": "0.0.8-alpha",
5+
"type": "module",
6+
"main": "src/index.ts",
7+
"author": "Ernest Nowacki",
8+
"license": "BUSL-1.1",
9+
"description": "Utility app that allows for easy triggering of workflows via HTTP.",
10+
"scripts": {
11+
"start": "bun src/index.ts",
12+
"check": "biome check --write ${BIOME_PATHS:-.}",
13+
"format": "biome format --write ${BIOME_PATHS:-.}",
14+
"full-checks": "bun typecheck && bun check",
15+
"lint": "biome lint --write",
16+
"typecheck": "tsc"
17+
},
18+
"dependencies": {
19+
"uuid": "13.0.0",
20+
"viem": "2.38.0",
21+
"zod": "3.25.76"
22+
},
23+
"devDependencies": {
24+
"@types/bun": "1.2.21"
25+
},
26+
"engines": {
27+
"bun": ">=1.2.21"
28+
}
29+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { v4 as uuidv4 } from 'uuid'
2+
import { type Hex, parseSignature } from 'viem'
3+
import { privateKeyToAccount } from 'viem/accounts'
4+
import type { WorkflowSelector } from './schemas'
5+
import { base64URLEncode, sha256 } from './utils'
6+
7+
export interface JSONRPCRequest {
8+
jsonrpc: string
9+
id: string
10+
method: string
11+
params: {
12+
input: any
13+
workflow: WorkflowSelector
14+
}
15+
}
16+
17+
export const createJWT = async (request: JSONRPCRequest, privateKey: Hex): Promise<string> => {
18+
const account = privateKeyToAccount(privateKey)
19+
const address = account.address
20+
21+
// Create JWT header
22+
const header = {
23+
alg: 'ETH',
24+
typ: 'JWT',
25+
}
26+
27+
// Create JWT payload with request and metadata
28+
const now = Math.floor(Date.now() / 1000)
29+
30+
// Note: Request needs to be in the following order:
31+
// Version string `json:"jsonrpc"`
32+
// ID string `json:"id"`
33+
// Method string `json:"method"`
34+
// Params *Params `json:"params"`
35+
36+
const payload = {
37+
digest: `0x${sha256(request)}`,
38+
iss: address,
39+
iat: now,
40+
exp: now + 300, // 5 minutes expiration
41+
jti: uuidv4(),
42+
}
43+
44+
// Encode header and payload
45+
const encodedHeader = base64URLEncode(JSON.stringify(header))
46+
const encodedPayload = base64URLEncode(JSON.stringify(payload))
47+
const rawMessage = `${encodedHeader}.${encodedPayload}`
48+
49+
// Sign the message - viem's signMessage handles the Ethereum Signed Message prefix and hashing
50+
const signature = await account.signMessage({
51+
message: rawMessage,
52+
})
53+
54+
// Convert signature to JWT format (r, s, v components)
55+
const { r, s, v, yParity } = parseSignature(signature)
56+
// Use yParity if v is undefined (yParity is 0 or 1)
57+
const recoveryId = v !== undefined ? (v >= 27n ? v - 27n : v) : yParity
58+
59+
if (recoveryId === undefined) {
60+
throw new Error('Unable to extract recovery ID from signature')
61+
}
62+
63+
// Combine r, s, and adjusted v into a single buffer
64+
const signatureBytes = Buffer.concat([
65+
Buffer.from(r.slice(2), 'hex'), // Remove 0x prefix from r
66+
Buffer.from(s.slice(2), 'hex'), // Remove 0x prefix from s
67+
Buffer.from([Number(recoveryId)]),
68+
])
69+
70+
const encodedSignature = base64URLEncode(signatureBytes.toString('binary'))
71+
72+
return `${rawMessage}.${encodedSignature}`
73+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { type Hex } from 'viem'
2+
import { z } from 'zod'
3+
4+
const configSchema = z.object({
5+
gatewayURL: z.string(),
6+
privateKey: z.string(),
7+
})
8+
9+
export type Config = z.infer<typeof configSchema>
10+
11+
export const getConfig = () => {
12+
const config = configSchema.parse({
13+
gatewayURL: process.env.GATEWAY_URL,
14+
privateKey:
15+
(process.env.PRIVATE_KEY?.startsWith('0x')
16+
? process.env.PRIVATE_KEY
17+
: `0x${process.env.PRIVATE_KEY}`) || '0x',
18+
})
19+
20+
return {
21+
gatewayURL: config.gatewayURL,
22+
privateKey: config.privateKey as Hex,
23+
}
24+
}

0 commit comments

Comments
 (0)