Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lit-action-derived-wallets/nodejs/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ETHEREUM_PRIVATE_KEY=
4 changes: 4 additions & 0 deletions lit-action-derived-wallets/nodejs/.mocharc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"$schema": "https://json.schemastore.org/mocharc.json",
"require": "tsx"
}
30 changes: 30 additions & 0 deletions lit-action-derived-wallets/nodejs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Lit Action Fetch Example

This code demonstrates executing a Lit Action to make a `fetch` request to an API, and use the response as a conditions for providing a PKP signature.

## Running the Examples

### Install the Dependencies

In this directory, `lit-action-using-fetch/nodejs`, run `yarn` to install the project dependencies.

### Setting Up the `.env` File

Make a copy of the provided `.env.example` file and name it `.env`:

```
cp .env.example .env
```

Within the `.env` there is the ENV:

- `ETHEREUM_PRIVATE_KEY` - **Required**
- Must have Lit test tokens on the Chronicle Yellowstone blockchain
- [Faucet for Chronicle Yellowstone](https://chronicle-yellowstone-faucet.getlit.dev/)
- Will be used to mint PKPs and pay for Lit usage

### Running the Test

After the `.env` is configured, there is a NPM scripts in the `package.json` to run the test suite:

- `test` Runs the test
29 changes: 29 additions & 0 deletions lit-action-derived-wallets/nodejs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "lit-action-using-fetch-nodejs",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"type": "module",
"scripts": {
"test": "npx @dotenvx/dotenvx run -- mocha test/**/*.spec.ts"
},
"dependencies": {
"@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/lit-node-client": "^7.0.4",
"ethers": "5.7.2"
},
"devDependencies": {
"@types/chai": "^4.3.16",
"@types/chai-json-schema": "^1.4.10",
"@types/mocha": "^10.0.6",
"chai": "4.5.0",
"chai-json-schema": "^1.5.1",
"mocha": "^10.4.0",
"tsc": "^2.0.4",
"tsx": "^4.12.0",
"typescript": "^5.4.5"
}
}
12 changes: 12 additions & 0 deletions lit-action-derived-wallets/nodejs/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "lit-action-using-fetch-nodejs",
"root": "lit-action-using-fetch/nodejs",
"targets": {
"test-lit": {
"executor": "nx:run-commands",
"options": {
"command": "cd lit-action-using-fetch/nodejs && yarn && yarn test"
}
}
}
}
134 changes: 134 additions & 0 deletions lit-action-derived-wallets/nodejs/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import ethers from "ethers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LIT_RPC, LIT_NETWORK, LIT_ABILITY } from "@lit-protocol/constants";
import { LitContracts } from "@lit-protocol/contracts-sdk";
import {
createSiweMessageWithRecaps,
generateAuthSig,
LitActionResource,
LitPKPResource,
} from "@lit-protocol/auth-helpers";

import { getEnv } from "./utils";
import { litActionCode } from "./litAction";

const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");

export const runExample = async () => {
let litNodeClient: LitNodeClient;

try {
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();
console.log("✅ Connected to Lit Contracts");

console.log("🔄 Connecting to Lit network...");
litNodeClient = new LitNodeClient({
litNetwork: LIT_NETWORK.DatilDev,
debug: false,
});
await litNodeClient.connect();
console.log("✅ Connected to Lit network");

console.log("🔄 Getting Session Signatures...");
const sessionSigs = await litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours
resourceAbilityRequests: [
{
resource: new LitPKPResource("*"),
ability: LIT_ABILITY.PKPSigning,
},
{
resource: new LitActionResource("*"),
ability: LIT_ABILITY.LitActionExecution,
},
],
authNeededCallback: async ({
resourceAbilityRequests,
expiration,
uri,
}) => {
const toSign = await createSiweMessageWithRecaps({
uri: uri!,
expiration: expiration!,
resources: resourceAbilityRequests!,
walletAddress: ethersSigner.address,
nonce: await litNodeClient.getLatestBlockhash(),
litNodeClient,
});

return await generateAuthSig({
signer: ethersSigner,
toSign,
});
},
});
console.log("✅ Got Session Signatures");

