-
Notifications
You must be signed in to change notification settings - Fork 10
Overhaul #94
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v2-old
Are you sure you want to change the base?
Overhaul #94
Changes from all commits
c64251b
08a4c6f
45d8412
8af7d10
9a52c69
ff0b804
103020e
d2874ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"name": "@lit-dev/example-lit-actions", | ||
"version": "1.0.0", | ||
"description": "Example Lit Actions test", | ||
"scripts": { | ||
"test": "dotenvx run --env-file=../.env -- mocha -r ts-node/register test/**/*.spec.ts" | ||
}, | ||
"dependencies": { | ||
"@lit-protocol/lit-node-client": "^7.0.0", | ||
"@lit-protocol/lit-node-client-nodejs": "^7.0.0", | ||
"@lit-protocol/constants": "^7.0.0", | ||
"@lit-protocol/auth-helpers": "^7.0.0", | ||
"ethers": "^5.7.2" | ||
}, | ||
"devDependencies": { | ||
"@types/mocha": "^10.0.10", | ||
"@types/chai": "^5.2.1", | ||
"chai": "^5.2.0", | ||
"mocha": "^11.1.0", | ||
"ts-node": "^10.9.2" | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"name": "example-lit-actions", | ||
"$schema": "../node_modules/nx/schemas/project-schema.json", | ||
"projectType": "library", | ||
"sourceRoot": "exampleLitActions/src", | ||
"targets": { | ||
"test": { | ||
"executor": "nx:run-commands", | ||
"options": { | ||
"command": "dotenvx run -- mocha -r ts-node/register test/**/*.spec.ts", | ||
"cwd": "exampleLitActions" | ||
} | ||
} | ||
}, | ||
"tags": [] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
import { LitNodeClient } from '@lit-protocol/lit-node-client'; | ||
import { LIT_RPC, LIT_NETWORK, LIT_ABILITY } from "@lit-protocol/constants"; | ||
import { | ||
createSiweMessage, | ||
generateAuthSig, | ||
LitActionResource, | ||
} from "@lit-protocol/auth-helpers"; | ||
import { LitContracts } from "@lit-protocol/contracts-sdk"; | ||
import * as ethers from "ethers"; | ||
|
||
// Utility function to get environment variables | ||
const getEnv = (name: string): string => { | ||
const value = process.env[name]; | ||
if (!value) { | ||
console.warn(`Environment variable ${name} is not set`); | ||
} | ||
return value || ""; | ||
}; | ||
Comment on lines
+11
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should always define util methods in a |
||
|
||
// Environment variables | ||
const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY"); | ||
const CHAIN_TO_CHECK_CONDITION_ON = getEnv("CHAIN_TO_CHECK_CONDITION_ON") || "ethereum"; | ||
const LIT_PKP_PUBLIC_KEY = process.env["LIT_PKP_PUBLIC_KEY"]; | ||
const LIT_CAPACITY_CREDIT_TOKEN_ID = process.env["LIT_CAPACITY_CREDIT_TOKEN_ID"]; | ||
|
||
// Define the Lit Action code for conditional signing | ||
const litActionCode = ` | ||
async () => { | ||
try { | ||
// 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" }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This should use the |
||
return; | ||
} | ||
|
||
const sigShare = await LitActions.signEcdsa({ | ||
toSign: dataToSign, | ||
publicKey, | ||
sigName: "sig", | ||
}); | ||
} catch (error) { | ||
LitActions.setResponse({ response: error.message }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of |
||
} | ||
}; | ||
`; | ||
Comment on lines
+26
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
const _litActionCode = async () => {
// Lit Action code here...
};
export const litActionCode = `(${_litActionCode.toString()})();`; This gives us syntax highlighting for the Lit Action code |
||
|
||
// Result interface for our function | ||
interface ConditionalSigningResult { | ||
success: boolean; | ||
signatureResult?: Record<string, any>; | ||
response?: string; | ||
error?: string; | ||
} | ||
Comment on lines
+53
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't return a custom type from the example functions, we should just return the raw Lit SDK response and then handle the parsing inside the test. We don't want to clutter the code example with code that's not technically required - it can become confusing for the user copy/pasting the example |
||
|
||
/** | ||
* Execute conditional signing with Lit Protocol | ||
* @returns Result of the conditional signing operation | ||
*/ | ||
export const conditionalSigning = async (): Promise<ConditionalSigningResult> => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Each code example should just be named |
||
let litNodeClient: LitNodeClient; | ||
|
||
try { | ||
// Create ethers wallet for signing | ||
const ethersWallet = new ethers.Wallet( | ||
ETHEREUM_PRIVATE_KEY, | ||
new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE) | ||
); | ||
|
||
console.log("🔄 Connecting to the Lit network..."); | ||
litNodeClient = new LitNodeClient({ | ||
litNetwork: LIT_NETWORK.DatilTest, | ||
debug: false, | ||
Comment on lines
+77
to
+78
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For all code examples, can we make these ENVs? It makes it easier to test across the different Lit network when we want to debug something happening on a specific Lit network |
||
}); | ||
await litNodeClient.connect(); | ||
console.log("✅ Connected to the Lit network"); | ||
|
||
// Initialize Lit Contracts client | ||
console.log("🔄 Connecting LitContracts client to network..."); | ||
const litContracts = new LitContracts({ | ||
signer: ethersWallet, | ||
network: LIT_NETWORK.DatilTest, | ||
debug: false, | ||
Comment on lines
+87
to
+88
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ENVs for these as well |
||
}); | ||
await litContracts.connect(); | ||
console.log("✅ Connected LitContracts client to network"); | ||
|
||
// Handle PKP (Programmable Key Pair) | ||
let pkpPublicKey = LIT_PKP_PUBLIC_KEY; | ||
if (!pkpPublicKey) { | ||
console.log("🔄 PKP wasn't provided, minting a new one..."); | ||
const pkpInfo = (await litContracts.pkpNftContractUtils.write.mint()).pkp; | ||
pkpPublicKey = pkpInfo.publicKey; | ||
console.log("✅ PKP successfully minted"); | ||
console.log(`ℹ️ PKP token ID: ${pkpInfo.tokenId}`); | ||
console.log(`ℹ️ PKP public key: ${pkpPublicKey}`); | ||
console.log(`ℹ️ PKP ETH address: ${pkpInfo.ethAddress}`); | ||
} else { | ||
console.log(`ℹ️ Using provided PKP: ${pkpPublicKey}`); | ||
} | ||
Comment on lines
+94
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So this is the type of functionality we want to abstract into a util function. It's going to be used by many code examples and isn't directly relevant to the code example which is covering how to conditional signing with a PKP. Really the code to highlight for this code example is in the Lit Action, we just need a PKP public key to send along with the Lit Action request |
||
|
||
// Handle Capacity Credit | ||
let capacityTokenId = LIT_CAPACITY_CREDIT_TOKEN_ID; | ||
if (!capacityTokenId) { | ||
console.log("🔄 No Capacity Credit provided, minting a new one..."); | ||
capacityTokenId = ( | ||
await litContracts.mintCapacityCreditsNFT({ | ||
requestsPerKilosecond: 10, | ||
daysUntilUTCMidnightExpiration: 1, | ||
}) | ||
).capacityTokenIdStr; | ||
console.log(`✅ Minted new Capacity Credit with ID: ${capacityTokenId}`); | ||
} else { | ||
console.log( | ||
`ℹ️ Using provided Capacity Credit with ID: ${capacityTokenId}` | ||
); | ||
} | ||
|
||
// Create capacity delegation auth signature | ||
console.log("🔄 Creating capacityDelegationAuthSig..."); | ||
const { capacityDelegationAuthSig } = | ||
await litNodeClient.createCapacityDelegationAuthSig({ | ||
dAppOwnerWallet: ethersWallet, | ||
capacityTokenId, | ||
delegateeAddresses: [ethersWallet.address], | ||
uses: "1", | ||
}); | ||
console.log("✅ Capacity Delegation Auth Sig created"); | ||
Comment on lines
+107
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These can also be abstracted into util functions |
||
|
||
// Get session signatures | ||
const sessionSigs = await litNodeClient.getSessionSigs({ | ||
chain: CHAIN_TO_CHECK_CONDITION_ON, | ||
capabilityAuthSigs: [capacityDelegationAuthSig], | ||
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours | ||
resourceAbilityRequests: [ | ||
{ | ||
resource: new LitActionResource("*"), | ||
ability: LIT_ABILITY.LitActionExecution, | ||
}, | ||
], | ||
authNeededCallback: async ({ resourceAbilityRequests, expiration, uri }) => { | ||
const toSign = await createSiweMessage({ | ||
uri: uri || "", | ||
expiration: expiration || "", | ||
resources: resourceAbilityRequests || [], | ||
walletAddress: await ethersWallet.getAddress(), | ||
nonce: await litNodeClient.getLatestBlockhash(), | ||
litNodeClient: litNodeClient, | ||
}); | ||
|
||
return await generateAuthSig({ | ||
signer: ethersWallet, | ||
toSign, | ||
}); | ||
}, | ||
}); | ||
Comment on lines
+135
to
+161
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you something similar to |
||
|
||
// Create auth signature for the action | ||
const authSig = await (async () => { | ||
const toSign = await createSiweMessage({ | ||
uri: "http://localhost", | ||
expiration: new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(), // 24 hours | ||
walletAddress: await ethersWallet.getAddress(), | ||
nonce: await litNodeClient.getLatestBlockhash(), | ||
resources: [ | ||
{ | ||
resource: new LitActionResource("*"), | ||
ability: LIT_ABILITY.LitActionExecution, | ||
}, | ||
], | ||
litNodeClient: litNodeClient, | ||
}); | ||
return await generateAuthSig({ | ||
signer: ethersWallet, | ||
toSign, | ||
}); | ||
})(); | ||
Comment on lines
+163
to
+182
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another util methods that takes the |
||
|
||
// Data to sign - using a known hash for consistent testing | ||
const dataToSign = ethers.utils.arrayify( | ||
ethers.utils.keccak256([1, 2, 3, 4, 5]) | ||
); | ||
|
||
// Execute the Lit Action | ||
console.log("🔄 Executing Lit Action with conditional signing..."); | ||
const litActionResult = await litNodeClient.executeJs({ | ||
sessionSigs: sessionSigs, | ||
code: litActionCode, | ||
jsParams: { | ||
conditions: [ | ||
{ | ||
conditionType: "evmBasic", | ||
contractAddress: "", | ||
standardContractType: "", | ||
chain: CHAIN_TO_CHECK_CONDITION_ON, | ||
method: "eth_getBalance", | ||
parameters: [":userAddress", "latest"], | ||
returnValueTest: { | ||
comparator: ">=", | ||
value: "0", | ||
}, | ||
}, | ||
], | ||
authSig: authSig, | ||
chain: CHAIN_TO_CHECK_CONDITION_ON, | ||
dataToSign: dataToSign, | ||
publicKey: pkpPublicKey, | ||
}, | ||
}); | ||
|
||
console.log("✅ Lit Action executed successfully"); | ||
console.log(JSON.stringify(litActionResult, null, 2)); | ||
|
||
return { | ||
success: litActionResult.success, | ||
signatureResult: litActionResult.signatures || {}, | ||
response: typeof litActionResult.response === 'string' | ||
? litActionResult.response | ||
: JSON.stringify(litActionResult.response) | ||
}; | ||
Comment on lines
+219
to
+225
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we should just return |
||
} catch (error: any) { | ||
console.error("Error in conditional signing:", error); | ||
return { | ||
success: false, | ||
error: error?.message || String(error) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should also do |
||
}; | ||
} finally { | ||
if (litNodeClient!) { | ||
await litNodeClient.disconnect(); | ||
console.log("✅ Disconnected from Lit network"); | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All code example files should be named
src/index.ts
for consistency across all the code examples. Basically every code example is going to at least have these three files:src/index.ts
,src/utils.ts
, andsrc/litAction.ts
with the latter two being dependent on whether the code example actually needs them