Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f028c97

Browse files
committedApr 11, 2025
feat: add sign eth tx guide implementation
1 parent 7ef9120 commit f028c97

File tree

7 files changed

+34039
-3094
lines changed

7 files changed

+34039
-3094
lines changed
 

‎deno.lock

Lines changed: 381 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎exampleLitActions/src/signEthTx.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { LitNodeClient } from '@lit-protocol/lit-node-client';
2+
import { LIT_RPC, LIT_ABILITY } from "@lit-protocol/constants";
3+
import {
4+
createSiweMessage,
5+
generateAuthSig,
6+
LitActionResource,
7+
} from "@lit-protocol/auth-helpers";
8+
import * as ethers from "ethers";
9+
10+
// Utility function to get environment variables
11+
const getEnv = (name: string): string => {
12+
const value = process.env[name];
13+
if (!value) {
14+
console.warn(`Environment variable ${name} is not set`);
15+
}
16+
return value || "";
17+
};
18+
19+
// Environment variables
20+
const ETHEREUM_PRIVATE_KEY = getEnv("ETHEREUM_PRIVATE_KEY");
21+
22+
// Define the Lit Action code for conditional signing
23+
const litActionCode = `
24+
async () => {
25+
const signature = await Lit.Actions.signAndCombineEcdsa({
26+
toSign,
27+
publicKey,
28+
sigName,
29+
});
30+
31+
const jsonSignature = JSON.parse(signature);
32+
jsonSignature.r = "0x" + jsonSignature.r.substring(2);
33+
jsonSignature.s = "0x" + jsonSignature.s;
34+
const hexSignature = ethers.utils.joinSignature(jsonSignature);
35+
36+
const signedTx = ethers.utils.serializeTransaction(
37+
unsignedTransaction,
38+
hexSignature
39+
);
40+
41+
const recoveredAddress = ethers.utils.recoverAddress(toSign, hexSignature);
42+
console.log("Recovered Address:", recoveredAddress);
43+
44+
const response = await Lit.Actions.runOnce(
45+
{ waitForResponse: true, name: "txnSender" },
46+
async () => {
47+
try {
48+
const rpcUrl = await Lit.Actions.getRpcUrl({ chain });
49+
const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
50+
const transactionReceipt = await provider.sendTransaction(signedTx);
51+
52+
return \`Transaction Sent Successfully. Transaction Hash: \${transactionReceipt.hash}\`;
53+
} catch (error) {
54+
return \`Error: When sending transaction: \${error.message}\`;
55+
}
56+
}
57+
);
58+
59+
Lit.Actions.setResponse({ response });
60+
};
61+
`;
62+
63+
// Define the ethTxResult type with success and error properties
64+
interface ethTxResult {
65+
response: any;
66+
success: boolean;
67+
error?: any;
68+
}
69+
70+
/**
71+
* Execute eth transaction inside a Lit Action
72+
* @returns Result of the transaction
73+
*/
74+
export const signEthTx = async (): Promise<ethTxResult> => {
75+
try {
76+
// Create ethers wallet for signing
77+
const ethersWallet = new ethers.Wallet(
78+
ETHEREUM_PRIVATE_KEY,
79+
new ethers.providers.JsonRpcProvider(LIT_RPC.CHRONICLE_YELLOWSTONE)
80+
);
81+
82+
const message = new Uint8Array(
83+
await crypto.subtle.digest('SHA-256', new TextEncoder().encode('Hello world'))
84+
);
85+
86+
const litNodeClient = new LitNodeClient({
87+
alertWhenUnauthorized: false,
88+
litNetwork: "datil-dev",
89+
debug: false,
90+
});
91+
92+
await litNodeClient.connect();
93+
94+
const sessionSigs = await litNodeClient.getSessionSigs({
95+
chain: "ethereum",
96+
expiration: new Date(Date.now() + 1000 * 60 * 10).toISOString(), // 10 minutes
97+
resourceAbilityRequests: [
98+
{
99+
resource: new LitActionResource("*"),
100+
ability: LIT_ABILITY.LitActionExecution,
101+
},
102+
],
103+
authNeededCallback: async ({
104+
uri,
105+
expiration,
106+
resourceAbilityRequests,
107+
}) => {
108+
const toSign = await createSiweMessage({
109+
uri,
110+
expiration,
111+
resources: resourceAbilityRequests,
112+
walletAddress: await ethersWallet.getAddress(),
113+
nonce: await litNodeClient.getLatestBlockhash(),
114+
litNodeClient,
115+
});
116+
return await generateAuthSig({
117+
signer: ethersWallet,
118+
toSign,
119+
});
120+
},
121+
});
122+
123+
const signatures = await litNodeClient.executeJs({
124+
code: litActionCode,
125+
sessionSigs,
126+
// all jsParams can be used anywhere in your litActionCode
127+
jsParams: {
128+
toSign: message,
129+
publicKey:
130+
"0x02e5896d70c1bc4b4b4844458748fe0f936c7919d7968341e391fb6d82c258192e64",
131+
sigName: "sig1",
132+
// Add missing parameters that the Lit Action needs
133+
unsignedTransaction: {
134+
// You should populate this with a real transaction object
135+
to: "0x0000000000000000000000000000000000000000",
136+
value: ethers.utils.parseEther("0"),
137+
nonce: 0,
138+
gasLimit: 21000,
139+
gasPrice: ethers.utils.parseUnits("50", "gwei"),
140+
data: "0x",
141+
chainId: 84532, // Base Sepolia testnet
142+
},
143+
chain: "ethereum",
144+
},
145+
});
146+
147+
console.log("signatures: ", signatures);
148+
149+
// Disconnect from the Lit Node network
150+
await litNodeClient.disconnect();
151+
152+
return {
153+
response: signatures,
154+
success: true
155+
};
156+
} catch (error) {
157+
console.error("Error in signEthTx:", error);
158+
return {
159+
response: null,
160+
success: false,
161+
error
162+
};
163+
}
164+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { expect } from 'chai';
2+
import { signEthTx } from '../src/signEthTx';
3+
4+
describe('Lit Action ETH transaction test', function() {
5+
// Increase timeout for Lit network operations
6+
this.timeout(60000);
7+
8+
it('should execute an eth transaction inside a Lit Action', async function() {
9+
const result = await signEthTx();
10+
11+
// Check if the operation was successful
12+
expect(result.success, 'Transaction should execute successfully').to.be.true;
13+
14+
// Verify we have a response object
15+
expect(result.response, 'Response should exist').to.not.be.null;
16+
expect(result.response, 'Response should be an object').to.be.an('object');
17+
18+
// If we have a success response, error should be undefined
19+
if (result.success) {
20+
expect(result.error, 'Error should be undefined on success').to.be.undefined;
21+
} else {
22+
// In case of failure, log the error for debugging
23+
console.error('Transaction failed with error:', result.error);
24+
}
25+
26+
// Check the response structure based on actual output
27+
if (result.success && result.response) {
28+
expect(result.response, 'Should have success property').to.have.property('success');
29+
expect(result.response, 'Should have signedData property').to.have.property('signedData');
30+
expect(result.response, 'Should have decryptedData property').to.have.property('decryptedData');
31+
}
32+
});
33+
});

‎lerna.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"payment-delegation-db/**/*",
1212
"programmable-key-pairs/**/*",
1313
"session-signatures/**/*",
14-
"wrapped-keys/**/*"
14+
"wrapped-keys/**/*",
15+
"exampleLitActions"
1516
]
1617
}

‎package-lock.json

Lines changed: 30047 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"payment-delegation-db/**/*",
1111
"programmable-key-pairs/**/*",
1212
"session-signatures/**/*",
13-
"wrapped-keys/**/*"
13+
"wrapped-keys/**/*",
14+
"exampleLitActions"
1415
],
1516
"devDependencies": {
1617
"@eslint/js": "^9.8.0",
@@ -45,5 +46,9 @@
4546
},
4647
"scripts": {
4748
"test:all": "lerna run test --stream"
49+
},
50+
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
51+
"dependencies": {
52+
"ethers": "^5.8.0"
4853
}
4954
}

‎yarn.lock

Lines changed: 3406 additions & 3092 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.