Skip to content

Commit 9e11c77

Browse files
committed
Init working example
1 parent 03f8c10 commit 9e11c77

File tree

8 files changed

+506
-315
lines changed

8 files changed

+506
-315
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
ETHEREUM_PRIVATE_KEY=
2+
CAPACITY_CREDIT_TOKEN_ID=
3+
LIT_NETWORK=

wrapped-keys/eip-712/nodejs/README.md

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,75 @@
1-
## Running the Example
1+
# EIP-712 Signatures using Wrapped Keys
22

3-
1. `yarn`
4-
2. `cp .env.example .env`
5-
- Fill in `ETHEREUM_PRIVATE_KEY` env with an account that has Lit test tokens on Yellowstone
6-
3. `yarn test`
3+
This code example demonstrates how to sign an EIP-712 message using a Wrapped Key.
74

8-
[test/index.spec.ts](./test/index.spec.ts) creates a EIP-712 message, serializes it, and then signs it using the Wrapped Keys SDK. However, when attempting to recover the address from the signed message, the address is not being recovered correctly because the `signTypedData` function is not being used within the Wrapped Key signing Lit Action. Instead `signMessage` is used which adds the following prefix to the message before signing: `0x19Ethereum Signed Message:\n${message.length}${message}`.
5+
## Prerequisites
6+
7+
- An Ethereum private key
8+
- This private key will be used to:
9+
- Mint a Lit Capacity Credit if none was specific in the project's `.env` file
10+
- In order to pay for this, the corresponding Ethereum account must have Lit Test Tokens. If you do not have any, you can get some from [the faucet](https://chronicle-yellowstone-faucet.getlit.dev/)
11+
- Create a Lit Capacity Credit delegation Auth Sig
12+
- This code example uses Node.js and Yarn please have these installed before running the example
13+
14+
## Installation and Setup
15+
16+
1. Clone the repository
17+
2. `cd` into the code example directory: `cd wrapped-keys/eip-712/nodejs`
18+
3. Install the dependencies: `yarn`
19+
4. Create and fill in the `.env` file: `cp .env.example .env`
20+
- `ETHEREUM_PRIVATE_KEY`: **Required** This is the Ethereum private key that will be used to mint a Lit Capacity Credit and create Lit Session Signatures
21+
- `CAPACITY_CREDIT_TOKEN_ID`: **Optional** This is the ID of the Lit Capacity Credit to use for the PKP delegation Auth Sig
22+
- `LIT_NETWORK`: **Optional** This is the Lit Network to use for the Lit Contracts and Lit Node Clients
23+
24+
## Executing the Example
25+
26+
1. Run `yarn test` to execute the examples-authentication.png)
27+
28+
### Expected Output
29+
30+
```
31+
Signing an EIP-712 message using a Wrapped Key
32+
🔄 Generating EIP-712 message...
33+
✅ Generated and serialized EIP-712 message: {"domain":{"name":"Ether Mail","version":"1","chainId":1,"verifyingContract":"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"types":{"Person":[{"name":"name","type":"string"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","message":{"from":{"name":"Alice","wallet":"0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"},"contents":"Hello, Bob!"}}
34+
🔄 Connecting LitNodeClient to Lit network...
35+
✅ Connected LitNodeClient to Lit network
36+
🔄 Connecting LitContracts client to network...
37+
✅ Connected LitContracts client to network
38+
🔄 Minting Capacity Credits NFT...
39+
✅ Minted new Capacity Credit with ID: 25144
40+
🔄 Minting new PKP...
41+
✅ Minted new PKP with public key: 04cf95d05f3e237fef866f7daf169be4b773ced0cbcc2ce260b03b63026fc858ea182dabad3bdb37c49554527b92dbe53b260d3934dfde18e0fac8a03ccb10fe09 and ETH address: 0xC3bb43D840801959890A565bB58A0290fF0FE8Be
42+
🔄 Creating capacityDelegationAuthSig...
43+
✅ Created the capacityDelegationAuthSig
44+
🔄 Getting PKP Session Sigs...
45+
Storage key "lit-session-key" is missing. Not a problem. Contiune...
46+
Storage key "lit-wallet-sig" is missing. Not a problem. Continue...
47+
Unable to store walletSig in local storage. Not a problem. Continue...
48+
✅ Got PKP Session Sigs
49+
🔄 Generating wrapped key...
50+
✅ Generated wrapped key with id: 1133b4d5-2ffb-46c3-951d-0f0d7f4ccde2 and public key: 0x0402e173bf643ae623771b24cdda0ca01bec0409ca01d67f02090961c1394e81e1dd78c80fa3e116b0be032616d907f2775a4e9e1dc4cc08d19cbec0e51b1f83af
51+
🔄 Getting wrapped key metadata...
52+
✅ Got wrapped key metadata: {
53+
"ciphertext": "okHLOUDld4UJD+LaDz++3QVDngfm/mz9HNPDuOlnJHH/iiCPiDT2Adnn6odOuw2igW8g3cclh0PDwoZO97zdsEmCfQrs2UuUEzR844n/S01HmO7exNHAVRglrK9azstkShE+0/KcIWjG3I2x6BCX9OfvJcTDb/LX2okN5HxLDNZMNuNtdBK11GAkfqPoV28AoBpj/oYdnw4C",
54+
"dataToEncryptHash": "76f7ff2ae254f0771089216fe8413a768f189242fa1b406c3805edaf7c64dc13",
55+
"id": "1133b4d5-2ffb-46c3-951d-0f0d7f4ccde2",
56+
"keyType": "K256",
57+
"pkpAddress": "0xC3bb43D840801959890A565bB58A0290fF0FE8Be",
58+
"publicKey": "0x0402e173bf643ae623771b24cdda0ca01bec0409ca01d67f02090961c1394e81e1dd78c80fa3e116b0be032616d907f2775a4e9e1dc4cc08d19cbec0e51b1f83af",
59+
"litNetwork": "datil-test"
60+
}
61+
🔄 Signing EIP-712 message with Wrapped Key...
62+
✅ Signed EIP-712 message
63+
```
64+
65+
## Specific Files to Reference
66+
67+
- [index.ts](./src/index.ts): Contains the code for:
68+
- Minting a PKP
69+
- Generating PKP Session Signatures
70+
- Generating a Wrapped Key
71+
- Getting the Wrapped Key metadata
72+
- Making the request to execute the Lit Action that signs an EIP-712 message using the Wrapped Key
73+
- [utils.ts](./src/utils.ts): Contains utility functions for the example
74+
- [wrappedKeyLitAction.ts](./src/wrappedKeyLitAction.ts): Contains the Lit Action code that signs an EIP-712 message
75+
- [index.spec.ts](./test/index.spec.ts): Contains the example EIP-712 message and test the example

