From 05a4180e2fba9c0f95812c80eb91014fa583bda5 Mon Sep 17 00:00:00 2001 From: Tasos Derisiotis <50984242+Eengineer1@users.noreply.github.com> Date: Wed, 21 Aug 2024 17:08:53 +0300 Subject: [PATCH] Resolved index --- package.json | 18 +- src/agent/ICheqd.ts | 252 ++++++++++++++++++-------- src/did-manager/cheqd-did-provider.ts | 160 +++++++++++++--- src/dkg-threshold/lit-protocol/v2.ts | 3 +- src/dkg-threshold/lit-protocol/v3.ts | 130 ++++++++++++- 5 files changed, 448 insertions(+), 115 deletions(-) diff --git a/package.json b/package.json index 3c29cb74..1a050ee0 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "build:cjs": "echo 'Experimental backwards compatibility! Use v2 major version or `@cjs` npm tag for deep CommonJS exports.' && tsc -p tsconfig.cjs.json", "build:watch": "rm -rf build && npm run build:esm -- --watch", "build:local": "npm run build && cp -r build ~/AppData/Roaming/npm/node_modules/@cheqd/did-provider-cheqd && veramo config verify", + "build:local:deps": "npm run build && cp -r build ~/AppData/Roaming/npm/node_modules/@cheqd/did-provider-cheqd && cp -r node_modules ~/AppData/Roaming/npm/node_modules/@cheqd/did-provider-cheqd && veramo config verify", "build:local:ubuntu": "npm run build && cp -r build /usr/lib/node_modules/@cheqd/did-provider-cheqd && veramo config verify", "generate-plugin-schema": "veramo dev generate-plugin-schema", "start": "veramo server", @@ -102,9 +103,10 @@ "@cosmjs/stargate": "^0.32.2", "@cosmjs/utils": "^0.32.2", "@digitalbazaar/vc-status-list": "^7.1.0", - "@lit-protocol/lit-node-client": "^3.1.1", + "@lit-protocol/auth-helpers": "^3.1.4", + "@lit-protocol/contracts-sdk": "^3.1.4", + "@lit-protocol/lit-node-client": "^3.2.2", "@lit-protocol/lit-node-client-v2": "npm:@lit-protocol/lit-node-client@2.2.63", - "@lit-protocol/encryption-v2": "npm:@lit-protocol/encryption@2.2.63", "@veramo/core": "^5.6.0", "@veramo/did-manager": "^5.6.0", "@veramo/did-provider-key": "^5.6.0", @@ -114,12 +116,12 @@ "did-jwt": "^7.4.7", "did-resolver": "^4.1.0", "generate-password": "^1.7.1", - "uint8arrays": "^5.0.1", + "uint8arrays": "^5.0.2", "uuid": "^9.0.1" }, "devDependencies": { - "@lit-protocol/types": "^3.0.27", - "@lit-protocol/types-v2": "npm:@lit-protocol/types@^2.2.63", + "@lit-protocol/types": "^3.2.2", + "@lit-protocol/types-v2": "npm:@lit-protocol/types@2.2.63", "@semantic-release/changelog": "^6.0.3", "@semantic-release/commit-analyzer": "^11.1.0", "@semantic-release/git": "^10.0.1", @@ -127,17 +129,17 @@ "@semantic-release/npm": "^11.0.2", "@semantic-release/release-notes-generator": "^12.1.0", "@types/debug": "^4.1.12", - "@types/jest": "^29.5.11", + "@types/jest": "^29.5.12", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "conventional-changelog-conventionalcommits": "^7.0.2", - "eslint": "^8.56.0", + "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-config-typescript": "^3.0.0", "jest": "^29.7.0", "long": "^4.0.0", - "prettier": "^3.2.4", + "prettier": "^3.2.5", "semantic-release": "^22.0.12", "ts-jest": "^29.1.2", "typescript": "^5.3.3" diff --git a/src/agent/ICheqd.ts b/src/agent/ICheqd.ts index 7ac4f146..fc68a1c9 100644 --- a/src/agent/ICheqd.ts +++ b/src/agent/ICheqd.ts @@ -16,7 +16,6 @@ import { createKeyPairBase64, createKeyPairHex, createVerificationKeys, - toMultibaseRaw, } from '@cheqd/sdk'; import { Coin, DeliverTxResponse } from '@cosmjs/stargate'; import { @@ -39,7 +38,6 @@ import { IResolver, W3CVerifiableCredential, ICredentialVerifier, - DIDResolutionResult, } from '@veramo/core'; import { CheqdDIDProvider, @@ -54,7 +52,6 @@ import { DefaultStatusList2021Encoding, DefaultStatusList2021ResourceType, DefaultStatusList2021StatusPurposeType, - createMsgCreateDidDocPayloadToSign, TPublicKeyEd25519, } from '../did-manager/cheqd-did-provider.js'; import { fromString, toString } from 'uint8arrays'; @@ -65,10 +62,13 @@ import fs from 'fs'; import Debug from 'debug'; import { CosmosAccessControlCondition, + CreateCapacityDelegationAuthSignatureResult, LitCompatibleCosmosChain, LitCompatibleCosmosChains, + LitContracts, LitNetwork, LitProtocol, + MintCapacityCreditsResult, } from '../dkg-threshold/lit-protocol/v3.js'; import { blobToHexString, @@ -345,6 +345,16 @@ export type ObservationResult = { error?: IError; }; +export type MintCapacityCreditResult = { + minted: boolean; + error?: IError; +} & Partial; + +export type DelegateCapacityCreditResult = { + delegated: boolean; + error?: IError; +} & Partial; + export const AccessControlConditionTypes = { timelockPayment: 'timelockPayment', memoNonce: 'memoNonce', @@ -386,6 +396,8 @@ export const UnsuspendCredentialMethodName = 'cheqdUnsuspendCredential'; export const UnsuspendCredentialsMethodName = 'cheqdUnsuspendCredentials'; export const TransactSendTokensMethodName = 'cheqdTransactSendTokens'; export const ObservePaymentConditionMethodName = 'cheqdObservePaymentCondition'; +export const MintCapacityCreditMethodName = 'cheqdMintCapacityCredit'; +export const DelegateCapacityCreditMethodName = 'cheqdDelegateCapacityCredit'; export const DidPrefix = 'did'; export const CheqdDidMethod = 'cheqd'; @@ -654,6 +666,23 @@ export interface ICheqdObservePaymentConditionArgs { returnTxResponse?: boolean; } +export interface ICheqdMintCapacityCreditArgs { + network: CheqdNetwork; + effectiveDays: number; + requestsPerDay?: number; + requestsPerSecond?: number; + requestsPerKilosecond?: number; +} + +export interface ICheqdDelegateCapacityCreditArgs { + network: CheqdNetwork; + capacityTokenId: string; + delegateeAddresses: string[]; + usesPermitted: number; + expiration?: string; + statement?: string; +} + export interface ICheqdStatusList2021Options { statusListFile?: string; statusListInlineBitstring?: string; @@ -789,6 +818,14 @@ export interface ICheqd extends IPluginMethodMap { args: ICheqdObservePaymentConditionArgs, context: IContext ) => Promise; + [MintCapacityCreditMethodName]: ( + args: ICheqdMintCapacityCreditArgs, + context: IContext + ) => Promise; + [DelegateCapacityCreditMethodName]: ( + args: ICheqdDelegateCapacityCreditArgs, + context: IContext + ) => Promise; } export class Cheqd implements IAgentPlugin { @@ -1241,6 +1278,8 @@ export class Cheqd implements IAgentPlugin { [UnsuspendCredentialsMethodName]: this.UnsuspendBulkCredentialsWithStatusList2021.bind(this), [TransactSendTokensMethodName]: this.TransactSendTokens.bind(this), [ObservePaymentConditionMethodName]: this.ObservePaymentCondition.bind(this), + [MintCapacityCreditMethodName]: this.MintCapacityCredit.bind(this), + [DelegateCapacityCreditMethodName]: this.DelegateCapacityCredit.bind(this), }; } @@ -1260,7 +1299,7 @@ export class Cheqd implements IAgentPlugin { throw new Error('[did-provider-cheqd]: document object is required'); } - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + const provider = await Cheqd.getProviderFromDidUrl(args.document.id, this.supportedDidProviders); this.didProvider = provider; this.providerId = Cheqd.generateProviderId(this.didProvider.network); @@ -1290,7 +1329,7 @@ export class Cheqd implements IAgentPlugin { throw new Error('[did-provider-cheqd]: document object is required'); } - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + const provider = await Cheqd.getProviderFromDidUrl(args.document.id, this.supportedDidProviders); this.didProvider = provider; this.providerId = Cheqd.generateProviderId(this.didProvider.network); @@ -1316,7 +1355,7 @@ export class Cheqd implements IAgentPlugin { throw new Error('[did-provider-cheqd]: document object is required'); } - const provider = await Cheqd.loadProvider(args.document.id, this.supportedDidProviders); + const provider = await Cheqd.getProviderFromDidUrl(args.document.id, this.supportedDidProviders); this.didProvider = provider; this.providerId = Cheqd.generateProviderId(this.didProvider.network); @@ -1356,7 +1395,7 @@ export class Cheqd implements IAgentPlugin { } this.providerId = Cheqd.generateProviderId(args.network); - this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromNetwork(args.network, this.supportedDidProviders); return await this.didProvider.createResource( { @@ -1488,6 +1527,17 @@ export class Cheqd implements IAgentPlugin { // get network const network = args.issuerDid.split(':')[2]; + // define provider + const provider = (function (that) { + // switch on network + return ( + that.supportedDidProviders.find((provider) => provider.network === network) || + (function () { + throw new Error(`[did-provider-cheqd]: no relevant providers found`); + })() + ); + })(this); + // generate bitstring const bitstring = await context.agent[GenerateStatusList2021MethodName]({ length: args?.statusListLength || Cheqd.defaultStatusList2021Length, @@ -1504,10 +1554,7 @@ export class Cheqd implements IAgentPlugin { ); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: args?.dkgOptions?.chain || that.didProvider.dkgOptions.chain, - litNetwork: args?.dkgOptions?.network || that.didProvider.dkgOptions.network, - }); + const lit = await provider.instantiateDkgThresholdProtocolClient({}); // construct access control conditions const unifiedAccessControlConditions = await Promise.all( @@ -1705,7 +1752,11 @@ export class Cheqd implements IAgentPlugin { } this.providerId = Cheqd.generateProviderId(args.network); - this.didProvider = await Cheqd.loadProvider(this.providerId, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromNetwork(args.network, this.supportedDidProviders); + + console.warn('providerId:', this.providerId); + + console.warn('didProvider:', this.didProvider); return await this.didProvider.createResource( { @@ -2001,7 +2052,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2014,12 +2065,12 @@ export class Cheqd implements IAgentPlugin { case DefaultStatusList2021StatusPurposeTypes.revocation: return { ...verificationResult, - revoked: await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args }), + revoked: await Cheqd.checkRevoked(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }), }; case DefaultStatusList2021StatusPurposeTypes.suspension: return { ...verificationResult, - suspended: await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args }), + suspended: await Cheqd.checkSuspended(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }), }; default: throw new Error( @@ -2061,7 +2112,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2073,12 +2124,12 @@ export class Cheqd implements IAgentPlugin { case DefaultStatusList2021StatusPurposeTypes.revocation: return { ...verificationResult, - revoked: await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args }), + revoked: await Cheqd.checkRevoked(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }), }; case DefaultStatusList2021StatusPurposeTypes.suspension: return { ...verificationResult, - suspended: await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args }), + suspended: await Cheqd.checkSuspended(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }), }; default: throw new Error( @@ -2165,7 +2216,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2175,9 +2226,9 @@ export class Cheqd implements IAgentPlugin { switch (credential.credentialStatus?.statusPurpose) { case DefaultStatusList2021StatusPurposeTypes.revocation: - return { revoked: await Cheqd.checkRevoked(credential, { ...args.options, topArgs: args }) }; + return { revoked: await Cheqd.checkRevoked(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }) }; case DefaultStatusList2021StatusPurposeTypes.suspension: - return { suspended: await Cheqd.checkSuspended(credential, { ...args.options, topArgs: args }) }; + return { suspended: await Cheqd.checkSuspended(credential, { ...args.options, instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), topArgs: args }) }; default: throw new Error( `[did-provider-cheqd]: check status: Unsupported status purpose: ${credential.credentialStatus?.statusPurpose}` @@ -2279,7 +2330,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2293,6 +2344,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), statusListEncoding: args?.options?.statusListEncoding, statusListValidUntil: args?.options?.statusListValidUntil, resourceId: args?.options?.resourceId, @@ -2416,7 +2468,7 @@ export class Cheqd implements IAgentPlugin { : (credentials[0].issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2430,6 +2482,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), resourceId: args?.options?.resourceId, resourceVersion: args?.options?.resourceVersion, resourceAlsoKnownAs: args?.options?.alsoKnownAs, @@ -2533,7 +2586,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2547,6 +2600,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), statusListEncoding: args?.options?.statusListEncoding, statusListValidUntil: args?.options?.statusListValidUntil, resourceId: args?.options?.resourceId, @@ -2670,7 +2724,7 @@ export class Cheqd implements IAgentPlugin { : (credentials[0].issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2684,6 +2738,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), resourceId: args?.options?.resourceId, resourceVersion: args?.options?.resourceVersion, resourceAlsoKnownAs: args?.options?.alsoKnownAs, @@ -2787,7 +2842,7 @@ export class Cheqd implements IAgentPlugin { typeof credential.issuer === 'string' ? credential.issuer : (credential.issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2801,6 +2856,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), statusListEncoding: args?.options?.statusListEncoding, statusListValidUntil: args?.options?.statusListValidUntil, resourceId: args?.options?.resourceId, @@ -2924,7 +2980,7 @@ export class Cheqd implements IAgentPlugin { : (credentials[0].issuer as { id: string }).id; // define provider, if applicable - this.didProvider = await Cheqd.loadProvider(issuer, this.supportedDidProviders); + this.didProvider = await Cheqd.getProviderFromDidUrl(issuer, this.supportedDidProviders); // define provider id, if applicable this.providerId = Cheqd.generateProviderId(issuer); @@ -2938,6 +2994,7 @@ export class Cheqd implements IAgentPlugin { topArgs: args, publishOptions: { context, + instantiateDkgClient: this.didProvider.instantiateDkgThresholdProtocolClient(args.dkgOptions), resourceId: args?.options?.resourceId, resourceVersion: args?.options?.resourceVersion, resourceAlsoKnownAs: args?.options?.alsoKnownAs, @@ -2952,15 +3009,7 @@ export class Cheqd implements IAgentPlugin { context: IContext ): Promise { // define provider - const provider = (function (that) { - // switch on network - return ( - that.supportedDidProviders.find((provider) => provider.network === args.network) || - (function () { - throw new Error(`[did-provider-cheqd]: transact: no relevant providers found`); - })() - ); - })(this); + const provider = await Cheqd.getProviderFromNetwork(args.network, this.supportedDidProviders); try { // delegate to provider @@ -2980,6 +3029,8 @@ export class Cheqd implements IAgentPlugin { txResponse: args?.returnTxResponse ? transactionResult : undefined, } satisfies TransactionResult; } catch (error) { + console.warn('[did-provider-cheqd]: transact: sendTokens', error); + // return error return { successful: false, @@ -3233,6 +3284,67 @@ export class Cheqd implements IAgentPlugin { } } + private async MintCapacityCredit( + args: ICheqdMintCapacityCreditArgs, + context: IContext + ): Promise { + // define provider + const provider = await Cheqd.getProviderFromNetwork(args.network, this.supportedDidProviders); + + try { + // delegate to provider + const mintingResult = await provider.mintCapacityCredit({ + effectiveDays: args.effectiveDays, + requestsPerDay: args.requestsPerDay, + requestsPerSecond: args.requestsPerSecond, + requestsPerKilosecond: args.requestsPerKilosecond, + }); + + // return mint result + return { + minted: true, + ...mintingResult, + } satisfies MintCapacityCreditResult; + } catch (error) { + // return error + return { + minted: false, + error: error as IError, + } satisfies MintCapacityCreditResult; + } + } + + private async DelegateCapacityCredit( + args: ICheqdDelegateCapacityCreditArgs, + context: IContext + ): Promise { + // define provider + const provider = await Cheqd.getProviderFromNetwork(args.network, this.supportedDidProviders); + + try { + // delegate to provider + const delegationResult = await provider.delegateCapacityCredit({ + capacityTokenId: args.capacityTokenId, + delegateeAddresses: args.delegateeAddresses, + uses: args.usesPermitted, + expiration: args.expiration, + statement: args.statement, + }); + + // return delegation result + return { + delegated: true, + ...delegationResult, + } satisfies DelegateCapacityCreditResult; + } catch (error) { + // return error + return { + delegated: false, + error: error as IError, + } satisfies DelegateCapacityCreditResult; + } + } + static async revokeCredential( credential: VerifiableCredential, options?: ICheqdStatusList2021Options @@ -3504,10 +3616,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -4096,10 +4205,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -4612,10 +4718,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -5204,10 +5307,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -5719,10 +5819,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -6311,10 +6408,7 @@ export class Cheqd implements IAgentPlugin { } = await LitProtocol.encryptDirect(fromString(bitstring, 'base64url')); // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: topArgs?.dkgOptions?.chain, - litNetwork: topArgs?.dkgOptions?.network, - }); + const lit = await options!.publishOptions.instantiateDkgClient as LitProtocol; // construct access control conditions and payment conditions tuple const unifiedAccessControlConditionsTuple = publishedList.metadata.encrypted @@ -6605,10 +6699,7 @@ export class Cheqd implements IAgentPlugin { )[1]; // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: options?.topArgs?.dkgOptions?.chain, - litNetwork: options?.topArgs?.dkgOptions?.network, - }); + const lit = (await options.instantiateDkgClient) as LitProtocol; // construct access control conditions const unifiedAccessControlConditions = await Promise.all( @@ -6775,10 +6866,7 @@ export class Cheqd implements IAgentPlugin { )[1]; // instantiate dkg-threshold client, in which case lit-protocol is used - const lit = await LitProtocol.create({ - chain: options?.topArgs?.dkgOptions?.chain, - litNetwork: options?.topArgs?.dkgOptions?.network, - }); + const lit = await options.instantiateDkgClient as LitProtocol; // construct access control conditions const unifiedAccessControlConditions = await Promise.all( @@ -7333,12 +7421,34 @@ export class Cheqd implements IAgentPlugin { ); } - static async loadProvider(didUrl: string, providers: CheqdDIDProvider[]): Promise { + static async getProviderFromDidUrl( + didUrl: string, + providers: CheqdDIDProvider[], + message?: string + ): Promise { const provider = providers.find((provider) => - didUrl.includes(`${DidPrefix}:${CheqdDidMethod}:${provider.network}`) + didUrl.includes(`${DidPrefix}:${CheqdDidMethod}:${provider.network}:`) ); if (!provider) { - throw new Error(`[did-provider-cheqd]: Provider namespace not found`); + throw new Error( + message || + `[did-provider-cheqd]: no relevant providers found for did url ${didUrl}: loaded providers: ${providers.map((provider) => `${DidPrefix}:${CheqdDidMethod}:${provider.network}`).join(', ')}` + ); + } + return provider; + } + + static async getProviderFromNetwork( + network: CheqdNetwork, + providers: CheqdDIDProvider[], + message?: string + ): Promise { + const provider = providers.find((provider) => provider.network === network); + if (!provider) { + throw new Error( + message || + `[did-provider-cheqd]: no relevant providers found for network ${network}: loaded providers: ${providers.map((provider) => `${DidPrefix}:${CheqdDidMethod}:${provider.network}`).join(', ')}` + ); } return provider; } diff --git a/src/did-manager/cheqd-did-provider.ts b/src/did-manager/cheqd-did-provider.ts index 5b20515f..c7c244a0 100644 --- a/src/did-manager/cheqd-did-provider.ts +++ b/src/did-manager/cheqd-did-provider.ts @@ -34,18 +34,24 @@ import { import { AbstractIdentifierProvider } from '@veramo/did-manager'; import { base64ToBytes, extractPublicKeyHex } from '@veramo/utils'; import Debug from 'debug'; -import { EnglishMnemonic as _, Ed25519 } from '@cosmjs/crypto'; +import { EnglishMnemonic as _, Bip39, Ed25519, Random } from '@cosmjs/crypto'; import { fromString, toString } from 'uint8arrays'; import { MsgCreateDidDocPayload, MsgDeactivateDidDocPayload, SignInfo } from '@cheqd/ts-proto/cheqd/did/v2/index.js'; import { v4 } from 'uuid'; import { + CreateCapacityDelegationAuthSignatureResult, LitCompatibleCosmosChain, LitCompatibleCosmosChains, + LitContracts, LitNetwork, LitNetworks, + LitProtocol, + MintCapacityCreditsResult, } from '../dkg-threshold/lit-protocol/v3.js'; -import { IContext } from '../agent/ICheqd.js'; +import { DkgOptions, IContext } from '../agent/ICheqd.js'; import { getControllers } from '../utils/helpers.js'; +import { ethers } from 'ethers'; +import { Secp256k1HdWallet, Secp256k1Wallet } from '@cosmjs/amino'; const debug = Debug('veramo:did-provider-cheqd'); @@ -438,6 +444,8 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { public readonly network: CheqdNetwork; public readonly rpcUrl: string; private readonly cosmosPayerWallet: Promise; + private readonly _aminoSigner: Promise; + private readonly ethereumAuthWallet: ethers.HDNodeWallet | ethers.Wallet; public readonly dkgOptions: { chain: Extract; network: LitNetwork; @@ -466,47 +474,80 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { chain: options.dkgOptions.chain ? options.dkgOptions.chain : DefaultDkgSupportedChains[this.network], - network: options.dkgOptions.network ? options.dkgOptions.network : LitNetworks.cayenne, + network: options.dkgOptions.network ? options.dkgOptions.network : LitNetworks.habanero, } - : { chain: DefaultDkgSupportedChains[this.network], network: LitNetworks.cayenne }; + : { chain: DefaultDkgSupportedChains[this.network], network: LitNetworks.habanero }; if (!options?.cosmosPayerSeed || options.cosmosPayerSeed === '') { - this.cosmosPayerWallet = DirectSecp256k1HdWallet.generate(); + // generate mnemonic, if not provided + const mnemonic = Bip39.encode(Random.getBytes(32)).toString(); + + // setup wallets - case: cosmos direct payer wallet + this.cosmosPayerWallet = DirectSecp256k1HdWallet.fromMnemonic(mnemonic); + + // setup wallets - case: ethereum signer wallet + this.ethereumAuthWallet = ethers.Wallet.fromPhrase(mnemonic); + + // setup wallets - case: amino signer wallet + this._aminoSigner = Secp256k1HdWallet.fromMnemonic(mnemonic); + return; } - this.cosmosPayerWallet = EnglishMnemonic._mnemonicMatcher.test(options.cosmosPayerSeed) + + const isMnemonic = EnglishMnemonic._mnemonicMatcher.test(options.cosmosPayerSeed); + + this.cosmosPayerWallet = isMnemonic ? DirectSecp256k1HdWallet.fromMnemonic(options.cosmosPayerSeed, { prefix: 'cheqd' }) : DirectSecp256k1Wallet.fromKey(fromString(options.cosmosPayerSeed.replace(/^0x/, ''), 'hex'), 'cheqd'); + + this.ethereumAuthWallet = isMnemonic + ? ethers.Wallet.fromPhrase(options.cosmosPayerSeed) + : new ethers.Wallet(options.cosmosPayerSeed); + + this._aminoSigner = isMnemonic + ? Secp256k1HdWallet.fromMnemonic(options.cosmosPayerSeed, { prefix: 'cheqd' }) + : Secp256k1Wallet.fromKey(fromString(options.cosmosPayerSeed.replace(/^0x/, ''), 'hex'), 'cheqd'); } async getWalletAccounts(): Promise { return await (await this.cosmosPayerWallet).getAccounts(); } + async getEthereumWalletAccounts(): Promise { + return [ + { + address: this.ethereumAuthWallet.address, + pubkey: fromString(this.ethereumAuthWallet.signingKey.publicKey, 'hex'), + algo: 'secp256k1', + }, + ]; + } + private async getCheqdSDK(fee?: DidStdFee, gasPrice?: GasPrice): Promise { - if (!this.sdk) { - const wallet = await this.cosmosPayerWallet.catch(() => { - throw new Error(`[did-provider-cheqd]: network: ${this.network} valid cosmosPayerSeed is required`); - }); - const sdkOptions: ICheqdSDKOptions = { - modules: [ - DIDModule as unknown as AbstractCheqdSDKModule, - ResourceModule as unknown as AbstractCheqdSDKModule, - ], - rpcUrl: this.rpcUrl, - wallet: wallet, - gasPrice, - }; + if (this.sdk) return this.sdk; + + const wallet = await this.cosmosPayerWallet.catch(() => { + throw new Error(`[did-provider-cheqd]: network: ${this.network} valid cosmosPayerSeed is required`); + }); + const sdkOptions: ICheqdSDKOptions = { + modules: [ + DIDModule as unknown as AbstractCheqdSDKModule, + ResourceModule as unknown as AbstractCheqdSDKModule, + ], + rpcUrl: this.rpcUrl, + wallet: wallet, + gasPrice, + }; - this.sdk = await createCheqdSDK(sdkOptions); - this.fee = fee; + this.sdk = await createCheqdSDK(sdkOptions); + this.fee = fee; - if (this?.fee && !this?.fee?.payer) { - const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address; - this.fee.payer = feePayer; - } + if (this?.fee && !this?.fee?.payer) { + const feePayer = (await (await this.cosmosPayerWallet).getAccounts())[0].address; + this.fee.payer = feePayer; } - return this.sdk!; + + return this.sdk; } async createIdentifier( @@ -537,6 +578,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { sdk: sdk, } satisfies ISDKContext); + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to create DID. Reason: ${tx.rawLog}`); const identifier: ICheqdIDentifier = { @@ -670,6 +712,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { { sdk: sdk } satisfies ISDKContext ); + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`); // Setup return value const identifier: ICheqdIDentifier = { @@ -788,6 +831,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { { sdk: sdk } satisfies ISDKContext ); + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to update DID. Reason: ${tx.rawLog}`); debug('Deactivated DID', did); @@ -843,6 +887,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { sdk: sdk, }); + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to create Resource. Reason: ${tx.rawLog}`); const mapKeyType = (keyType: 'Ed25519' | 'Secp256k1' | 'P256' | undefined): TKeyType | undefined => { @@ -947,6 +992,7 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { const tx = await sdk.signer.broadcastTx(args.txBytes, args?.timeoutMs, args?.pollIntervalMs); // assert tx code is 0, in other words, tx succeeded + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`); // keep log @@ -963,6 +1009,9 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { args.memo ); + console.warn('tx', tx); + + // TODO: switch to tx.events, after cheqd-sdk is updated to match cosmos-sdk v0.50+ assert(tx.code === 0, `cosmos_transaction: Failed to send tokens. Reason: ${tx.rawLog}`); debug('Sent tokens', args.amount.amount, args.amount.denom, 'to', args.recipientAddress); @@ -970,6 +1019,65 @@ export class CheqdDIDProvider extends AbstractIdentifierProvider { return tx; } + async mintCapacityCredit(args: { + effectiveDays: number; + requestsPerDay?: number; + requestsPerSecond?: number; + requestsPerKilosecond?: number; + }): Promise { + // instantiate dkg-threshold contract client, in which case lit-protocol is used + const litContracts = await this.instantiateDkgThresholdContractClient(); + + // mint capacity credits + const result = await litContracts.mintCapacityCredits(args); + + // keep log + debug('Minted capacity credits', result.capacityTokenIdStr, 'for', args.effectiveDays, 'days', 'with transaction hash', result.rliTxHash, 'from address', this.ethereumAuthWallet.address); + + return result; + }; + + async delegateCapacityCredit(args: { + capacityTokenId: string; + delegateeAddresses: string[]; + uses: number; + expiration?: string; + statement?: string; + }): Promise { + // instantiate dkg-threshold client, in which case lit-protocol is used + const litProtocol = await this.instantiateDkgThresholdProtocolClient(); + + // delegate capacity credits + const result = await litProtocol.createCapacityDelegationAuthSignature({ + dAppOwnerWallet: this.ethereumAuthWallet instanceof ethers.Wallet ? this.ethereumAuthWallet : new ethers.Wallet(this.ethereumAuthWallet.privateKey), + capacityTokenId: args.capacityTokenId, + delegateeAddresses: args.delegateeAddresses, + uses: args.uses.toString(), + expiration: args.expiration, + statement: args.statement, + }); + + // keep log + debug('Delegated capacity credits', args.capacityTokenId, 'to', args.delegateeAddresses.join(', '), 'with auth signature', result.capacityDelegationAuthSig.sig, 'from address', this.ethereumAuthWallet.address); + + return result; + } + + async instantiateDkgThresholdProtocolClient(dkgOptions: DkgOptions = this.dkgOptions): Promise { + return await LitProtocol.create({ + chain: dkgOptions.chain || this.dkgOptions.chain, + litNetwork: dkgOptions.network || this.dkgOptions.network, + cosmosAuthWallet: await this._aminoSigner, + }); + } + + async instantiateDkgThresholdContractClient(dkgNetwork: LitNetwork = this.dkgOptions.network): Promise { + return await LitContracts.create({ + ethereumAuthWallet: this.ethereumAuthWallet instanceof ethers.Wallet ? this.ethereumAuthWallet : new ethers.Wallet(this.ethereumAuthWallet.privateKey), + litNetwork: dkgNetwork, + }); + } + private async signPayload( context: IAgentContext, data: Uint8Array, diff --git a/src/dkg-threshold/lit-protocol/v2.ts b/src/dkg-threshold/lit-protocol/v2.ts index 0982122e..31c77ca5 100644 --- a/src/dkg-threshold/lit-protocol/v2.ts +++ b/src/dkg-threshold/lit-protocol/v2.ts @@ -1,8 +1,7 @@ import { OfflineAminoSigner, Secp256k1HdWallet, StdSignDoc } from '@cosmjs/amino'; import { toString } from 'uint8arrays/to-string'; import { sha256 } from '@cosmjs/crypto'; -import { LitNodeClientNodeJs, LitNodeClient, encryptString } from '@lit-protocol/lit-node-client-v2'; -import { decryptString } from '@lit-protocol/encryption-v2'; +import { LitNodeClientNodeJs, LitNodeClient, encryptString, decryptString } from '@lit-protocol/lit-node-client-v2'; import { JsonSaveEncryptionKeyRequest } from '@lit-protocol/types-v2'; import { randomBytes } from '../../utils/helpers.js'; import { isBrowser, isNode } from '../../utils/env.js'; diff --git a/src/dkg-threshold/lit-protocol/v3.ts b/src/dkg-threshold/lit-protocol/v3.ts index 41f40dc7..4dc54b46 100644 --- a/src/dkg-threshold/lit-protocol/v3.ts +++ b/src/dkg-threshold/lit-protocol/v3.ts @@ -1,13 +1,16 @@ -import { OfflineAminoSigner, Secp256k1HdWallet, StdSignDoc } from '@cosmjs/amino'; +import { OfflineAminoSigner, Secp256k1HdWallet, Secp256k1Wallet, StdSignDoc } from '@cosmjs/amino'; import { toString } from 'uint8arrays/to-string'; import { sha256 } from '@cosmjs/crypto'; import { LitNodeClientNodeJs, LitNodeClient } from '@lit-protocol/lit-node-client'; -import { DecryptResponse, EncryptResponse, UnifiedAccessControlConditions } from '@lit-protocol/types'; +import { DecryptResponse, EncryptResponse, MintCapacityCreditsRes, AuthSig as GenericAuthSig } from '@lit-protocol/types'; +import { LitRLIResource } from '@lit-protocol/auth-helpers'; import { generateSymmetricKey, randomBytes } from '../../utils/helpers.js'; import { isBrowser, isNode } from '../../utils/env.js'; import { v4 } from 'uuid'; import { fromString } from 'uint8arrays'; import { LitProtocolDebugEnabled } from '../../utils/constants.js'; +import { LitContracts as LitContractsClient } from '@lit-protocol/contracts-sdk'; +import { ethers } from 'ethers'; export type ThresholdEncryptionResult = { encryptedString: Uint8Array; @@ -62,13 +65,41 @@ export type DecryptToStringMethod = ( export type LitNetwork = (typeof LitNetworks)[keyof typeof LitNetworks]; export type LitCompatibleCosmosChain = (typeof LitCompatibleCosmosChains)[keyof typeof LitCompatibleCosmosChains]; export type LitProtocolOptions = { - cosmosAuthWallet: Secp256k1HdWallet; + cosmosAuthWallet: Secp256k1HdWallet | Secp256k1Wallet; litNetwork?: LitNetwork; chain?: LitCompatibleCosmosChain; }; +export type LitContractsOptions = { + ethereumAuthWallet: ethers.Wallet; + litNetwork?: LitNetwork; +}; +export type LitContractsMintCapacityCreditsOptions = { + requestsPerDay?: number; + requestsPerSecond?: number; + requestsPerKilosecond?: number; + effectiveDays: number; +}; +export type LitContractsCreateCapacityDelegationAuthSignatureOptions = { + dAppOwnerWallet: ethers.Wallet; + capacityTokenId: string; + delegateeAddresses: string[]; + uses?: string; + domain?: string; + expiration?: string; + statement?: string; +}; +export type MintCapacityCreditsResult = MintCapacityCreditsRes; +export type CreateCapacityDelegationAuthSignatureResult = { + litResource: LitRLIResource; + capacityDelegationAuthSig: GenericAuthSig; +}; + export type TxNonceFormat = (typeof TxNonceFormats)[keyof typeof TxNonceFormats]; +export type PrivateKeyLiteral = `0x${string}`; export const LitNetworks = { + manzano: 'manzano', + habanero: 'habanero', cayenne: 'cayenne', localhost: 'localhost', custom: 'custom', @@ -82,9 +113,9 @@ export const TxNonceFormats = { entropy: 'entropy', uuid: 'uuid', timestamp: 'ti export class LitProtocol { client: LitNodeClientNodeJs | LitNodeClient; - litNetwork: LitNetwork = LitNetworks.cayenne; + litNetwork: LitNetwork = LitNetworks.habanero; chain: LitCompatibleCosmosChain = LitCompatibleCosmosChains.cheqdTestnet; - private readonly cosmosAuthWallet: Secp256k1HdWallet; + private readonly cosmosAuthWallet: Secp256k1HdWallet | Secp256k1Wallet; private constructor(options: LitProtocolOptions) { // validate options @@ -112,7 +143,7 @@ export class LitProtocol { async encrypt( secret: Uint8Array, - unifiedAccessControlConditions: NonNullable + unifiedAccessControlConditions: NonNullable ): Promise { // generate auth signature const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet); @@ -121,6 +152,7 @@ export class LitProtocol { const { ciphertext: encryptedString, dataToEncryptHash: stringHash } = (await this.client.encrypt({ chain: this.chain, dataToEncrypt: secret, + // @ts-expect-error missing chains in 3rd party types unifiedAccessControlConditions, authSig, })) satisfies EncryptStringMethodResult; @@ -134,7 +166,7 @@ export class LitProtocol { async decrypt( encryptedString: string, stringHash: string, - unifiedAccessControlConditions: NonNullable + unifiedAccessControlConditions: NonNullable ): Promise { // generate auth signature const authSig = await LitProtocol.generateAuthSignature(this.cosmosAuthWallet); @@ -144,6 +176,7 @@ export class LitProtocol { chain: this.chain, ciphertext: encryptedString, dataToEncryptHash: stringHash, + // @ts-expect-error missing chains in 3rd party types unifiedAccessControlConditions, authSig, })) satisfies DecryptToStringMethodResult; @@ -151,6 +184,19 @@ export class LitProtocol { return toString(decryptedData, 'utf-8'); } + async createCapacityDelegationAuthSignature(options: LitContractsCreateCapacityDelegationAuthSignatureOptions): Promise { + return await this.client.createCapacityDelegationAuthSig({ + // @ts-expect-error inferred as any, as per the original code + dAppOwnerWallet: options.dAppOwnerWallet, + capacityTokenId: options.capacityTokenId, + delegateeAddresses: options.delegateeAddresses, + uses: options.uses, + domain: options.domain, + expiration: options.expiration, + statement: options.statement, + }); + } + static async encryptDirect(data: Uint8Array): Promise { try { // generate symmetric key @@ -234,7 +280,7 @@ export class LitProtocol { if (!options?.chain) options.chain = LitCompatibleCosmosChains.cheqdTestnet; // validate top-level options litNetwork - if (!options?.litNetwork) options.litNetwork = LitNetworks.cayenne; + if (!options?.litNetwork) options.litNetwork = LitNetworks.habanero; const litProtocol = new LitProtocol(options as LitProtocolOptions); await litProtocol.connect(); @@ -351,3 +397,71 @@ export class LitProtocol { }; } } + +export class LitContracts { + client: LitContractsClient; + litNetwork: LitNetwork = LitNetworks.habanero; + private readonly ethereumAuthWallet: ethers.Wallet; + + constructor (options: LitContractsOptions) { + // validate options + if (options.litNetwork && !Object.values(LitNetworks).includes(options.litNetwork)) + throw new Error(`[did-provider-cheqd]: lit-contracts: Invalid LitNetwork: ${options.litNetwork}`); + + // set options + if (options.litNetwork) this.litNetwork = options.litNetwork; + this.ethereumAuthWallet = options.ethereumAuthWallet; + + // set client + this.client = new LitContractsClient({ signer: this.ethereumAuthWallet, network: this.litNetwork }); + } + + async connect(): Promise { + return await this.client.connect(); + } + + async mintCapacityCredits(options: LitContractsMintCapacityCreditsOptions): Promise { + return await this.client.mintCapacityCreditsNFT({ + requestsPerDay: options.requestsPerDay, + requestsPerSecond: options.requestsPerSecond, + requestsPerKilosecond: options.requestsPerKilosecond, + daysUntilUTCMidnightExpiration: options.effectiveDays, + }); + } + + static async create(options: Partial): Promise { + // instantiate underlying ethereum auth wallet + if (!options.ethereumAuthWallet) + options.ethereumAuthWallet = await LitContracts.generateRandomEthereumAuthWallet(); + + // validate top-level options litNetwork + if (!options?.litNetwork) options.litNetwork = LitNetworks.habanero; + + const litContracts = new LitContracts(options as LitContractsOptions); + await litContracts.connect(); + return litContracts; + } + + static async generateRandomEthereumAuthWallet(): Promise { + // generate private key + wallet + return new ethers.Wallet(await LitContracts.generateRandomPrivateKey()); + } + + static async generateRandomPrivateKey(length = 32, raw = false): Promise { + // ensure crypto, if applicable + const crypto = await (async function () { + if (isNode) return (await import('crypto')).default as Crypto; + if (isBrowser) return window.crypto; + throw new Error('[did-provider-cheqd]: lit-contracts: Unsupported runtime environment'); + } + )(); + + // generate random raw private key + const rawPrivateKey = crypto.getRandomValues(new Uint8Array(length)); + + // return as per request + return (raw + ? rawPrivateKey + : `0x${toString(rawPrivateKey, 'hex')}`) as T; + } +} \ No newline at end of file