Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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 conditional-signing/browser/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PKP_PUBLIC_KEY=
4 changes: 4 additions & 0 deletions conditional-signing/browser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.env
.cache
dist
node_modules
97 changes: 97 additions & 0 deletions conditional-signing/browser/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Conditional Signing Using the Lit SDK in the Browser

This code example demonstrates how to execute a Lit Action that only provides a Lit network signature (using a PKP) if a provided Ethereum address (provided via a [SIWE](https://eips.ethereum.org/EIPS/eip-4361) message) holds 1 or more Wei on Ethereum mainnet.

## Running this Example

### Install the Dependencies

In this directory, `conditional-signing/browser`, run `yarn` to install the project dependencies.

### Specifying Your Lit PKP's Public Key

If you already have a Lit PKP that you'd like to use, you can copy the contents of the provided `.env.example` to a `.env` file to specify it. If you don't have a PKP, or wish to use a new one for this example, then you can skip this step and one will be created for you when you run this example.

**NOTE** In order for a new Lit PKP to minted for you, you **must** have `testLPX` tokens. You can receive some `testLPX` using the faucet available [here](https://faucet.litprotocol.com/).

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

Your `.env` file should look like:

```
PKP_PUBLIC_KEY=yourPublicKey
```

### Starting the Example

In this directory, `conditional-signing/browser`, run `yarn start` to bundle all of this code and server the HTML file at: [http://localhost:1234](http://localhost:1234).

After starting and waiting for Parcel to serve the website, you should see a page with a single button that says: `Click Me`.

Before you click the button, open up the JavaScript console in your browser so you can see the output of this example.

After clicking the button a couple things will happen:

1. You will be prompted by your wallet (i.e. MetaMask) to connect an account to the site
2. After connecting an account, you will see the following output in the JavaScript console:
```
Clicked
Connected account: 0xA89543a7145C68E52a4D584f1ceb123605131211
Connecting litNodeClient to network...
litNodeClient connected!
Getting Session Signatures...
```
3. You will then be prompted by your wallet to sign a message, this message will be used to authenticate you with the Lit network
4. After signing that message, you will be prompted by your wallet to sign another message. This message is the SIWE message the Lit Action will derive an address from to use for the conditional check of whether or not to sign a message with your PKP. The Lit Action will **only** sign a message with your PKP if the derived address has 1 or more Wei on Ethereum Mainnet
- After signing this message, you should see in the JavaScript console something similar to:
```json
Got Auth Sig for Lit Action conditional check!
{
"sig": "0x6607f337861bf1f2ce331ee3a6ef7b5c08e31fb124e2139c48c6f0223258727306a1b38dbd9fa696f6ecc8a9dca82cca61e23882c0787bbf07dc0ba7bbb1ef081c",
"derivedVia": "web3.eth.personal.sign",
"signedMessage": "localhost wants you to sign in with your Ethereum account:\n0xA89543a7145C68E52a4D584f1ceb123605131211\n\nThis is a test statement. You can put anything you want here.\n\nURI: http://localhost\nVersion: 1\nChain ID: 1\nNonce: 0x40e2d121e4513c45063212f65f139b300625a91e3900a76f56478ffd815bef21\nIssued At: 2024-05-03T23:43:48.500Z\nExpiration Time: 2024-05-04T23:43:45.894Z",
"address": "0xA89543a7145C68E52a4D584f1ceb123605131211"
}
```
5. After signing that message, depending on whether or not you specified a PKP Public Key in the `.env` file:
a. If you did, you'll be on the next step
b. If you didn't, you will be prompted by your wallet to sign a transaction to mint a new PKP on the Lit Chronicle rollup. This transaction will cost `testLPX` (which is only a testnet token with **no** real-world value). After signing this transaction you should see in the JavaScript console something similar to:
```json
Minted PKP!
{
"tokenId": "0x59cb949a00d46ccd9deceb9912f935871deea7711951433254135242f53153fd",
"publicKey": "0400f74bb0ece6c5c4f9577f358a77790a7b790eef48e3367bd05a3cc648504fb1efc99b840549809b85d66c5f55503f9ef92eeee5e3eb30ee06976d7b5fbc3c90",
"ethAddress": "0x5c2e895EDAdce9fB019D497238013bF7527d6690"
}
```
6. Finally, if the Ethereum address you signed the SIWE message with has a balance equal to or greater than 1 Wei on Ethereum Mainnet, you should see in the JavaScript console something similar to:
```json
litActionSignatures:
{
"claims": {},
"signatures": {
"sig": {
"r": "b276fc11cbb133eeb25e0d5ff5c759514cfb21f15d0248cdfafc0440fa148d2a",
"s": "006e3b9ece94f8531b5baa4b428a8c6155335e61a5639980d20c9d7477f6746f",
"recid": 1,
"signature": "0xb276fc11cbb133eeb25e0d5ff5c759514cfb21f15d0248cdfafc0440fa148d2a006e3b9ece94f8531b5baa4b428a8c6155335e61a5639980d20c9d7477f6746f1c",
"publicKey": "041E7A220A697F47491525798337BFAAC6073C6094FDDE9187D749D28D947F59FE73FBAE024FC0B87D2A61068EA8087E94ECC843820752295307537F9D06432876",
"dataSigned": "7D87C5EA75F7378BB701E404C50639161AF3EFF66293E9F375B5F17EB50476F4"
}
},
"logs": ""
}
```
If the address does not have 1 or more Wei, you should see an empty signature with the custom error response set within the Lit Action:
```json
litActionSignatures:
{
"success": true,
"signedData": {},
"decryptedData": {},
"claimData": {},
"response": "address does not have 1 or more Wei on Ethereum Mainnet"
}
```
23 changes: 23 additions & 0 deletions conditional-signing/browser/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "generating-session-sigs-in-browser",
"version": "0.1.0",
"description": "Example of generating a Lit Session Signature within the browser",
"source": "src/index.html",
"license": "MIT",
"scripts": {
"start": "parcel ./src/index.html"
},
"dependencies": {
"@dotenvx/dotenvx": "^0.37.1",
"@lit-protocol/auth-browser": "^6.0.0-alpha.4",
"@lit-protocol/auth-helpers": "^6.0.0-alpha.4",
"@lit-protocol/constants": "^6.0.0-alpha.4",
"@lit-protocol/contracts-sdk": "^6.0.0-alpha.4",
"@lit-protocol/lit-node-client": "^6.0.0-alpha.4",
"ethers": "5.7.2"
},
"devDependencies": {
"parcel-bundler": "^1.12.5",
"tslib": "^2.6.2"
}
}
12 changes: 12 additions & 0 deletions conditional-signing/browser/src/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lit Session Signature Example</title>
</head>
<body>
<button id="myButton">Click Me</button>
<script src="./index.js"></script>
</body>
</html>
158 changes: 158 additions & 0 deletions conditional-signing/browser/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { LitNetwork } from "@lit-protocol/constants";
import {
createSiweMessageWithRecaps,
generateAuthSig,
LitAbility,
LitAccessControlConditionResource,
LitActionResource,
LitPKPResource,
} from "@lit-protocol/auth-helpers";
import { disconnectWeb3 } from "@lit-protocol/auth-browser";
import { LitContracts } from "@lit-protocol/contracts-sdk";
import * as ethers from "ethers";

import { litActionCode } from "./litAction";

document.addEventListener("DOMContentLoaded", () => {
document.getElementById("myButton").addEventListener("click", buttonClick);
});

async function buttonClick() {
try {
console.log("Clicked");

const provider = new ethers.providers.Web3Provider(window.ethereum);
await provider.send("eth_requestAccounts", []);
const ethersSigner = provider.getSigner();
console.log("Connected account:", await ethersSigner.getAddress());

const litNodeClient = await getLitNodeClient();

const sessionSigs = await getSessionSigs(litNodeClient, ethersSigner);
console.log("Got Session Signatures!");

const authSig = await genAuthSig(litNodeClient, ethersSigner);
console.log("Got Auth Sig for Lit Action conditional check!", authSig);

const litActionSignatures = await litNodeClient.executeJs({
sessionSigs,
code: litActionCode,
jsParams: {
conditions: [
{
conditionType: "evmBasic",
contractAddress: "",
standardContractType: "",
chain: "ethereum",
method: "eth_getBalance",
parameters: [":userAddress", "latest"],
returnValueTest: {
comparator: ">=",
value: "1",
},
},
],
authSig,
chain: "ethereum",
dataToSign: ethers.utils.arrayify(
ethers.utils.keccak256([1, 2, 3, 4, 5])
),
publicKey: await getPkpPublicKey(ethersSigner),
},
});
console.log("litActionSignatures: ", litActionSignatures);
} catch (error) {
console.error(error);
} finally {
disconnectWeb3();
}
}

async function getLitNodeClient() {
const litNodeClient = new LitNodeClient({
litNetwork: LitNetwork.Cayenne,
});

console.log("Connecting litNodeClient to network...");
await litNodeClient.connect();

console.log("litNodeClient connected!");
return litNodeClient;
}

async function getPkpPublicKey(ethersSigner) {
if (
process.env.PKP_PUBLIC_KEY !== undefined &&
process.env.PKP_PUBLIC_KEY !== ""
)
return process.env.PKP_PUBLIC_KEY;

const pkp = await mintPkp(ethersSigner);
console.log("Minted PKP!", pkp);
return pkp.publicKey;
}

async function mintPkp(ethersSigner) {
console.log("Minting new PKP...");
const litContracts = new LitContracts({
signer: ethersSigner,
network: LitNetwork.Cayenne,
});

await litContracts.connect();

return (await litContracts.pkpNftContractUtils.write.mint()).pkp;
}

async function getSessionSigs(litNodeClient, ethersSigner) {
console.log("Getting Session Signatures...");
return litNodeClient.getSessionSigs({
chain: "ethereum",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours
resourceAbilityRequests: [
{
resource: new LitPKPResource("*"),
ability: LitAbility.PKPSigning,
},
{
resource: new LitActionResource("*"),
ability: LitAbility.LitActionExecution,
},
],
authNeededCallback: getAuthNeededCallback(litNodeClient, ethersSigner),
});
}

function getAuthNeededCallback(litNodeClient, ethersSigner) {
return async ({ resourceAbilityRequests, expiration, uri }) => {
const toSign = await createSiweMessageWithRecaps({
uri,
expiration,
resources: resourceAbilityRequests,
walletAddress: await ethersSigner.getAddress(),
nonce: await litNodeClient.getLatestBlockhash(),
litNodeClient,
});

return await generateAuthSig({
signer: ethersSigner,
toSign,
});
};
}

async function genAuthSig(litNodeClient, ethersSigner) {
const toSign = await createSiweMessageWithRecaps({
uri: "http://localhost",
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours
walletAddress: await ethersSigner.getAddress(),
nonce: await litNodeClient.getLatestBlockhash(),
litNodeClient: litNodeClient,
});

return await generateAuthSig({
signer: ethersSigner,
toSign,
});
}
21 changes: 21 additions & 0 deletions conditional-signing/browser/src/litAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const litActionCode = `
(async () => {
// test an access control condition
const testResult = await Lit.Actions.checkConditions({
conditions,
authSig,
chain,
});

if (!testResult) {
LitActions.setResponse({ response: "address does not have 1 or more Wei on Ethereum Mainnet" });
return
}

const sigShare = await LitActions.signEcdsa({
toSign: dataToSign,
publicKey,
sigName: "sig",
});
})();
`;
Loading