wrapped-keys/eip-712/nodejs/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
},
1010
"dependencies": {
1111
"@dotenvx/dotenvx": "^0.44.1",
12-
"@lit-protocol/constants": "^6.8.1",
13-
"@lit-protocol/contracts-sdk": "^6.8.1",
14-
"@lit-protocol/lit-auth-client": "^6.8.1",
15-
"@lit-protocol/lit-node-client": "^6.8.1",
16-
"@lit-protocol/types": "^6.8.1",
17-
"@lit-protocol/wrapped-keys": "^6.8.1",
12+
"@lit-protocol/constants": "^6.9.0",
13+
"@lit-protocol/contracts-sdk": "^6.9.0",
14+
"@lit-protocol/lit-auth-client": "^6.9.0",
15+
"@lit-protocol/lit-node-client": "^6.9.0",
16+
"@lit-protocol/types": "^6.9.0",
17+
"@lit-protocol/wrapped-keys": "^6.9.0",
1818
"ethers": "v5"
1919
},
2020
"devDependencies": {

wrapped-keys/eip-712/nodejs/src/index.ts

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { LIT_RPC, LitNetwork } from "@lit-protocol/constants";
22
import { LitNodeClient } from "@lit-protocol/lit-node-client";
33
import { LitAbility } from "@lit-protocol/types";
4-
import { api } from "@lit-protocol/wrapped-keys";
54
import { ethers } from "ethers";
6-
7-
const { signMessageWithEncryptedKey } = api;
5+
import { api } from "@lit-protocol/wrapped-keys";
6+
import { LitActionResource } from "@lit-protocol/auth-helpers";
87

98
import {
109
generateWrappedKey,
@@ -13,10 +12,13 @@ import {
1312
getEnv,
1413
getLitContracts,
1514
getLitNodeClient,
15+
getPkpAccessControlCondition,
1616
getSessionSigsViaPkp,
1717
mintPkp,
1818
} from "./utils";
19-
import { LitActionResource } from "@lit-protocol/auth-helpers";
19+
import { litActionCode } from "./wrappedKeyLitAction";
20+
21+
const { getEncryptedKey } = api;
2022

2123
const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");
2224
const CAPACITY_CREDIT_TOKEN_ID =
@@ -76,19 +78,41 @@ export const runExample = async (serializedEip712Message: string) => {
7678
"This is a test memo"
7779
);
7880

79-
console.log("🔄 Signing EIP-712 message with Wrapped Key...");
80-
const signedMessage = await signMessageWithEncryptedKey({
81-
litNodeClient,
81+
console.log("🔄 Getting wrapped key metadata...");
82+
const wrappedKeyMetadata = await getEncryptedKey({
8283
pkpSessionSigs,
83-
network: "evm",
84+
litNodeClient,
8485
id: wrappedKeyInfo.id,
85-
messageToSign: serializedEip712Message,
86+
});
87+
console.log(
88+
`✅ Got wrapped key metadata: ${JSON.stringify(
89+
wrappedKeyMetadata,
90+
null,
91+
2
92+
)}`
93+
);
94+
95+
console.log("🔄 Signing EIP-712 message with Wrapped Key...");
96+
const response = await litNodeClient.executeJs({
97+
sessionSigs: pkpSessionSigs,
98+
code: litActionCode,
99+
jsParams: {
100+
accessControlConditions: [
101+
getPkpAccessControlCondition(wrappedKeyInfo.pkpAddress),
102+
],
103+
ciphertext: wrappedKeyMetadata.ciphertext,
104+
dataToEncryptHash: wrappedKeyMetadata.dataToEncryptHash,
105+
messageToSign: serializedEip712Message,
106+
useEip712Signing: true,
107+
},
86108
});
87109
console.log("✅ Signed EIP-712 message");
88110

89111
return {
90-
signedMessage,
91-
wrappedKeyEthAddress: wrappedKeyInfo.pkpAddress,
112+
signedMessage: response.response as string,
113+
wrappedKeyEthAddress: ethers.utils.computeAddress(
114+
wrappedKeyMetadata.publicKey
115+
),
92116
};
93117
} catch (error) {
94118
console.error(error);

wrapped-keys/eip-712/nodejs/src/utils.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "@lit-protocol/auth-helpers";
1111
import { EthWalletProvider } from "@lit-protocol/lit-auth-client";
1212
import { api } from "@lit-protocol/wrapped-keys";
13-
import { SessionSigsMap } from "@lit-protocol/types";
13+
import { AccsDefaultParams, SessionSigsMap } from "@lit-protocol/types";
1414

1515
const { generatePrivateKey } = api;
1616

@@ -143,3 +143,25 @@ export const generateWrappedKey = async (
143143

144144
return wrappedKeyInfo;
145145
};
146+
147+
export function getPkpAccessControlCondition(
148+
pkpAddress: string
149+
): AccsDefaultParams {
150+
if (!ethers.utils.isAddress(pkpAddress)) {
151+
throw new Error(
152+
`pkpAddress is not a valid Ethereum Address: ${pkpAddress}`
153+
);
154+
}
155+
156+
return {
157+
contractAddress: "",
158+
standardContractType: "",
159+
chain: "ethereum",
160+
method: "",
161+
parameters: [":userAddress"],
162+
returnValueTest: {
163+
comparator: "=",
164+
value: pkpAddress,
165+
},
166+
};
167+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Signs a message with the Ethers wallet which is also decrypted inside the Lit Action.
3+
*
4+
* @jsParam pkpAddress - The Eth address of the PKP which is associated with the Wrapped Key
5+
* @jsParam ciphertext - For the encrypted Wrapped Key
6+
* @jsParam dataToEncryptHash - For the encrypted Wrapped Key
7+
* @jsParam messageToSign - The unsigned message to be signed by the Wrapped Key
8+
* @jsParam accessControlConditions - The access control condition that allows only the pkpAddress to decrypt the Wrapped Key
9+
* @jsParam useEip712Signing - Whether to use EIP-712 or EIP-191 for signing messageToSign
10+
*
11+
* @returns { Promise<string> } - Returns a message signed by the Ethers Wrapped key. Or returns errors if any.
12+
*/
13+
export const litActionCode = `
14+
const LIT_PREFIX = "lit_";
15+
16+
async function getDecryptedKeyToSingleNode({
17+
accessControlConditions,
18+
ciphertext,
19+
dataToEncryptHash,
20+
}) {
21+
try {
22+
// May be undefined, since we're using \`decryptToSingleNode\`
23+
return await Lit.Actions.decryptToSingleNode({
24+
accessControlConditions,
25+
ciphertext,
26+
dataToEncryptHash,
27+
chain: "ethereum",
28+
authSig: null,
29+
});
30+
} catch (err) {
31+
throw new Error(\`When decrypting key to a single node - \${err.message}\`);
32+
}
33+
}
34+
35+
function removeSaltFromDecryptedKey(decryptedPrivateKey) {
36+
if (!decryptedPrivateKey.startsWith(LIT_PREFIX)) {
37+
throw new Error(
38+
\`PKey was not encrypted with salt; all wrapped keys must be prefixed with '\${LIT_PREFIX}'\`
39+
);
40+
}
41+
42+
return decryptedPrivateKey.slice(LIT_PREFIX.length);
43+
}
44+
45+
async function signMessageEthereumKey({ privateKey, messageToSign }) {
46+
const wallet = new ethers.Wallet(privateKey);
47+
48+
let signature;
49+
if (useEip712Signing ?? false) {
50+
const { domain, types, message } = JSON.parse(messageToSign);
51+
52+
signature = await wallet._signTypedData(domain, types, message);
53+
} else {
54+
signature = await wallet.signMessage(messageToSign);
55+
}
56+
57+
return signature;
58+
}
59+
60+
(async () => {
61+
try {
62+
const decryptedPrivateKey = await getDecryptedKeyToSingleNode({
63+
accessControlConditions,
64+
ciphertext,
65+
dataToEncryptHash,
66+
});
67+
68+
if (!decryptedPrivateKey) {
69+
// Silently exit on nodes which didn't run the \`decryptToSingleNode\` code
70+
return;
71+
}
72+
73+
const privateKey = removeSaltFromDecryptedKey(decryptedPrivateKey);
74+
75+
let signature = await signMessageEthereumKey({
76+
privateKey,
77+
messageToSign,
78+
});
79+
80+
Lit.Actions.setResponse({ response: signature });
81+
} catch (err) {
82+
Lit.Actions.setResponse({ response: \`Error: \${err.message}\` });
83+
}
84+
})();
85+
`;

wrapped-keys/eip-712/nodejs/test/index.spec.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,12 @@ use(chaiJsonSchema);
88
describe("Signing an EIP-712 message using a Wrapped Key", () => {
99
it("should return signed EIP-712 message", async () => {
1010
console.log("🔄 Generating EIP-712 message...");
11-
// Define the domain
1211
const domain = {
1312
name: "Ether Mail",
1413
version: "1",
1514
chainId: 1,
1615
verifyingContract: "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
1716
};
18-
19-
// Define the types
2017
const types = {
2118
Person: [
2219
{ name: "name", type: "string" },
@@ -28,9 +25,7 @@ describe("Signing an EIP-712 message using a Wrapped Key", () => {
2825
{ name: "contents", type: "string" },
2926
],
3027
};
31-
32-
// Define the value
33-
const value = {
28+
const message = {
3429
from: {
3530
name: "Alice",
3631
wallet: "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
@@ -42,24 +37,27 @@ describe("Signing an EIP-712 message using a Wrapped Key", () => {
4237
contents: "Hello, Bob!",
4338
};
4439

45-
const serializedEip712Message = JSON.stringify(
46-
ethers.utils._TypedDataEncoder.getPayload(domain, types, value)
47-
);
40+
const typedData = {
41+
domain,
42+
types,
43+
primaryType: "Mail",
44+
message,
45+
};
46+
const serializedEip712Message = JSON.stringify(typedData);
4847
console.log(
4948
`✅ Generated and serialized EIP-712 message: ${serializedEip712Message}`
5049
);
5150

5251
const { signedMessage, wrappedKeyEthAddress } = (await runExample(
5352
serializedEip712Message
5453
)) ?? { signedMessage: "", wrappedKeyEthAddress: "" };
55-
5654
expect(signedMessage).to.not.equal("");
5755
expect(wrappedKeyEthAddress).to.not.equal("");
5856

5957
const recoveredAddress = ethers.utils.verifyTypedData(
6058
domain,
6159
types,
62-
value,
60+
message,
6361
signedMessage
6462
);
6563
expect(recoveredAddress).to.equal(wrappedKeyEthAddress);

0 commit comments

Comments
 (0)