console.log("🔄 Executing Lit Action...");
const toSign = ethers.utils.arrayify(
ethers.utils.keccak256(
ethers.utils.toUtf8Bytes("hello this is something you can sign")
)
);
const litActionResponse = await litNodeClient.executeJs({
sessionSigs,
code: litActionCode,
jsParams: {
toSign,
},
});
console.log("✅ Executed Lit Action");
console.log(litActionResponse);
const litActionDerivedPubkey =
"0x" + JSON.parse(litActionResponse.response as string).pubkey;
const ipfsCid = JSON.parse(litActionResponse.response as string).ipfsCid;
console.log(`✅ Lit Action derived public key: ${litActionDerivedPubkey}`);
console.log(`✅ IPFS CID: ${ipfsCid}`);

// now, calculate the derived action wallet address locally and check that it's the same
const derivedKeyId = ethers.utils.keccak256(
ethers.utils.toUtf8Bytes("lit_action_" + ipfsCid)
);
const derivedPubkey =
await contractClient.pubkeyRouterContract.read.getDerivedPubkey(
contractClient.stakingContract.read.address,
derivedKeyId
);
console.log(`✅ Derived public key locally: ${derivedPubkey}`);

// recover the pubkey from the signature. in the test harness, we check that this matches the locally derived pubkey
const signature = JSON.parse(
litActionResponse.response as string
).signature;
console.log(`✅ Signature: ${JSON.stringify(signature)}`);

const combinedSignature = ethers.utils.joinSignature({
recoveryParam: signature.v,
r: `0x${signature.r.substring(2)}`,
s: `0x${signature.s}`,
});
console.log(`✅ Combined signature: ${combinedSignature}`);
const recoveredPubkey = ethers.utils.recoverPublicKey(
toSign,
combinedSignature
);
console.log(`✅ Recovered pubkey: ${recoveredPubkey}`);

return {
localDerivedPubkey: derivedPubkey,
litActionDerivedPubkey: litActionDerivedPubkey,
recoveredPubkey: recoveredPubkey,
};
} catch (error) {
console.error(error);
} finally {
litNodeClient!.disconnect();
}
};
24 changes: 24 additions & 0 deletions lit-action-derived-wallets/nodejs/src/litAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @ts-nocheck

const _litActionCode = async () => {
const pubkey = await Lit.Actions.getActionPublicKey({
actionIpfsCid: Lit.Auth.actionIpfsIds[0],
signingScheme: "EcdsaK256Sha256",
});

const signature = await Lit.Actions.signAsAction({
toSign, // supplied as a JSParam to the Lit Action
sigName: "aSignature",
signingScheme: "EcdsaK256Sha256",
});

Lit.Actions.setResponse({
response: JSON.stringify({
pubkey,
ipfsCid: Lit.Auth.actionIpfsIds[0],
signature: JSON.parse(signature),
}),
});
};

export const litActionCode = `(${_litActionCode.toString()})();`;
12 changes: 12 additions & 0 deletions lit-action-derived-wallets/nodejs/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as ethers from "ethers";
import { LitContracts } from "@lit-protocol/contracts-sdk";
import { LIT_NETWORK } from "@lit-protocol/constants";

export const getEnv = (name: string): string => {
const env = process.env[name];
if (env === undefined || env === "")
throw new Error(
`${name} ENV is not defined, please define it in the .env file`
);
return env;
};
24 changes: 24 additions & 0 deletions lit-action-derived-wallets/nodejs/test/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect, use } from "chai";
import chaiJsonSchema from "chai-json-schema";

import { runExample } from "../src";

use(chaiJsonSchema);

describe("Testing specific functionality", () => {
before(async function () {
this.timeout(120_000);
});

it("should test for a specific thing", async () => {
const result = await runExample();
const localDerivedPubkey = result?.localDerivedPubkey;
const litActionDerivedPubkey = result?.litActionDerivedPubkey;
const recoveredPubkey = result?.recoveredPubkey;
expect(localDerivedPubkey).to.equal(litActionDerivedPubkey);
expect(localDerivedPubkey).to.equal(recoveredPubkey);
expect(localDerivedPubkey).to.not.be.undefined;
expect(litActionDerivedPubkey).to.not.be.undefined;
expect(recoveredPubkey).to.not.be.undefined;
}).timeout(120_000);
});
Loading
Loading