UNPKG

@lit-protocol/vincent-scaffold-sdk

Version:

Shared build configuration and utilities for Vincent abilities and policies

196 lines (168 loc) 5.24 kB
/** * LitActionsSmartSigner for EIP-7702 delegated transactions * * This signer implements the Alchemy SmartAccountSigner interface to support * EIP-7702 delegation with the Lit Protocol PKP. It enables gasless transactions * through integration with Alchemy's gas sponsorship. * * @example * ```typescript * const signer = new LitActionsSmartSigner({ * pkpPublicKey, * chainId, * }); * * // Use with Alchemy's Smart Account Client, and pass as the signer. * ``` */ export interface LitActionsSmartSignerConfig { pkpPublicKey: string; chainId: number; } // Type definitions compatible with Alchemy SDK and viem type Address = `0x${string}`; type Hex = `0x${string}`; // SignableMessage from viem - can be string or object with raw property type SignableMessage = | string | { raw: Hex | Uint8Array; }; // TypedDataDefinition compatible with viem type TypedDataDefinition = { domain?: any; types?: any; primaryType?: string; message?: any; }; // EIP-7702 types type AuthorizationRequest = { address?: Address; contractAddress?: Address; chainId: number; nonce: number; }; type SignedAuthorization = { address: Address; chainId: number; nonce: number; r: Hex; s: Hex; v?: bigint; yParity: number; }; // SmartAccountSigner interface compatible with @account-kit/smart-contracts interface SmartAccountSigner<Inner = any> { signerType: string; inner: Inner; getAddress: () => Promise<Address>; signMessage: (message: SignableMessage) => Promise<Hex>; signTypedData: (params: TypedDataDefinition) => Promise<Hex>; signAuthorization?: ( unsignedAuthorization: AuthorizationRequest ) => Promise<SignedAuthorization>; } export class LitActionsSmartSigner implements SmartAccountSigner { readonly signerType = "lit-actions"; readonly inner: any; private pkpPublicKey: string; private signerAddress: string; constructor(config: LitActionsSmartSignerConfig) { if (config.pkpPublicKey.startsWith("0x")) { config.pkpPublicKey = config.pkpPublicKey.slice(2); } this.pkpPublicKey = config.pkpPublicKey; this.signerAddress = ethers.utils.computeAddress( "0x" + config.pkpPublicKey ); this.inner = { pkpPublicKey: config.pkpPublicKey, chainId: config.chainId, }; // Inner client reference } async getAddress(): Promise<Address> { return this.signerAddress as Address; } async signMessage(message: SignableMessage): Promise<Hex> { let messageToSign: string | Uint8Array; if (typeof message === "string") { messageToSign = message; } else { messageToSign = typeof message.raw === "string" ? ethers.utils.arrayify(message.raw) : message.raw; } const messageHash = ethers.utils.hashMessage(messageToSign); const sig = await Lit.Actions.signAndCombineEcdsa({ toSign: ethers.utils.arrayify(messageHash), publicKey: this.pkpPublicKey, sigName: `alchemyMessage`, }); const parsedSig = JSON.parse(sig); return ethers.utils.joinSignature({ r: "0x" + parsedSig.r.substring(2), s: "0x" + parsedSig.s, v: parsedSig.v, }) as Hex; } async signTypedData(params: TypedDataDefinition): Promise<Hex> { // console.log("signTypedData called with params", params); // Create the EIP-712 hash const hash = ethers.utils._TypedDataEncoder.hash( params.domain || {}, params.types || {}, params.message || {} ); const sig = await Lit.Actions.signAndCombineEcdsa({ toSign: ethers.utils.arrayify(hash), publicKey: this.pkpPublicKey, sigName: `alchemyTypedData`, }); const parsedSig = JSON.parse(sig); return ethers.utils.joinSignature({ r: "0x" + parsedSig.r.substring(2), s: "0x" + parsedSig.s, v: parsedSig.v, }) as Hex; } // reference implementation is from Viem SmartAccountSigner async signAuthorization( unsignedAuthorization: AuthorizationRequest ): Promise<SignedAuthorization> { // console.log("signAuthorization called with params", unsignedAuthorization); const { contractAddress, chainId, nonce } = unsignedAuthorization; if (!contractAddress || !chainId) { throw new Error( "Invalid authorization: contractAddress and chainId are required" ); } const hash = ethers.utils.keccak256( ethers.utils.hexConcat([ "0x05", ethers.utils.RLP.encode([ ethers.utils.hexlify(chainId), contractAddress, nonce ? ethers.utils.hexlify(nonce) : "0x", ]), ]) ); // console.log("hash in signAuthorization", hash); const sig = await Lit.Actions.signAndCombineEcdsa({ toSign: ethers.utils.arrayify(hash), publicKey: this.pkpPublicKey, sigName: `alchemyAuth7702`, }); const sigObj = JSON.parse(sig); // console.log("sigObj in signAuthorization", sigObj); return { address: (unsignedAuthorization.address || contractAddress!) as Address, chainId: chainId, nonce: nonce, r: ("0x" + sigObj.r.substring(2)) as Hex, s: ("0x" + sigObj.s) as Hex, v: BigInt(sigObj.v), yParity: sigObj.v, }; } }