diff --git a/.changeset/two-mangos-lead.md b/.changeset/two-mangos-lead.md
new file mode 100644
index 000000000..5956fb2db
--- /dev/null
+++ b/.changeset/two-mangos-lead.md
@@ -0,0 +1,8 @@
+---
+'@lit-protocol/lit-client': minor
+'@lit-protocol/contracts': minor
+'@lit-protocol/networks': minor
+'@lit-protocol/e2e': minor
+---
+
+introduce `litClient.utils.getDerivedKeyId` - a little helper to resolve the Lit Action public key outside of the Action runtime
diff --git a/docs/guides/lit-action-sign-as-action.mdx b/docs/guides/lit-action-sign-as-action.mdx
index ba6bb2ce2..6a919ba6f 100644
--- a/docs/guides/lit-action-sign-as-action.mdx
+++ b/docs/guides/lit-action-sign-as-action.mdx
@@ -69,3 +69,21 @@ const { keccak256, arrayify } = ethers.utils;
```
This approach keeps the derivation entirely within the Lit Action context. Because the public key depends only on the Action CID and signing scheme, you can rely on `Lit.Actions.getActionPublicKey` for a deterministic identity without needing to execute the Action externally first.
+
+## Derive the Same Public Key from Client Code
+
+If you prefer to resolve the Lit Action public key outside of the Action runtime - e.g., inside tests or other tooling—the SDK now exposes a helper that calls the on-chain PubkeyRouter contract.
+
+```ts
+import { createLitClient } from "@lit-protocol/lit-client";
+import { nagaDev } from "@lit-protocol/networks";
+import { keccak256, stringToBytes } from "viem";
+
+const litClient = await createLitClient({ network: nagaDev });
+const derivedKeyId = keccak256(stringToBytes(`lit_action_${actionIpfsCid}`));
+const actionPublicKey = await litClient.utils.getDerivedKeyId(derivedKeyId);
+
+console.log("Derived Lit Action pubkey:", actionPublicKey);
+```
+
+Under the hood, `getDerivedKeyId` routes through `PubkeyRouter.getDerivedPubkey`, passing the staking contract address and the default Naga key set (`naga-keyset1`) (will be dynamic in the future). This mirrors the same key derivation the nodes perform, letting you confirm identities or signatures without re-running the Lit Action.
diff --git a/docs/sdk/sdk-reference/lit-client/functions/createLitClient.mdx b/docs/sdk/sdk-reference/lit-client/functions/createLitClient.mdx
index f62160754..35a3bf5fa 100644
--- a/docs/sdk/sdk-reference/lit-client/functions/createLitClient.mdx
+++ b/docs/sdk/sdk-reference/lit-client/functions/createLitClient.mdx
@@ -1046,6 +1046,33 @@ Create a Viem-compatible account backed by a PKP.
+### utils.getDerivedKeyId
+
+Resolve the uncompressed PKP public key for a given derived key identifier.
+
+#### Parameters
+
+
+ 0x-prefixed bytes32 identifier (for example, keccak256(stringToBytes(`lit_action_${ipfsCid}`))).
+
+
+#### Returns
+
+
+ 0x-prefixed uncompressed public key as returned by the PubkeyRouter contract.
+
+
+#### Example
+
+```ts
+import { keccak256, stringToBytes } from "viem";
+
+const derivedKeyId = keccak256(stringToBytes(`lit_action_${ipfsCid}`));
+const derivedPubkey = await litClient.utils.getDerivedKeyId(derivedKeyId);
+```
+
+> Note: this helper currently calls `PubkeyRouter.getDerivedPubkey` with the default Naga key set (`naga-keyset1`). See the [Derive Lit Action Public Keys guide](../../../../guides/lit-action-sign-as-action) for an end-to-end workflow.
+
### getChainConfig
Returns the chain configuration for the current network.
@@ -1074,4 +1101,4 @@ Stop background state updates and release resources.
none
#### Returns
-void
\ No newline at end of file
+void
diff --git a/packages/contracts/dist/dev/develop.cjs b/packages/contracts/dist/dev/develop.cjs
index 68c964778..a94c15d8b 100644
--- a/packages/contracts/dist/dev/develop.cjs
+++ b/packages/contracts/dist/dev/develop.cjs
@@ -15431,11 +15431,6 @@ module.exports = {
"name": "CallerNotOwner",
"type": "error"
},
- {
- "inputs": [],
- "name": "MustBeLessThan100",
- "type": "error"
- },
{
"inputs": [],
"name": "MustBeNonzero",
diff --git a/packages/contracts/dist/dev/develop.js b/packages/contracts/dist/dev/develop.js
index e4ee8767a..8f5c6d742 100644
--- a/packages/contracts/dist/dev/develop.js
+++ b/packages/contracts/dist/dev/develop.js
@@ -15429,11 +15429,6 @@ export const develop = {
"name": "CallerNotOwner",
"type": "error"
},
- {
- "inputs": [],
- "name": "MustBeLessThan100",
- "type": "error"
- },
{
"inputs": [],
"name": "MustBeNonzero",
diff --git a/packages/contracts/dist/dev/develop.json b/packages/contracts/dist/dev/develop.json
index 3f0ead8e1..edb5c53df 100644
--- a/packages/contracts/dist/dev/develop.json
+++ b/packages/contracts/dist/dev/develop.json
@@ -15429,11 +15429,6 @@
"name": "CallerNotOwner",
"type": "error"
},
- {
- "inputs": [],
- "name": "MustBeLessThan100",
- "type": "error"
- },
{
"inputs": [],
"name": "MustBeNonzero",
diff --git a/packages/contracts/dist/dev/develop.ts b/packages/contracts/dist/dev/develop.ts
index 23cfb5053..a7c4b1936 100644
--- a/packages/contracts/dist/dev/develop.ts
+++ b/packages/contracts/dist/dev/develop.ts
@@ -15429,11 +15429,6 @@ export const develop = {
"name": "CallerNotOwner",
"type": "error"
},
- {
- "inputs": [],
- "name": "MustBeLessThan100",
- "type": "error"
- },
{
"inputs": [],
"name": "MustBeNonzero",
diff --git a/packages/contracts/dist/signatures/datil-dev.cjs b/packages/contracts/dist/signatures/datil-dev.cjs
index ab91df099..e76149ec6 100644
--- a/packages/contracts/dist/signatures/datil-dev.cjs
+++ b/packages/contracts/dist/signatures/datil-dev.cjs
@@ -673,6 +673,30 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil-dev.d.ts b/packages/contracts/dist/signatures/datil-dev.d.ts
index 987c785ad..7869843d0 100644
--- a/packages/contracts/dist/signatures/datil-dev.d.ts
+++ b/packages/contracts/dist/signatures/datil-dev.d.ts
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil-dev.js b/packages/contracts/dist/signatures/datil-dev.js
index 56ce87ed8..26a4baacd 100644
--- a/packages/contracts/dist/signatures/datil-dev.js
+++ b/packages/contracts/dist/signatures/datil-dev.js
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil-test.cjs b/packages/contracts/dist/signatures/datil-test.cjs
index 25d299bbd..44e21a9ef 100644
--- a/packages/contracts/dist/signatures/datil-test.cjs
+++ b/packages/contracts/dist/signatures/datil-test.cjs
@@ -673,6 +673,30 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil-test.d.ts b/packages/contracts/dist/signatures/datil-test.d.ts
index 1b427544a..13ae18825 100644
--- a/packages/contracts/dist/signatures/datil-test.d.ts
+++ b/packages/contracts/dist/signatures/datil-test.d.ts
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil-test.js b/packages/contracts/dist/signatures/datil-test.js
index 850a4ff32..6a878e482 100644
--- a/packages/contracts/dist/signatures/datil-test.js
+++ b/packages/contracts/dist/signatures/datil-test.js
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil.cjs b/packages/contracts/dist/signatures/datil.cjs
index 4699b01b5..0176461b8 100644
--- a/packages/contracts/dist/signatures/datil.cjs
+++ b/packages/contracts/dist/signatures/datil.cjs
@@ -673,6 +673,30 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil.d.ts b/packages/contracts/dist/signatures/datil.d.ts
index 53b19b0eb..8bfe3ba1d 100644
--- a/packages/contracts/dist/signatures/datil.d.ts
+++ b/packages/contracts/dist/signatures/datil.d.ts
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/datil.js b/packages/contracts/dist/signatures/datil.js
index e6e8b27e0..8636f7f00 100644
--- a/packages/contracts/dist/signatures/datil.js
+++ b/packages/contracts/dist/signatures/datil.js
@@ -673,6 +673,30 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/develop.cjs b/packages/contracts/dist/signatures/develop.cjs
index 1fa8fd6d7..c0ae731dd 100644
--- a/packages/contracts/dist/signatures/develop.cjs
+++ b/packages/contracts/dist/signatures/develop.cjs
@@ -1072,6 +1072,35 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/develop.d.ts b/packages/contracts/dist/signatures/develop.d.ts
index 98d219194..d1a353046 100644
--- a/packages/contracts/dist/signatures/develop.d.ts
+++ b/packages/contracts/dist/signatures/develop.d.ts
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/develop.js b/packages/contracts/dist/signatures/develop.js
index 981584fdf..5cbc839ee 100644
--- a/packages/contracts/dist/signatures/develop.js
+++ b/packages/contracts/dist/signatures/develop.js
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-dev.cjs b/packages/contracts/dist/signatures/naga-dev.cjs
index bdd8aa3bc..7a686724e 100644
--- a/packages/contracts/dist/signatures/naga-dev.cjs
+++ b/packages/contracts/dist/signatures/naga-dev.cjs
@@ -1072,6 +1072,35 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-dev.d.ts b/packages/contracts/dist/signatures/naga-dev.d.ts
index aebf82da5..d3fc0190f 100644
--- a/packages/contracts/dist/signatures/naga-dev.d.ts
+++ b/packages/contracts/dist/signatures/naga-dev.d.ts
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-dev.js b/packages/contracts/dist/signatures/naga-dev.js
index 3a0e4bc82..ce8337944 100644
--- a/packages/contracts/dist/signatures/naga-dev.js
+++ b/packages/contracts/dist/signatures/naga-dev.js
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-staging.cjs b/packages/contracts/dist/signatures/naga-staging.cjs
index 2cb8426aa..2aa12ebd5 100644
--- a/packages/contracts/dist/signatures/naga-staging.cjs
+++ b/packages/contracts/dist/signatures/naga-staging.cjs
@@ -1072,6 +1072,35 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-staging.d.ts b/packages/contracts/dist/signatures/naga-staging.d.ts
index 727b54b01..139c06c6c 100644
--- a/packages/contracts/dist/signatures/naga-staging.d.ts
+++ b/packages/contracts/dist/signatures/naga-staging.d.ts
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-staging.js b/packages/contracts/dist/signatures/naga-staging.js
index ad718fabf..ea43e368f 100644
--- a/packages/contracts/dist/signatures/naga-staging.js
+++ b/packages/contracts/dist/signatures/naga-staging.js
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-test.cjs b/packages/contracts/dist/signatures/naga-test.cjs
index ab0c59b32..0af31a7c0 100644
--- a/packages/contracts/dist/signatures/naga-test.cjs
+++ b/packages/contracts/dist/signatures/naga-test.cjs
@@ -1072,6 +1072,35 @@ const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-test.d.ts b/packages/contracts/dist/signatures/naga-test.d.ts
index 1f8c34253..be18c1a33 100644
--- a/packages/contracts/dist/signatures/naga-test.d.ts
+++ b/packages/contracts/dist/signatures/naga-test.d.ts
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/dist/signatures/naga-test.js b/packages/contracts/dist/signatures/naga-test.js
index 83f86b272..302113c9f 100644
--- a/packages/contracts/dist/signatures/naga-test.js
+++ b/packages/contracts/dist/signatures/naga-test.js
@@ -1072,6 +1072,35 @@ export const signatures = {
"stateMutability": "view",
"type": "function"
},
+ "getDerivedPubkey": {
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "stakingContract",
+ "type": "address"
+ },
+ {
+ "internalType": "string",
+ "name": "keySetId",
+ "type": "string"
+ },
+ {
+ "internalType": "bytes32",
+ "name": "derivedKeyId",
+ "type": "bytes32"
+ }
+ ],
+ "name": "getDerivedPubkey",
+ "outputs": [
+ {
+ "internalType": "bytes",
+ "name": "",
+ "type": "bytes"
+ }
+ ],
+ "stateMutability": "view",
+ "type": "function"
+ },
"getEthAddress": {
"inputs": [
{
diff --git a/packages/contracts/src/config/methods.ts b/packages/contracts/src/config/methods.ts
index bf676ba7a..1e2d7f53e 100644
--- a/packages/contracts/src/config/methods.ts
+++ b/packages/contracts/src/config/methods.ts
@@ -40,6 +40,7 @@ export const METHODS_TO_EXTRACT = [
'PubkeyRouter.ethAddressToPkpId',
'PubkeyRouter.getPubkey',
'PubkeyRouter.getEthAddress',
+ 'PubkeyRouter.getDerivedPubkey',
// Ledger:
'Ledger.deposit',
diff --git a/packages/e2e/src/tickets/derived-pubkey.spec.ts b/packages/e2e/src/tickets/derived-pubkey.spec.ts
new file mode 100644
index 000000000..ab6a7e241
--- /dev/null
+++ b/packages/e2e/src/tickets/derived-pubkey.spec.ts
@@ -0,0 +1,18 @@
+import { createLitClient } from '@lit-protocol/lit-client';
+import { nagaTest } from '@lit-protocol/networks';
+import { keccak256, stringToBytes } from 'viem';
+
+const IPFS_CID = 'QmcA9npUnrzsmvx9sfmZDnEnPAEbMA5kp4tnkueYqiJKZv';
+const EXPECTED_DERIVED_PUBKEY =
+ '0x044e8f8e87e6192869a369b774fd9feba4607df95057eb58981626bd108f77d50674e587cc48c8a0a8c69ad650825ee1adf5f31acb4075e9327e625cd880a1dfdb';
+
+describe('Derived Pubkey Ticket', () => {
+ test('should derive pubkey', async () => {
+ const derivedKeyId = keccak256(stringToBytes(`lit_action_${IPFS_CID}`));
+ const litClient = await createLitClient({
+ network: nagaTest,
+ });
+ const ctx = await litClient.utils.getDerivedKeyId(derivedKeyId);
+ expect(ctx).toBe(EXPECTED_DERIVED_PUBKEY);
+ });
+});
diff --git a/packages/lit-client/src/lib/LitClient/createLitClient.ts b/packages/lit-client/src/lib/LitClient/createLitClient.ts
index 089553ade..79bb3c17e 100644
--- a/packages/lit-client/src/lib/LitClient/createLitClient.ts
+++ b/packages/lit-client/src/lib/LitClient/createLitClient.ts
@@ -732,6 +732,37 @@ export const _createNagaLitClient = async (
executeJs: _executeJs,
};
},
+ utils: {
+ getDerivedKeyId: async (derivedKeyId: string) => {
+ const contractManager = _stateManager.contractManager;
+
+ if (!contractManager) {
+ throw new Error(
+ 'Contract manager is not available from state manager'
+ );
+ }
+
+ const pubkeyRouterContract = contractManager.pubkeyRouterContract;
+
+ if (
+ !pubkeyRouterContract?.read?.getDerivedPubkey ||
+ !contractManager.stakingContract
+ ) {
+ throw new Error(
+ 'Required contracts are not available on the contract manager'
+ );
+ }
+
+ // TODO: support configurable key set ids per network when required
+ const DEFAULT_KEY_SET_ID = 'naga-keyset1';
+
+ return pubkeyRouterContract.read.getDerivedPubkey([
+ contractManager.stakingContract.address,
+ DEFAULT_KEY_SET_ID,
+ derivedKeyId,
+ ]);
+ },
+ },
getChainConfig: () => {
const viemConfig = networkModule.getChainConfig();
const rpcUrl = networkModule.getRpcUrl();
diff --git a/packages/networks/src/networks/vNaga/shared/managers/contract-manager/createContractsManager.ts b/packages/networks/src/networks/vNaga/shared/managers/contract-manager/createContractsManager.ts
index c8b7593cd..41c20bd19 100644
--- a/packages/networks/src/networks/vNaga/shared/managers/contract-manager/createContractsManager.ts
+++ b/packages/networks/src/networks/vNaga/shared/managers/contract-manager/createContractsManager.ts
@@ -157,6 +157,7 @@ export const createContractsManager = (
contractData.PubkeyRouter.methods.deriveEthAddressFromPubkey,
contractData.PubkeyRouter.methods.ethAddressToPkpId,
contractData.PubkeyRouter.methods.getEthAddress,
+ contractData.PubkeyRouter.methods.getDerivedPubkey,
contractData.PubkeyRouter.methods.getPubkey,
...contractData.PubkeyRouter.events,
],
diff --git a/packages/networks/src/networks/vNaga/shared/managers/state-manager/createStateManager.ts b/packages/networks/src/networks/vNaga/shared/managers/state-manager/createStateManager.ts
index aeb4fb038..9ab10a449 100644
--- a/packages/networks/src/networks/vNaga/shared/managers/state-manager/createStateManager.ts
+++ b/packages/networks/src/networks/vNaga/shared/managers/state-manager/createStateManager.ts
@@ -235,5 +235,6 @@ export const createStateManager = async (params: {
eventStateManager.stop();
// clearInterval(timer);
},
+ contractManager,
};
};