Skip to content

Commit

Permalink
[wip] update PKPBase props (#75)
Browse files Browse the repository at this point in the history
* feat: add types to 3rd party libs & updated tests

* feat: add jsParams setter to PKP base

* WIP: eth_signTypedData

* chore: update types

* fix: add webpack to react so it loads crypto

* feat: restructure pkp-cosmos

* gitignore update

* wip: update handler

* chore: add note

* fix: handler.mjs

* feat: add .spec.mjs tools

* feat: added some dev styling

* chore: update style

* feat: add ts json handlers

* feat: add sessionSigs helper in PKPBase

* refactor: make rpc optional on PKPBase, check for provider in PKPEthers

* refactor: flatten wallet props, track sigs expiry, handle connect rpc

* fix: remove pkpPubKey

---------

Co-authored-by: Ansonhkg <ansonox@gmail.com>
  • Loading branch information
sarahzdev and Ansonhkg committed Apr 7, 2023
1 parent 42cffba commit c5679ea
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 66 deletions.
87 changes: 59 additions & 28 deletions packages/pkp-base/src/lib/pkp-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
}
Expand All @@ -40,20 +43,26 @@ const compressPubKey = (pubKey: string): string => {
* A base class that can be shared between Ethers and Cosmos signers.
*/
export class PKPBase<T = PKPBaseDefaultParams> {
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
Expand All @@ -73,11 +82,14 @@ export class PKPBase<T = PKPBaseDefaultParams> {
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 || {});
Expand Down Expand Up @@ -144,16 +156,38 @@ export class PKPBase<T = PKPBaseDefaultParams> {
}

/**
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<CustomType extends T = T>(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<void | never> {
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.
*/
Expand All @@ -180,35 +214,32 @@ export class PKPBase<T = PKPBaseDefaultParams> {

async runLitAction(toSign: Uint8Array, sigName: string): Promise<any> {
// 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;
}

Expand All @@ -220,11 +251,11 @@ export class PKPBase<T = PKPBaseDefaultParams> {
...(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,
},
...{
Expand All @@ -243,7 +274,7 @@ export class PKPBase<T = PKPBaseDefaultParams> {
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);
Expand Down
5 changes: 4 additions & 1 deletion packages/pkp-cosmos/src/lib/pkp-cosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<any> => {
throw new Error('Method not implemented.');
};
Expand Down Expand Up @@ -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[];
Expand Down
64 changes: 40 additions & 24 deletions packages/pkp-ethers/src/lib/pkp-ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
Expand All @@ -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) => {
Expand Down Expand Up @@ -311,46 +317,56 @@ export class PKPEthersWallet
): Promise<ethers.BigNumber> {
return this.rpcProvider.getBalance(this.address, blockTag);
}

getTransactionCount(
blockTag?: ethers.providers.BlockTag | undefined
): Promise<number> {
return this.rpcProvider.getTransactionCount(this.address, blockTag);
}

estimateGas(
transaction: ethers.utils.Deferrable<TransactionRequest>
): Promise<ethers.BigNumber> {
return this.rpcProvider.estimateGas(transaction);
}

call(
transaction: ethers.utils.Deferrable<TransactionRequest>,
blockTag?: ethers.providers.BlockTag | undefined
): Promise<string> {
return this.throwError(`Not implemented into PKPEthers`);
return this.throwError(`Not available in PKPEthersWallet`);
}

getChainId(): Promise<number> {
return this.throwError(`Not implemented into PKPEthers`);
return this.throwError(`Not available in PKPEthersWallet`);
}

getGasPrice(): Promise<ethers.BigNumber> {
return this.rpcProvider.getGasPrice();
}

getFeeData(): Promise<ethers.providers.FeeData> {
return this.rpcProvider.getFeeData();
}

resolveName(name: string): Promise<string> {
return this.throwError(`Not implemented into PKPEthers`);
return this.throwError(`Not available in PKPEthersWallet`);
}

checkTransaction(
transaction: ethers.utils.Deferrable<TransactionRequest>
): ethers.utils.Deferrable<TransactionRequest> {
return this.throwError(`Not implemented into PKPEthers`);
return this.throwError(`Not available in PKPEthersWallet`);
}

populateTransaction(
transaction: ethers.utils.Deferrable<TransactionRequest>
): Promise<TransactionRequest> {
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() {
Expand Down
Loading

0 comments on commit c5679ea

Please sign in to comment.