diff --git a/packages/pkp-base/src/lib/pkp-base.ts b/packages/pkp-base/src/lib/pkp-base.ts index 3750f8129..ec4714ba1 100644 --- a/packages/pkp-base/src/lib/pkp-base.ts +++ b/packages/pkp-base/src/lib/pkp-base.ts @@ -13,6 +13,9 @@ import { PKPBaseProp, JsonAuthSig, PKPBaseDefaultParams, + GetSessionSigsProps, + SessionSigs, + RPCUrls, } from '@lit-protocol/types'; import { LitNodeClient } from '@lit-protocol/lit-node-client'; import { publicKeyConvert } from 'secp256k1'; @@ -23,7 +26,7 @@ import { publicKeyConvert } from 'secp256k1'; * @returns {string} - The compressed public key. */ const compressPubKey = (pubKey: string): string => { - let testBuffer = Buffer.from(pubKey, 'hex'); + const testBuffer = Buffer.from(pubKey, 'hex'); if (testBuffer.length === 64) { pubKey = '04' + pubKey; } @@ -40,20 +43,26 @@ const compressPubKey = (pubKey: string): string => { * A base class that can be shared between Ethers and Cosmos signers. */ export class PKPBase { - pkpWalletProp: PKPBaseProp; + rpcs?: RPCUrls; + controllerAuthSig?: JsonAuthSig; + controllerSessionSigs?: SessionSigs; + sessionSigsExpiration?: string; + uncompressedPubKey!: string; uncompressedPubKeyBuffer!: Uint8Array; compressedPubKey!: string; compressedPubKeyBuffer!: Uint8Array; + litNodeClient!: LitNodeClient; litNodeClientReady: boolean = false; litActionCode?: string; litActionIPFS?: string; litActionJsParams!: T; debug: boolean; - defaultLitActionCode: string = ` + + readonly defaultLitActionCode: string = ` (async () => { - const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName }); + const sigShare = await LitActions.signEcdsa({ toSign, publicKey, sigName }); })();`; // -- debug things @@ -73,11 +82,14 @@ export class PKPBase { prop.pkpPubKey = prop.pkpPubKey.slice(2); } - this.log('prop.pkpPubKey', prop.pkpPubKey); - - this.pkpWalletProp = prop; this.setUncompressPubKeyAndBuffer(prop); this.setCompressedPubKeyAndBuffer(prop); + + this.rpcs = prop.rpcs; + this.controllerAuthSig = prop.controllerAuthSig; + this.controllerSessionSigs = prop.controllerSessionSigs; + this.sessionSigsExpiration = prop.sessionSigsExpiration; + this.debug = prop.debug || false; this.setLitAction(prop); this.setLitActionJsParams(prop.litActionJsParams || {}); @@ -144,16 +156,38 @@ export class PKPBase { } /** - A function that sets the value of the litActionJsParams property to the given params object. - @template CustomType - A generic type that extends T, where T is the type of the litActionJsParams property. - @param { CustomType } params - An object of type CustomType that contains the parameters to be set as litActionJsParams. - @returns { void } - @memberOf SomeClass - */ + * A function that sets the value of the litActionJsParams property to the given params object. + * @template CustomType - A generic type that extends T, where T is the type of the litActionJsParams property. + * @param { CustomType } params - An object of type CustomType that contains the parameters to be set as litActionJsParams. + * @returns { void } + * @memberOf SomeClass + */ setLitActionJsParams(params: CustomType): void { this.litActionJsParams = params; } + /** + * Creates and sets the session sigs and their expiration. + * + * @param {GetSessionSigsProps} sessionParams - The parameters for generating session sigs. + */ + async createAndSetSessionSigs( + sessionParams: GetSessionSigsProps + ): Promise { + try { + const expiration = + sessionParams.expiration || this.litNodeClient.getExpiration(); + const sessionSigs = await this.litNodeClient.getSessionSigs( + sessionParams + ); + + this.controllerSessionSigs = sessionSigs; + this.sessionSigsExpiration = expiration; + } catch (e) { + return this.throwError('Failed to create and set session sigs'); + } + } + /** * Initializes the PKPBase instance by connecting to the LIT node. */ @@ -180,35 +214,32 @@ export class PKPBase { async runLitAction(toSign: Uint8Array, sigName: string): Promise { // If no PKP public key is provided, throw error - if (!this.pkpWalletProp.pkpPubKey) { - throw new Error('pkpPubKey is required'); + if (!this.uncompressedPubKey) { + throw new Error('pkpPubKey (aka. uncompressPubKey) is required'); } // If no authSig or sessionSigs are provided, throw error - if ( - !this.pkpWalletProp.controllerAuthSig && - !this.pkpWalletProp.controllerSessionSigs - ) { + if (!this.controllerAuthSig && !this.controllerSessionSigs) { throw new Error('controllerAuthSig or controllerSessionSigs is required'); } // If session sigs are provided, they must be an object if ( - this.pkpWalletProp.controllerSessionSigs && - typeof this.pkpWalletProp.controllerSessionSigs !== 'object' + this.controllerSessionSigs && + typeof this.controllerSessionSigs !== 'object' ) { throw new Error('controllerSessionSigs must be an object'); } // If authSig is not provided but sessionSigs are, use the first sessionSig as authSig. In executeJs, the sessionSigs will take priority. - let authSig = this.pkpWalletProp.controllerAuthSig; + let authSig = this.controllerAuthSig; if ( !authSig && - this.pkpWalletProp.controllerSessionSigs && - Object.values(this.pkpWalletProp.controllerSessionSigs).length > 0 + this.controllerSessionSigs && + Object.values(this.controllerSessionSigs).length > 0 ) { authSig = Object.values( - this.pkpWalletProp.controllerSessionSigs + this.controllerSessionSigs )[0] as unknown as JsonAuthSig; } @@ -220,11 +251,11 @@ export class PKPBase { ...(this.litActionCode && { code: this.litActionCode }), ...(this.litActionIPFS && { ipfsId: this.litActionIPFS }), authSig: authSig, - sessionSigs: this.pkpWalletProp.controllerSessionSigs, + sessionSigs: this.controllerSessionSigs, jsParams: { ...{ toSign, - publicKey: this.pkpWalletProp.pkpPubKey, + publicKey: this.uncompressedPubKey, sigName, }, ...{ @@ -243,7 +274,7 @@ export class PKPBase { try { const res = await this.litNodeClient.executeJs(executeJsArgs); - let sig = res.signatures[sigName]; + const sig = res.signatures[sigName]; this.log('res:', res); this.log('res.signatures[sigName]:', sig); diff --git a/packages/pkp-cosmos/src/lib/pkp-cosmos.ts b/packages/pkp-cosmos/src/lib/pkp-cosmos.ts index 9caae4002..ca1c65a0f 100644 --- a/packages/pkp-cosmos/src/lib/pkp-cosmos.ts +++ b/packages/pkp-cosmos/src/lib/pkp-cosmos.ts @@ -67,12 +67,15 @@ export class PKPCosmosWallet // 2. Use a constant or configuration for the default RPC URL this.rpc = prop.rpc ?? DEFAULT_COSMOS_RPC_URL; } + getRpc = () => { return this.rpc; }; + setRpc = async (rpc: string) => { this.rpc = rpc; }; + handleRequest = async (payload: any): Promise => { throw new Error('Method not implemented.'); }; @@ -203,7 +206,7 @@ export class PKPCosmosWallet * @param {string} memo - An optional memo string to be included in the transaction * @param {SignerData} [explicitSignerData] - Optional SignerData to be used during signing (e.g., account number, sequence, and chain ID) * - * @returns {Promise<{ + * @returns {Promise<{ * bodyBytes: Uint8Array; * authInfoBytes: Uint8Array; * signatures: readonly Uint8Array[]; diff --git a/packages/pkp-ethers/src/lib/pkp-ethers.ts b/packages/pkp-ethers/src/lib/pkp-ethers.ts index acf7a6e4f..f82cdc4fb 100644 --- a/packages/pkp-ethers/src/lib/pkp-ethers.ts +++ b/packages/pkp-ethers/src/lib/pkp-ethers.ts @@ -105,9 +105,8 @@ export class PKPEthersWallet return Promise.resolve(addr); } - connect(): PKPEthersWallet { - // throw new Error("PKPWallet cannot be connected to a provider"); - return new PKPEthersWallet(this.pkpWalletProp); + connect(): never { + throw new Error('Use setRPC to set a new JSON RPC provider'); } async signTransaction(transaction: TransactionRequest): Promise { @@ -118,24 +117,31 @@ export class PKPEthersWallet const addr = await this.getAddress(); this.log('signTransaction => addr:', addr); - if (!transaction['nonce']) { - transaction.nonce = await this.rpcProvider.getTransactionCount(addr); - this.log('signTransaction => nonce:', transaction.nonce); - } + try { + if (!transaction['nonce']) { + transaction.nonce = await this.rpcProvider.getTransactionCount(addr); + this.log('signTransaction => nonce:', transaction.nonce); + } - if (!transaction['chainId']) { - transaction.chainId = (await this.rpcProvider.getNetwork()).chainId; - this.log('signTransaction => chainId:', transaction.chainId); - } + if (!transaction['chainId']) { + transaction.chainId = (await this.rpcProvider.getNetwork()).chainId; + this.log('signTransaction => chainId:', transaction.chainId); + } - if (!transaction['gasPrice']) { - transaction.gasPrice = await this.getGasPrice(); - this.log('signTransaction => gasPrice:', transaction.gasPrice); - } + if (!transaction['gasPrice']) { + transaction.gasPrice = await this.getGasPrice(); + this.log('signTransaction => gasPrice:', transaction.gasPrice); + } - if (!transaction['gasLimit']) { - transaction.gasLimit = await this.rpcProvider.estimateGas(transaction); - this.log('signTransaction => gasLimit:', transaction.gasLimit); + if (!transaction['gasLimit']) { + transaction.gasLimit = await this.rpcProvider.estimateGas(transaction); + this.log('signTransaction => gasLimit:', transaction.gasLimit); + } + } catch (err) { + this.log( + 'signTransaction => unable to populate transaction with details:', + err + ); } return resolveProperties(transaction).then(async (tx) => { @@ -311,46 +317,56 @@ export class PKPEthersWallet ): Promise { return this.rpcProvider.getBalance(this.address, blockTag); } + getTransactionCount( blockTag?: ethers.providers.BlockTag | undefined ): Promise { return this.rpcProvider.getTransactionCount(this.address, blockTag); } + estimateGas( transaction: ethers.utils.Deferrable ): Promise { return this.rpcProvider.estimateGas(transaction); } + call( transaction: ethers.utils.Deferrable, blockTag?: ethers.providers.BlockTag | undefined ): Promise { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } + getChainId(): Promise { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } + getGasPrice(): Promise { return this.rpcProvider.getGasPrice(); } + getFeeData(): Promise { return this.rpcProvider.getFeeData(); } + resolveName(name: string): Promise { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } + checkTransaction( transaction: ethers.utils.Deferrable ): ethers.utils.Deferrable { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } + populateTransaction( transaction: ethers.utils.Deferrable ): Promise { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } + _checkProvider(operation?: string | undefined): void { - return this.throwError(`Not implemented into PKPEthers`); + return this.throwError(`Not available in PKPEthersWallet`); } get mnemonic() { diff --git a/packages/types/src/lib/interfaces.ts b/packages/types/src/lib/interfaces.ts index d3f644e67..27434f2fd 100644 --- a/packages/types/src/lib/interfaces.ts +++ b/packages/types/src/lib/interfaces.ts @@ -757,7 +757,7 @@ export interface SignSessionKeyProp { resources: any; chainId?: number; - + //domain param is required, when calling from environment that doesn't have the 'location' object. i.e. NodeJs server. domain?: string; } @@ -843,13 +843,10 @@ export declare type AuthenticatorAttachment = 'cross-platform' | 'platform'; export interface PKPBaseProp { pkpPubKey: string; rpc?: string; - rpcs?: { - eth?: string; - cosmos?: string; - btc?: string; - }; + rpcs?: RPCUrls; controllerAuthSig?: JsonAuthSig; - controllerSessionSigs?: string; + controllerSessionSigs?: SessionSigs; + sessionSigsExpiration?: string; litNetwork?: any; debug?: boolean; litActionCode?: string; @@ -857,6 +854,12 @@ export interface PKPBaseProp { litActionJsParams?: any; } +export interface RPCUrls { + eth?: string; + cosmos?: string; + btc?: string; +} + export interface PKPEthersWalletProp extends PKPBaseProp {} export interface PKPCosmosWalletProp extends PKPBaseProp { @@ -874,6 +877,30 @@ export interface PKPBaseDefaultParams { sigName: string; } +export interface PKPClientHelpers { + handleRequest: (request: any) => Promise; + setRpc: (rpc: string) => void; + getRpc: () => string; +} + +export interface SessionSigs { + /** + * Map of Lit node urls to session signatures + */ + [key: string]: SessionSig; +} + +export interface SessionSig { + sig: string; + derivedVia: string; + signedMessage: string; + address: string; + algo: string; +} + +/** + * ========== LitAuthClient ========== + */ export interface LitAuthClientOptions { /** * Domain of the app using LitAuthClient @@ -1109,9 +1136,3 @@ export interface DefaultAuthNeededCallbackParams { // TODO: update type litNodeClient: any; } - -export interface PKPClientHelpers { - handleRequest: (request: any) => Promise; - setRpc: (rpc: string) => void; - getRpc: () => string; -}