Skip to content

Lit Protocol Integration Guide

LitProtocol is distributed cryptography for signing, encryption, and compute. A generalizable key management network, Lit provides developers with a set of tools for managing sovereign identities on the open Web.

Combining Lit Protocol's pkp wallet with Account Kit allows you to use your Programmable Key Pairs (PKPs) as a smart account for your users.

WARNING

Lit Protocol's pkp network is still in testnet. Backwards compatibility, and data availability will not be guaranteed until mainnet. Do not use PKP wallets to store valuable assets.

Integration

Install the pkp ethers package

bash
npm i @lit-protocol/pkp-ethers@cayenne
npm i @lit-protocol/pkp-ethers@cayenne
bash
yarn add @lit-protocol/pkp-ethers@cayenne
yarn add @lit-protocol/pkp-ethers@cayenne

Install the LitNodeClient

bash
npm i @lit-protocol/lit-node-client@cayenne
npm i @lit-protocol/lit-node-client@cayenne
bash
yarn add @lit-protocol/lit-node-client@cayenne
yarn add @lit-protocol/lit-node-client@cayenne

Creating PKP

See documentation here for creating PKPs

Create a SmartAccountSigner

Next, setup the LitNodeClient and PKPEthersWallet to create a SmartAccountSigner:

ts
import { WalletClientSigner, type SmartAccountSigner } from "@alchemy/aa-core";
import { LitAbility, LitActionResource } from "@lit-protocol/auth-helpers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { PKPEthersWallet } from "@lit-protocol/pkp-ethers";
import { AuthCallbackParams } from "@lit-protocol/types";
import { createWalletClient, custom } from "viem";
import { polygonMumbai } from "viem/chains";

const API_KEY = "<YOUR API KEY>";
const POLYGON_MUMBAI_RPC_URL = `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`;
const PKP_PUBLIC_KEY = "<YOUR PKP PUBLIC KEY>";

const litNodeClient = new LitNodeClient({
  litNetwork: "cayenne",
  debug: false,
});
await litNodeClient.connect();

const resourceAbilities = [
  {
    resource: new LitActionResource("*"),
    ability: LitAbility.PKPSigning,
  },
];

/**
 * For provisioning keys and setting up authentication methods see documentation below
 * https://developer.litprotocol.com/v2/pkp/minting
 */
const authNeededCallback = async (params: AuthCallbackParams) => {
  const response = await litNodeClient.signSessionKey({
    sessionKey: params.sessionKeyPair,
    statement: params.statement,
    authMethods: [],
    pkpPublicKey: PKP_PUBLIC_KEY,
    expiration: params.expiration,
    resources: params.resources,
    chainId: 1,
  });
  return response.authSig;
};

const sessionSigs = await litNodeClient
  .getSessionSigs({
    chain: "ethereum",
    expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
    resourceAbilityRequests: resourceAbilities,
    authNeededCallback,
  })
  .catch((err) => {
    console.log("error while attempting to access session signatures: ", err);
    throw err;
  });

const pkpWallet = new PKPEthersWallet({
  pkpPubKey: PKP_PUBLIC_KEY,
  rpc: POLYGON_MUMBAI_RPC_URL,
  controllerSessionSigs: sessionSigs,
});

// a smart account signer you can use as an owner on ISmartContractAccount
export const litSigner: SmartAccountSigner = new WalletClientSigner(
  createWalletClient({ transport: custom(pkpWallet.rpcProvider) }), // JsonRpcProvider instance,
  "lit" // signerType
);
import { WalletClientSigner, type SmartAccountSigner } from "@alchemy/aa-core";
import { LitAbility, LitActionResource } from "@lit-protocol/auth-helpers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { PKPEthersWallet } from "@lit-protocol/pkp-ethers";
import { AuthCallbackParams } from "@lit-protocol/types";
import { createWalletClient, custom } from "viem";
import { polygonMumbai } from "viem/chains";

const API_KEY = "<YOUR API KEY>";
const POLYGON_MUMBAI_RPC_URL = `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`;
const PKP_PUBLIC_KEY = "<YOUR PKP PUBLIC KEY>";

const litNodeClient = new LitNodeClient({
  litNetwork: "cayenne",
  debug: false,
});
await litNodeClient.connect();

const resourceAbilities = [
  {
    resource: new LitActionResource("*"),
    ability: LitAbility.PKPSigning,
  },
];

/**
 * For provisioning keys and setting up authentication methods see documentation below
 * https://developer.litprotocol.com/v2/pkp/minting
 */
const authNeededCallback = async (params: AuthCallbackParams) => {
  const response = await litNodeClient.signSessionKey({
    sessionKey: params.sessionKeyPair,
    statement: params.statement,
    authMethods: [],
    pkpPublicKey: PKP_PUBLIC_KEY,
    expiration: params.expiration,
    resources: params.resources,
    chainId: 1,
  });
  return response.authSig;
};

const sessionSigs = await litNodeClient
  .getSessionSigs({
    chain: "ethereum",
    expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
    resourceAbilityRequests: resourceAbilities,
    authNeededCallback,
  })
  .catch((err) => {
    console.log("error while attempting to access session signatures: ", err);
    throw err;
  });

const pkpWallet = new PKPEthersWallet({
  pkpPubKey: PKP_PUBLIC_KEY,
  rpc: POLYGON_MUMBAI_RPC_URL,
  controllerSessionSigs: sessionSigs,
});

// a smart account signer you can use as an owner on ISmartContractAccount
export const litSigner: SmartAccountSigner = new WalletClientSigner(
  createWalletClient({ transport: custom(pkpWallet.rpcProvider) }), // JsonRpcProvider instance,
  "lit" // signerType
);

Use it with LightAccount

