From 9ff8599ac4afcf6447f612bf910a5893969d2cb5 Mon Sep 17 00:00:00 2001 From: Wyatt Barnes Date: Thu, 23 Oct 2025 16:36:54 -1000 Subject: [PATCH 1/2] feat: add derive address example --- sign-as-action/nodejs/package.json | 2 +- .../src/deriveLitActionWalletAddress.ts | 31 +++++ sign-as-action/nodejs/src/index.ts | 5 +- .../signAsAction.ts} | 0 .../verifyActionSignature.ts} | 0 sign-as-action/nodejs/src/signAsAction.ts | 2 +- .../nodejs/src/verifyActionSignature.ts | 2 +- sign-as-action/nodejs/test/index.spec.ts | 39 +++++- yarn.lock | 112 +++++++++++++++++- 9 files changed, 182 insertions(+), 11 deletions(-) create mode 100644 sign-as-action/nodejs/src/deriveLitActionWalletAddress.ts rename sign-as-action/nodejs/src/{signAsActionLitAction.ts => litActions/signAsAction.ts} (100%) rename sign-as-action/nodejs/src/{verifyActionSignatureLitAction.ts => litActions/verifyActionSignature.ts} (100%) diff --git a/sign-as-action/nodejs/package.json b/sign-as-action/nodejs/package.json index ae7ddbf8..fbcb0ab1 100644 --- a/sign-as-action/nodejs/package.json +++ b/sign-as-action/nodejs/package.json @@ -11,7 +11,7 @@ "@dotenvx/dotenvx": "^0.44.1", "@lit-protocol/auth-helpers": "^7.0.4", "@lit-protocol/constants": "^7.0.4", - "@lit-protocol/contracts-sdk": "^7.0.4", + "@lit-protocol/contracts-sdk": "^7.3.1", "@lit-protocol/lit-node-client": "^7.0.4", "ethers": "5.7.2" }, diff --git a/sign-as-action/nodejs/src/deriveLitActionWalletAddress.ts b/sign-as-action/nodejs/src/deriveLitActionWalletAddress.ts new file mode 100644 index 00000000..bd57320c --- /dev/null +++ b/sign-as-action/nodejs/src/deriveLitActionWalletAddress.ts @@ -0,0 +1,31 @@ +import * as ethers from "ethers"; +import { LIT_RPC } from "@lit-protocol/constants"; +import { LitContracts } from "@lit-protocol/contracts-sdk"; + +import { getEnv } from "./utils"; + +const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY"); + +export const deriveLitActionWalletAddress = async ({ litActionIpfsCid }: { litActionIpfsCid: string }) => { + const ethersSigner = new ethers.Wallet( + ETHEREUM_PRIVATE_KEY, + new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE) + ); + + const contractClient = new LitContracts({ signer: ethersSigner }); + await contractClient.connect(); + + const derivedKeyId = ethers.utils.keccak256( + ethers.utils.toUtf8Bytes(`lit_action_${litActionIpfsCid}`) + ); + + const derivedPubkey = await contractClient.pubkeyRouterContract.read.getDerivedPubkey( + contractClient.stakingContract.read.address, + derivedKeyId + ); + + return { + derivedPubkey, + derivedAddress: ethers.utils.computeAddress(derivedPubkey), + } +}; diff --git a/sign-as-action/nodejs/src/index.ts b/sign-as-action/nodejs/src/index.ts index 37b9892a..4a7b20fa 100644 --- a/sign-as-action/nodejs/src/index.ts +++ b/sign-as-action/nodejs/src/index.ts @@ -1,4 +1,5 @@ export * from "./signAsAction"; export * from "./verifyActionSignature"; -export * from './signAsActionLitAction'; -export * from './verifyActionSignatureLitAction'; \ No newline at end of file +export * from "./deriveLitActionWalletAddress"; +export * from './litActions/signAsAction'; +export * from './litActions/verifyActionSignature'; \ No newline at end of file diff --git a/sign-as-action/nodejs/src/signAsActionLitAction.ts b/sign-as-action/nodejs/src/litActions/signAsAction.ts similarity index 100% rename from sign-as-action/nodejs/src/signAsActionLitAction.ts rename to sign-as-action/nodejs/src/litActions/signAsAction.ts diff --git a/sign-as-action/nodejs/src/verifyActionSignatureLitAction.ts b/sign-as-action/nodejs/src/litActions/verifyActionSignature.ts similarity index 100% rename from sign-as-action/nodejs/src/verifyActionSignatureLitAction.ts rename to sign-as-action/nodejs/src/litActions/verifyActionSignature.ts diff --git a/sign-as-action/nodejs/src/signAsAction.ts b/sign-as-action/nodejs/src/signAsAction.ts index 6c5f4312..73ccc6f3 100644 --- a/sign-as-action/nodejs/src/signAsAction.ts +++ b/sign-as-action/nodejs/src/signAsAction.ts @@ -3,7 +3,7 @@ import { LitNodeClient } from "@lit-protocol/lit-node-client"; import { LIT_RPC, LIT_NETWORK } from "@lit-protocol/constants"; import { getEnv, getSessionSigs } from "./utils"; -import { signAsActionLitAction } from "./signAsActionLitAction"; +import { signAsActionLitAction } from "./litActions/signAsAction"; const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY"); diff --git a/sign-as-action/nodejs/src/verifyActionSignature.ts b/sign-as-action/nodejs/src/verifyActionSignature.ts index 26087f7d..cea09cf4 100644 --- a/sign-as-action/nodejs/src/verifyActionSignature.ts +++ b/sign-as-action/nodejs/src/verifyActionSignature.ts @@ -3,7 +3,7 @@ import { LitNodeClient } from "@lit-protocol/lit-node-client"; import { LIT_RPC, LIT_NETWORK } from "@lit-protocol/constants"; import { getEnv, getSessionSigs } from "./utils"; -import { verifyActionSignatureLitAction } from "./verifyActionSignatureLitAction"; +import { verifyActionSignatureLitAction } from "./litActions/verifyActionSignature"; const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY"); diff --git a/sign-as-action/nodejs/test/index.spec.ts b/sign-as-action/nodejs/test/index.spec.ts index 2fa65911..ccbd5eb6 100644 --- a/sign-as-action/nodejs/test/index.spec.ts +++ b/sign-as-action/nodejs/test/index.spec.ts @@ -3,16 +3,21 @@ import chaiJsonSchema from "chai-json-schema"; import * as ethers from "ethers"; import IpfsHash from "ipfs-only-hash"; -import { signAsAction, verifyActionSignature, signAsActionLitAction } from "../src"; +import { signAsAction, verifyActionSignature, signAsActionLitAction, deriveLitActionWalletAddress } from "../src"; use(chaiJsonSchema); describe("Testing signAsAction and verifyActionSignature", () => { + let signAsActionLitActionIpfsCid: string; + let message: Uint8Array; + let messageHash: string; let litActionSignatureString: string; + let litActionSignatureObject: { r: string, s: string, v: number }; - it("should sign and verify an action signature", async () => { - const signAsActionLitActionIpfsCid = await IpfsHash.of(signAsActionLitAction); - const message = new Uint8Array( + it("should sign a message with signAsAction", async () => { + signAsActionLitActionIpfsCid = await IpfsHash.of(signAsActionLitAction); + + message = new Uint8Array( await crypto.subtle.digest( "SHA-256", new TextEncoder().encode("Hello world") @@ -37,7 +42,7 @@ describe("Testing signAsAction and verifyActionSignature", () => { litActionSignatureString = result!.response as string; - const litActionSignatureObject = JSON.parse(litActionSignatureString); + litActionSignatureObject = JSON.parse(litActionSignatureString); expect(litActionSignatureObject).to.be.jsonSchema( { type: "object", @@ -49,7 +54,9 @@ describe("Testing signAsAction and verifyActionSignature", () => { required: ["r", "s", "v"], } ); + }).timeout(120_000); + it("should sign and verify an action signature", async () => { const response = await verifyActionSignature({ actionIpfsCid: signAsActionLitActionIpfsCid, toSign: message, @@ -57,4 +64,26 @@ describe("Testing signAsAction and verifyActionSignature", () => { }); expect(response?.response).to.be.equal('true'); }).timeout(120_000); + + it("should calculate the ETH address of the Lit Action wallet and verify the signature", async () => { + const response = await deriveLitActionWalletAddress({ litActionIpfsCid: signAsActionLitActionIpfsCid }); + + const combinedSignature = ethers.utils.joinSignature({ + recoveryParam: litActionSignatureObject.v, + r: `0x${litActionSignatureObject.r.substring(2)}`, + s: `0x${litActionSignatureObject.s}`, + }); + + const recoveredPubkey = ethers.utils.recoverPublicKey( + message, + combinedSignature + ); + const recoveredAddress = ethers.utils.recoverAddress( + message, + combinedSignature + ); + + expect(recoveredPubkey).to.be.equal(response.derivedPubkey); + expect(recoveredAddress).to.be.equal(response.derivedAddress); + }).timeout(120_000); }); diff --git a/yarn.lock b/yarn.lock index b8e6f319..6e014718 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2841,6 +2841,15 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/accs-schemas@npm:^0.0.36": + version: 0.0.36 + resolution: "@lit-protocol/accs-schemas@npm:0.0.36" + dependencies: + ajv: "npm:^8.12.0" + checksum: 10c0/97d3ac6c6ebacba3dff020cb38d3ce631ae8b17a67f2ec941239b51ead02cfba9a9344967119dde8c14c69bc9d8392f543c38a086940bed7460f545eabc30e49 + languageName: node + linkType: hard + "@lit-protocol/auth-browser@npm:7.0.4, @lit-protocol/auth-browser@npm:^7.0.4": version: 7.0.4 resolution: "@lit-protocol/auth-browser@npm:7.0.4" @@ -2913,6 +2922,23 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/constants@npm:7.3.1": + version: 7.3.1 + resolution: "@lit-protocol/constants@npm:7.3.1" + dependencies: + "@ethersproject/abstract-provider": "npm:5.7.0" + "@lit-protocol/accs-schemas": "npm:^0.0.36" + "@lit-protocol/contracts": "npm:^0.0.74" + "@lit-protocol/types": "npm:7.3.1" + "@openagenda/verror": "npm:^3.1.4" + depd: "npm:^2.0.0" + ethers: "npm:^5.7.1" + siwe: "npm:^2.3.2" + tslib: "npm:1.14.1" + checksum: 10c0/fdc69994caee4d06aa8775aac47d792ff7aa8f68175745f22f3ef647c1e8c979af30f4f164dadd5373e9feb1571e88e71606e0d45d6cbc379e161b85d7c8ce83 + languageName: node + linkType: hard + "@lit-protocol/contracts-sdk@npm:7.0.4, @lit-protocol/contracts-sdk@npm:^7.0.4": version: 7.0.4 resolution: "@lit-protocol/contracts-sdk@npm:7.0.4" @@ -2941,6 +2967,34 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/contracts-sdk@npm:^7.3.1": + version: 7.3.1 + resolution: "@lit-protocol/contracts-sdk@npm:7.3.1" + dependencies: + "@ethersproject/abi": "npm:5.7.0" + "@ethersproject/abstract-provider": "npm:5.7.0" + "@ethersproject/contracts": "npm:5.7.0" + "@ethersproject/providers": "npm:5.7.2" + "@lit-protocol/accs-schemas": "npm:^0.0.36" + "@lit-protocol/constants": "npm:7.3.1" + "@lit-protocol/contracts": "npm:^0.0.74" + "@lit-protocol/logger": "npm:7.3.1" + "@lit-protocol/misc": "npm:7.3.1" + "@lit-protocol/types": "npm:7.3.1" + "@openagenda/verror": "npm:^3.1.4" + ajv: "npm:^8.12.0" + bech32: "npm:^2.0.0" + depd: "npm:^2.0.0" + ethers: "npm:^5.7.1" + jose: "npm:^4.14.4" + process: "npm:0.11.10" + siwe: "npm:^2.3.2" + tslib: "npm:1.14.1" + util: "npm:0.12.5" + checksum: 10c0/69721e42e5335102145a01e41e0a963719a98f5ba4c45616161d110d17ab72e912a83051ec340a3d60ce8627ec85ba4d28cd5e6a93e73e690a56f478856c0a38 + languageName: node + linkType: hard + "@lit-protocol/contracts@npm:^0.0.74": version: 0.0.74 resolution: "@lit-protocol/contracts@npm:0.0.74" @@ -3207,6 +3261,24 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/logger@npm:7.3.1": + version: 7.3.1 + resolution: "@lit-protocol/logger@npm:7.3.1" + dependencies: + "@ethersproject/abstract-provider": "npm:5.7.0" + "@lit-protocol/accs-schemas": "npm:^0.0.36" + "@lit-protocol/constants": "npm:7.3.1" + "@lit-protocol/contracts": "npm:^0.0.74" + "@lit-protocol/types": "npm:7.3.1" + "@openagenda/verror": "npm:^3.1.4" + depd: "npm:^2.0.0" + ethers: "npm:^5.7.1" + siwe: "npm:^2.3.2" + tslib: "npm:1.14.1" + checksum: 10c0/502e8f50dede9b4c99ece1b2a5e50a43d3aacbdaf239ca5c469914176e3ff4ab676d36a374b48b3d1527afb21d49820000bf76c541ccc3e39bcac57f124bc1fa + languageName: node + linkType: hard + "@lit-protocol/misc-browser@npm:7.0.4": version: 7.0.4 resolution: "@lit-protocol/misc-browser@npm:7.0.4" @@ -3250,6 +3322,30 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/misc@npm:7.3.1": + version: 7.3.1 + resolution: "@lit-protocol/misc@npm:7.3.1" + dependencies: + "@ethersproject/abstract-provider": "npm:5.7.0" + "@ethersproject/contracts": "npm:5.7.0" + "@ethersproject/providers": "npm:5.7.2" + "@lit-protocol/accs-schemas": "npm:^0.0.36" + "@lit-protocol/constants": "npm:7.3.1" + "@lit-protocol/contracts": "npm:^0.0.74" + "@lit-protocol/logger": "npm:7.3.1" + "@lit-protocol/types": "npm:7.3.1" + "@openagenda/verror": "npm:^3.1.4" + ajv: "npm:^8.12.0" + bech32: "npm:^2.0.0" + depd: "npm:^2.0.0" + ethers: "npm:^5.7.1" + siwe: "npm:^2.3.2" + tslib: "npm:1.14.1" + util: "npm:0.12.5" + checksum: 10c0/9987d80d5bd3eab311f2d65d0a7d7b7da7b0d767d25bfcbde58bc208c2cc872b834157065f374ff2198db37a2d4bd160408bea635902d963a73a5e022fc7f922 + languageName: node + linkType: hard + "@lit-protocol/nacl@npm:7.0.4": version: 7.0.4 resolution: "@lit-protocol/nacl@npm:7.0.4" @@ -3273,6 +3369,20 @@ __metadata: languageName: node linkType: hard +"@lit-protocol/types@npm:7.3.1": + version: 7.3.1 + resolution: "@lit-protocol/types@npm:7.3.1" + dependencies: + "@ethersproject/abstract-provider": "npm:5.7.0" + "@lit-protocol/accs-schemas": "npm:^0.0.36" + depd: "npm:^2.0.0" + ethers: "npm:^5.7.1" + siwe: "npm:^2.3.2" + tslib: "npm:1.14.1" + checksum: 10c0/f707023534a7af480e88f5cc8fd5da91534a17805e4ec153880abf9257e6b656a8ae3d99f83ffead7dbfaf9f1f2203aa01a0880899c3d3463d8f75d9eba38089 + languageName: node + linkType: hard + "@lit-protocol/uint8arrays@npm:7.0.4": version: 7.0.4 resolution: "@lit-protocol/uint8arrays@npm:7.0.4" @@ -19499,7 +19609,7 @@ __metadata: "@dotenvx/dotenvx": "npm:^0.44.1" "@lit-protocol/auth-helpers": "npm:^7.0.4" "@lit-protocol/constants": "npm:^7.0.4" - "@lit-protocol/contracts-sdk": "npm:^7.0.4" + "@lit-protocol/contracts-sdk": "npm:^7.3.1" "@lit-protocol/lit-node-client": "npm:^7.0.4" "@types/chai": "npm:^4.3.16" "@types/chai-json-schema": "npm:^1.4.10" From 4dea50c612efeb10508b297098c25d025ab3d24b Mon Sep 17 00:00:00 2001 From: Wyatt Barnes Date: Thu, 23 Oct 2025 16:56:16 -1000 Subject: [PATCH 2/2] fix: create env for sign-as-la test --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 52620c62..4288da86 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -129,6 +129,10 @@ jobs: echo "ETHEREUM_PRIVATE_KEY=${{ secrets.ETHEREUM_PRIVATE_KEY }}" > $GITHUB_WORKSPACE/solana-openai/nodejs/.env echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> $GITHUB_WORKSPACE/solana-openai/nodejs/.env + - name: Create .env file for sign-as-action/nodejs tests + run: | + echo "ETHEREUM_PRIVATE_KEY=${{ secrets.ETHEREUM_PRIVATE_KEY }}" > $GITHUB_WORKSPACE/sign-as-action/nodejs/.env + - name: Run Tests in Node.js run: yarn test:node continue-on-error: false