We can link our SmartAccountSigner to a LightSmartContractAccount from aa-accounts:

ts
import { AlchemyProvider } from "@alchemy/aa-alchemy";
import {
  LightSmartContractAccount,
  getDefaultLightAccountFactoryAddress,
} from "@alchemy/aa-accounts";
import { litSigner } from "./lit";

const chain = sepolia;
const provider = new AlchemyProvider({
  apiKey: "ALCHEMY_API_KEY",
  chain,
}).connect(
  (rpcClient) =>
    new LightSmartContractAccount({
      chain,
      owner: litSigner,
      factoryAddress: getDefaultLightAccountFactoryAddress(chain),
      rpcClient,
    })
);
import { AlchemyProvider } from "@alchemy/aa-alchemy";
import {
  LightSmartContractAccount,
  getDefaultLightAccountFactoryAddress,
} from "@alchemy/aa-accounts";
import { litSigner } from "./lit";

const chain = sepolia;
const provider = new AlchemyProvider({
  apiKey: "ALCHEMY_API_KEY",
  chain,
}).connect(
  (rpcClient) =>
    new LightSmartContractAccount({
      chain,
      owner: litSigner,
      factoryAddress: getDefaultLightAccountFactoryAddress(chain),
      rpcClient,
    })
);
ts
import { WalletClientSigner, type SmartAccountSigner } from "@alchemy/aa-core";
import { LitAbility, LitActionResource } from "@lit-protocol/auth-helpers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { PKPEthersWallet } from "@lit-protocol/pkp-ethers";
import { AuthCallbackParams } from "@lit-protocol/types";
import { createWalletClient, custom } from "viem";
import { polygonMumbai } from "viem/chains";

const API_KEY = "<YOUR API KEY>";
const POLYGON_MUMBAI_RPC_URL = `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`;
const PKP_PUBLIC_KEY = "<YOUR PKP PUBLIC KEY>";

const litNodeClient = new LitNodeClient({
  litNetwork: "cayenne",
  debug: false,
});
await litNodeClient.connect();

const resourceAbilities = [
  {
    resource: new LitActionResource("*"),
    ability: LitAbility.PKPSigning,
  },
];

/**
 * For provisioning keys and setting up authentication methods see documentation below
 * https://developer.litprotocol.com/v2/pkp/minting
 */
const authNeededCallback = async (params: AuthCallbackParams) => {
  const response = await litNodeClient.signSessionKey({
    sessionKey: params.sessionKeyPair,
    statement: params.statement,
    authMethods: [],
    pkpPublicKey: PKP_PUBLIC_KEY,
    expiration: params.expiration,
    resources: params.resources,
    chainId: 1,
  });
  return response.authSig;
};

const sessionSigs = await litNodeClient
  .getSessionSigs({
    chain: "ethereum",
    expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
    resourceAbilityRequests: resourceAbilities,
    authNeededCallback,
  })
  .catch((err) => {
    console.log("error while attempting to access session signatures: ", err);
    throw err;
  });

const pkpWallet = new PKPEthersWallet({
  pkpPubKey: PKP_PUBLIC_KEY,
  rpc: POLYGON_MUMBAI_RPC_URL,
  controllerSessionSigs: sessionSigs,
});

// a smart account signer you can use as an owner on ISmartContractAccount
export const litSigner: SmartAccountSigner = new WalletClientSigner(
  createWalletClient({ transport: custom(pkpWallet.rpcProvider) }), // JsonRpcProvider instance,
  "lit" // signerType
);
import { WalletClientSigner, type SmartAccountSigner } from "@alchemy/aa-core";
import { LitAbility, LitActionResource } from "@lit-protocol/auth-helpers";
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { PKPEthersWallet } from "@lit-protocol/pkp-ethers";
import { AuthCallbackParams } from "@lit-protocol/types";
import { createWalletClient, custom } from "viem";
import { polygonMumbai } from "viem/chains";

const API_KEY = "<YOUR API KEY>";
const POLYGON_MUMBAI_RPC_URL = `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`;
const PKP_PUBLIC_KEY = "<YOUR PKP PUBLIC KEY>";

const litNodeClient = new LitNodeClient({
  litNetwork: "cayenne",
  debug: false,
});
await litNodeClient.connect();

const resourceAbilities = [
  {
    resource: new LitActionResource("*"),
    ability: LitAbility.PKPSigning,
  },
];

/**
 * For provisioning keys and setting up authentication methods see documentation below
 * https://developer.litprotocol.com/v2/pkp/minting
 */
const authNeededCallback = async (params: AuthCallbackParams) => {
  const response = await litNodeClient.signSessionKey({
    sessionKey: params.sessionKeyPair,
    statement: params.statement,
    authMethods: [],
    pkpPublicKey: PKP_PUBLIC_KEY,
    expiration: params.expiration,
    resources: params.resources,
    chainId: 1,
  });
  return response.authSig;
};

const sessionSigs = await litNodeClient
  .getSessionSigs({
    chain: "ethereum",
    expiration: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7).toISOString(),
    resourceAbilityRequests: resourceAbilities,
    authNeededCallback,
  })
  .catch((err) => {
    console.log("error while attempting to access session signatures: ", err);
    throw err;
  });

const pkpWallet = new PKPEthersWallet({
  pkpPubKey: PKP_PUBLIC_KEY,
  rpc: POLYGON_MUMBAI_RPC_URL,
  controllerSessionSigs: sessionSigs,
});

// a smart account signer you can use as an owner on ISmartContractAccount
export const litSigner: SmartAccountSigner = new WalletClientSigner(
  createWalletClient({ transport: custom(pkpWallet.rpcProvider) }), // JsonRpcProvider instance,
  "lit" // signerType
);