From 6863f9d087c0b27526686b5d7ee1041cec794c29 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 11:33:52 +1100 Subject: [PATCH 01/12] Update DIDDocument methods to use Signer instead of private key - Added ethers as a dependency in both did-document and types packages. - Updated IDIDDocument interface and DIDDocument class to replace privateKey with Signer in addContext and signProof methods. - Refactored related methods to utilize Signer for signing operations. --- packages/did-document/package.json | 1 + packages/did-document/src/did-document.ts | 42 ++++++++++------------- packages/types/package.json | 2 +- packages/types/src/IDIDDocument.ts | 15 ++++---- packages/types/src/Web3Interfaces.ts | 16 ++++----- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/packages/did-document/package.json b/packages/did-document/package.json index 381d9771..f97ca1cc 100644 --- a/packages/did-document/package.json +++ b/packages/did-document/package.json @@ -25,6 +25,7 @@ "@verida/types": "^4.4.0", "@verida/vda-common": "^4.4.0", "did-resolver": "^4.0.1", + "ethers": "^5.8.0", "lodash": "^4.17.21" }, "devDependencies": { diff --git a/packages/did-document/src/did-document.ts b/packages/did-document/src/did-document.ts index dd15d0b8..28b03dd8 100644 --- a/packages/did-document/src/did-document.ts +++ b/packages/did-document/src/did-document.ts @@ -5,7 +5,7 @@ import { strip0x } from './helpers' import { IDIDDocument, IKeyring, Network, SecureContextEndpoints, SecureContextEndpointType, VeridaDocInterface, VerificationMethodTypes } from '@verida/types' import { BLOCKCHAIN_CHAINIDS, mapDidNetworkToBlockchainAnchor, interpretIdentifier } from '@verida/vda-common' import { BlockchainAnchor } from '@verida/types' -const _ = require('lodash') +import { Signer } from 'ethers' export default class DIDDocument implements IDIDDocument { @@ -14,8 +14,8 @@ export default class DIDDocument implements IDIDDocument { /** * Force lower case DID as we can't guarantee the DID will always be provided with checksum - * - * @param doc - this value can be a DocInterface or DID. + * + * @param doc - this value can be a DocInterface or DID. */ constructor(doc: VeridaDocInterface | string, publicKeyHex?: string) { if (typeof(doc) == 'string') { @@ -38,7 +38,7 @@ export default class DIDDocument implements IDIDDocument { const { address, network } = interpretIdentifier(this.doc.id) const blockchainAnchor = mapDidNetworkToBlockchainAnchor(network ? network.toString() : 'mainnet') const chainId = blockchainAnchor ? BLOCKCHAIN_CHAINIDS[blockchainAnchor] : BLOCKCHAIN_CHAINIDS[BlockchainAnchor.POLPOS] - + // Add default signing key this.doc.assertionMethod = [ `${this.doc.id}#controller`, @@ -82,13 +82,13 @@ export default class DIDDocument implements IDIDDocument { /** * Not used directly, used for testing - * + * * @param contextName string * @param keyring Keyring - * @param privateKey Private key of the DID that controls this DID Document + * @param signer Signer * @param endpoints Endpoints */ - public async addContext(network: Network, contextName: string, keyring: IKeyring, privateKey: string, endpoints: SecureContextEndpoints) { + public async addContext(network: Network, contextName: string, keyring: IKeyring, signer: Signer, endpoints: SecureContextEndpoints) { // Remove this context if it already exists this.removeContext(contextName, network) @@ -115,11 +115,8 @@ export default class DIDDocument implements IDIDDocument { // Generate a proof that the DID controls the context public signing key that can be used on chain const proofString = `${didAddress}${keys.signPublicAddress}`.toLowerCase() - const privateKeyArray = new Uint8Array( - Buffer.from(privateKey.slice(2), "hex") - ) - const proof = EncryptionUtils.signData(proofString, privateKeyArray) + const proof = await signer.signMessage(proofString) // Add keys to DID document this.addContextSignKey(network, contextHash, keys.signPublicKeyHex, proof) @@ -128,10 +125,10 @@ export default class DIDDocument implements IDIDDocument { /** * Remove the context from the DID document - * - * @param contextName - * @param network - * @returns + * + * @param contextName + * @param network + * @returns */ public removeContext(contextName: string, network?: Network): boolean { const contextHash = DIDDocument.generateContextHash(this.doc.id, contextName) @@ -161,14 +158,14 @@ export default class DIDDocument implements IDIDDocument { }) this.doc.assertionMethod = this.doc.assertionMethod!.filter((entry: string | VerificationMethod) => { return ( - entry !== `${this.doc.id}?${networkString}context=${contextHash}&type=sign` && + entry !== `${this.doc.id}?${networkString}context=${contextHash}&type=sign` && entry !== `${this.doc.id}?${networkString}context=${contextHash}&type=asym` ) }) this.doc.keyAgreement = this.doc.keyAgreement!.filter((entry: string | VerificationMethod) => { return entry !== `${this.doc.id}?${networkString}context=${contextHash}&type=asym` }) - + // Remove services this.doc.service = this.doc.service!.filter((entry: Service) => { return !entry.id.match(`${this.doc.id}\\?${networkString}context=${contextHash}`) @@ -342,13 +339,10 @@ export default class DIDDocument implements IDIDDocument { } } - public signProof(privateKey: Uint8Array | string) { - if (privateKey == 'string') { - privateKey = new Uint8Array(Buffer.from(privateKey.substr(2),'hex')) - } - + public async signProof(signer: Signer) { const proofData = this.getProofData() - const signature = EncryptionUtils.signData(proofData, privateKey) + + const signature = await signer.signMessage(proofData) this.doc.proof = { type: "EcdsaSecp256k1VerificationKey2019", @@ -379,4 +373,4 @@ export default class DIDDocument implements IDIDDocument { return date.toISOString().split('.')[0] + 'Z' } -} \ No newline at end of file +} diff --git a/packages/types/package.json b/packages/types/package.json index 06efe6d3..55e50618 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -23,12 +23,12 @@ "build": "rm -rf dist && tsc" }, "dependencies": { - "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/bignumber": "^5.7.0", "@ethersproject/providers": "^5.7.2", "@types/node": "^18.15.11", "@types/pouchdb-core": "^7.0.11", "did-resolver": "^4.0.1", + "ethers": "^5.8.0", "tweetnacl": "^1.0.3" } } diff --git a/packages/types/src/IDIDDocument.ts b/packages/types/src/IDIDDocument.ts index cf0b3805..efe7f6ef 100644 --- a/packages/types/src/IDIDDocument.ts +++ b/packages/types/src/IDIDDocument.ts @@ -2,21 +2,22 @@ import { ServiceEndpoint, Service } from 'did-resolver' import { SecureContextEndpoints, SecureContextEndpointType, VeridaDocInterface } from "./DocumentInterfaces" import { IKeyring } from './IKeyring' import { Network } from './NetworkInterfaces' +import { Signer } from 'ethers' export interface IDIDDocument { get id(): string - getErrors(): string[] + getErrors(): string[] - addContext(network: Network, contextName: string, keyring: IKeyring, privateKey: string, endpoints: SecureContextEndpoints): Promise + addContext(network: Network, contextName: string, keyring: IKeyring, signer: Signer, endpoints: SecureContextEndpoints): Promise - removeContext(contextName: string, network?: Network): boolean + removeContext(contextName: string, network?: Network): boolean setAttributes(attributes: Record): void import(doc: VeridaDocInterface): void - export(): VeridaDocInterface + export(): VeridaDocInterface addContextService(network: Network, contextHash: string, endpointType: SecureContextEndpointType, serviceType: string, endpointUris: ServiceEndpoint[]): void @@ -24,7 +25,7 @@ export interface IDIDDocument { addContextAsymKey(network: Network, contextHash: string, publicKeyHex: string): void - verifySig(data: any, signature: string): boolean + verifySig(data: any, signature: string): boolean verifyContextSignature(data: any, network: Network, contextName: string, signature: string, contextIsHash: boolean): boolean @@ -32,9 +33,9 @@ export interface IDIDDocument { locateContextProof(contextName: string, network: Network): string | undefined - signProof(privateKey: Uint8Array | string): void + signProof(signer: Signer): Promise verifyProof(): boolean buildTimestamp(date: Date): string -} \ No newline at end of file +} diff --git a/packages/types/src/Web3Interfaces.ts b/packages/types/src/Web3Interfaces.ts index 382a527b..a405c9dd 100644 --- a/packages/types/src/Web3Interfaces.ts +++ b/packages/types/src/Web3Interfaces.ts @@ -1,4 +1,4 @@ -import { Signer } from '@ethersproject/abstract-signer' +import { Signer } from 'ethers' import { BigNumber } from '@ethersproject/bignumber' import { Provider } from '@ethersproject/providers' import { BlockchainAnchor, Network } from './NetworkInterfaces' @@ -16,10 +16,10 @@ export interface Web3ContractInfo { /** EIP1559 Gas Configuration speed */ export type EIP1559GasMode = 'safeLow' | 'standard' | 'fast'; -/** Gas configuration - * +/** Gas configuration + * * eip1559Mode - optional - Once this parameter is set, all other parameters are not used. Gas information is pulled from network. - * + * * maxFeePerGas - optional - Used for EIP1559 chains * maxPriorityFeePerGas - optional - Used for EIP1559 chains * gasLimit - optional - Used for non EIP1559 chains @@ -41,7 +41,7 @@ export interface Web3GasConfiguration { * signer - optional - a Signer that sign the blockchain transactions. If a 'signer' is not provided, then 'contract' with an attached signer need to be used to make transactions * provider - optional - a web3 provider. At least one of `signer`,`provider`, or `rpcUrl` is required * rpcUrl - optinal - a JSON-RPC URL that can be used to connect to an ethereum network. At least one of `signer`, `provider`, or `rpcUrl` is required - * + * */ export interface Web3SelfTransactionConfig extends Web3GasConfiguration { blockchainAnchor?: BlockchainAnchor @@ -86,9 +86,9 @@ export interface Web3GaslessPostConfig { /** * Interface for VDA-DID instance creation. - * + * * `signKey` or `signer` must be provided - * + * * @param identifier: DID * @param signKey: private key of DID's controller. Used to generate signature in transactions to chains * @param signer: Signing function that accepts a private key and returns a signature in hex format @@ -129,4 +129,4 @@ export interface VdaTransactionResult { success: boolean; data?: any error?: string -} \ No newline at end of file +} From b58583e13ca332363bb03e6b4a3d09c064525723 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 11:46:51 +1100 Subject: [PATCH 02/12] Update StorageLink methods to use Signer instead of private key - Updated StorageLink methods to use Signer instead of private key for signing operations. - Added ethers as a dependency in storage-link package. --- packages/storage-link/package.json | 1 + packages/storage-link/src/storage-link.ts | 25 ++++++++++--------- .../storage-link/test/storage-link.test.ts | 24 +++++++++--------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/packages/storage-link/package.json b/packages/storage-link/package.json index d9b704fc..86613d9f 100644 --- a/packages/storage-link/package.json +++ b/packages/storage-link/package.json @@ -22,6 +22,7 @@ "@verida/encryption-utils": "^4.0.0", "@verida/keyring": "^4.4.0", "did-resolver": "^4.0.0", + "ethers": "^5.8.0", "url-parse": "^1.5.3" }, "devDependencies": { diff --git a/packages/storage-link/src/storage-link.ts b/packages/storage-link/src/storage-link.ts index d60c6b64..03e03221 100644 --- a/packages/storage-link/src/storage-link.ts +++ b/packages/storage-link/src/storage-link.ts @@ -2,6 +2,7 @@ import { DIDClient } from "@verida/did-client" import { DIDDocument as VeridaDIDDocument } from "@verida/did-document" import { DIDDocument as DocInterface, ServiceEndpoint } from 'did-resolver' import { Network, IKeyring, SecureContextConfig, SecureContextEndpoints, SecureContextEndpointType, VdaDidEndpointResponses } from "@verida/types" +import { Signer } from "ethers" const Url = require('url-parse') /** @@ -26,10 +27,10 @@ export default class StorageLink { } /** - * - * @param didClient - * @param did - * @param contextName + * + * @param didClient + * @param did + * @param contextName * @returns SecureStorageContextConfig | undefined (if not found) */ static async getLink(network: Network, didClient: DIDClient, did: string, context: string, contextIsName: boolean = true): Promise { @@ -49,11 +50,11 @@ export default class StorageLink { } /** - * + * * @param didClient * @param storageConfig (Must have .id as the contextName) */ - static async setLink(network: Network, didClient: DIDClient, storageConfig: SecureContextConfig, keyring: IKeyring, privateKey: string) { + static async setLink(network: Network, didClient: DIDClient, storageConfig: SecureContextConfig, keyring: IKeyring, signer: Signer) { let did = didClient.getDid() if (!did) { @@ -92,7 +93,7 @@ export default class StorageLink { endpoints.notification = storageConfig.services.notificationServer } - await didDocument.addContext(network, storageConfig.id, keyring, privateKey, endpoints) + await didDocument.addContext(network, storageConfig.id, keyring, signer, endpoints) return await didClient.save(didDocument) } @@ -166,7 +167,7 @@ export default class StorageLink { } const contextHash = assertionParts.query.context - + // Get signing key const signKeyVerificationMethod = doc.verificationMethod!.find((entry: any) => entry.id == `${did}?${networkString}context=${contextHash}&type=sign`) if (!signKeyVerificationMethod) { @@ -178,7 +179,7 @@ export default class StorageLink { // Get asym key const asymKeyVerificationMethod = doc.verificationMethod!.find((entry: any) => entry.id == `${did}?${networkString}context=${contextHash}&type=asym`) if (!asymKeyVerificationMethod) { - return + return } const asymKey = asymKeyVerificationMethod!.publicKeyHex @@ -250,9 +251,9 @@ export default class StorageLink { /** * Ensure the URL has a trailing slash and appropriate port set - * + * * @param endpoint ServiceEndpoint | ServiceEndpoint[] - * @returns + * @returns */ public static standardizeUrls(endpoints: ServiceEndpoint[]): ServiceEndpoint[] { const finalEndpoints = [] @@ -266,4 +267,4 @@ export default class StorageLink { return finalEndpoints } -} \ No newline at end of file +} diff --git a/packages/storage-link/test/storage-link.test.ts b/packages/storage-link/test/storage-link.test.ts index 369eeab9..37d784da 100644 --- a/packages/storage-link/test/storage-link.test.ts +++ b/packages/storage-link/test/storage-link.test.ts @@ -3,7 +3,6 @@ const assert = require('assert') import { StorageLink } from '../src/index' import { Keyring } from '@verida/keyring' -import EncryptionUtils from '@verida/encryption-utils' import { DIDDocument } from '@verida/did-document' import { Wallet } from 'ethers' import { getDIDClient } from './utils' @@ -11,13 +10,12 @@ import { DIDClient } from '@verida/did-client' import { Network, SecureContextConfig } from '@verida/types' import { CONTEXT_NAME } from './utils' -const NETWORK = Network.BANKSIA -const wallet = Wallet.createRandom() - function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } +const NETWORK = Network.BANKSIA +const wallet = Wallet.createRandom() console.log(wallet.mnemonic) const address = wallet.address.toLowerCase() const DID = `did:vda:polamoy:${address}` @@ -71,20 +69,22 @@ const expectedConfig: SecureContextConfig = { } const TEST_APP_NAME2 = 'Test App 2' -let didClient: DIDClient, keyring1: Keyring, keyring2: Keyring +let didClient: DIDClient +let keyring1: Keyring +let keyring2: Keyring -async function buildKeyring(did: string, contextName: string) { +async function buildKeyring(wallet: Wallet, did: string, contextName: string) { did = did.toLowerCase() const consentMessage = `Do you wish to unlock this storage context: "${contextName}"?\n\n${did}` - const signature = await EncryptionUtils.signData(consentMessage, Buffer.from(wallet.privateKey.substring(2), 'hex')) + const signature = await wallet.signMessage(consentMessage) return new Keyring(signature) } describe('Storage Link', () => { before(async () => { didClient = await getDIDClient(wallet) - keyring1 = await buildKeyring(DID, CONTEXT_NAME) - keyring2 = await buildKeyring(DID, TEST_APP_NAME2) + keyring1 = await buildKeyring(wallet, DID, CONTEXT_NAME) + keyring2 = await buildKeyring(wallet, DID, TEST_APP_NAME2) }) describe('Manage DID Links', async function() { @@ -99,14 +99,14 @@ describe('Storage Link', () => { let storageConfig = Object.assign({}, expectedConfig) - const success = await StorageLink.setLink(NETWORK, didClient, testConfig, keyring1, wallet.privateKey) + const success = await StorageLink.setLink(NETWORK, didClient, testConfig, keyring1, wallet) assert.ok(success, 'Set link succeeded') const links = await StorageLink.getLinks(NETWORK, didClient, DID) assert.ok(links.length, 1, 'Fetched exactly one saved link') const fetchedStorageConfig = await StorageLink.getLink(NETWORK, didClient, DID, testConfig.id) storageConfig.id = DIDDocument.generateContextHash(DID, CONTEXT_NAME) - + assert.deepStrictEqual(fetchedStorageConfig, storageConfig, 'Fetched storage config matches the expected storage config') }) @@ -114,7 +114,7 @@ describe('Storage Link', () => { await sleep(1000) let storageConfig = Object.assign({}, expectedConfig) storageConfig.id = TEST_APP_NAME2 - await StorageLink.setLink(NETWORK, didClient, storageConfig, keyring2, wallet.privateKey) + await StorageLink.setLink(NETWORK, didClient, storageConfig, keyring2, wallet) const fetchedStorageConfig = await StorageLink.getLink(NETWORK, didClient, DID, TEST_APP_NAME2) storageConfig.id = DIDDocument.generateContextHash(DID, TEST_APP_NAME2) From 7ee3251142845ca15611041936624a3c756f3894 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 11:48:53 +1100 Subject: [PATCH 03/12] Refactor client.ts and test files to improve code readability - Cleaned up whitespace and formatting in client.ts to enhance readability. - Removed unnecessary comments and ensured consistent formatting in test files. - Updated test files to maintain consistency with the latest code structure. --- packages/client-ts/src/client.ts | 34 +++++++++---------- .../test/client.contexthash.tests.ts | 6 ++-- .../client-ts/test/storage.endpoints.tests.ts | 9 ++--- .../vda-did-resolver/test/resolver.test.ts | 16 ++++----- 4 files changed, 29 insertions(+), 36 deletions(-) diff --git a/packages/client-ts/src/client.ts b/packages/client-ts/src/client.ts index 596f9155..bd738663 100644 --- a/packages/client-ts/src/client.ts +++ b/packages/client-ts/src/client.ts @@ -167,10 +167,10 @@ class Client implements IClient { } /** - * + * * @param contextName The name of the context OR a context hash (starting with 0x) - * @param did - * @returns + * @param did + * @returns */ public async openExternalContext(contextName: string, did: string): Promise { did = await this.parseDid(did) @@ -240,7 +240,7 @@ class Client implements IClient { if (networkFallback) { try { const profile = await this.openPublicProfile(did, contextName, profileName, fallbackContext) - + if (profile) { return profile.getMany({}, {}) } @@ -372,7 +372,7 @@ class Client implements IClient { // Get the DID document of this user // @ts-ignore - const didDocument = await this.didClient.get(this.did!) + const didDocument = await this.didClient.get(this.did!) const doc = didDocument.export() // Find all contexts for this account @@ -427,7 +427,7 @@ class Client implements IClient { } const endpointUris: ServiceEndpoint[] = endpointInfo!.serviceEndpoint - + // Delete context from all endpoints // For each endpoint; this deletes all context databases, plus the database that tracks all databases for a context const promises = [] @@ -436,7 +436,7 @@ class Client implements IClient { endpointUri = endpointUri.substring(0, endpointUri.length-1) // strip trailing slash const consentMessage = `Delete context (${contextName}) from server: "${endpointUri}"?\n\n${did}\n${timestamp}` const signature = await this.account!.sign(consentMessage) - + promises.push(Axios.post(`${endpointUri}/user/destroyContext`, { did, timestamp, @@ -474,7 +474,7 @@ class Client implements IClient { // Get the DID document of this user if (!didDocument) { // @ts-ignore - didDocument = await this.didClient.get(this.did!) + didDocument = await this.didClient.get(this.did!) } const services = didDocument.export().service! @@ -528,9 +528,9 @@ class Client implements IClient { /** * Converts a string that may be either a valid DID or a valid Verida username into * a Verida username. - * + * * @param didOrUsername DID string or Verida username string (ending in `.vda`) - * @returns + * @returns */ public async parseDid(didOrUsername: string): Promise { if (didOrUsername.match(/\.vda$/)) { @@ -538,15 +538,15 @@ class Client implements IClient { // @throws Error if the username doesn't exist return await this.getDID(didOrUsername) } - + return didOrUsername } /** * Get the DID linked to a username - * - * @param username - * @returns + * + * @param username + * @returns */ public async getDID(username: string): Promise { return await this.nameClient.getDID(username) @@ -554,9 +554,9 @@ class Client implements IClient { /** * Get an array of usernames linked to a DID - * - * @param did - * @returns + * + * @param did + * @returns */ public async getUsernames(did: string): Promise { return await this.nameClient.getUsernames(did) diff --git a/packages/client-ts/test/client.contexthash.tests.ts b/packages/client-ts/test/client.contexthash.tests.ts index cb3e24c8..0a1d97dc 100644 --- a/packages/client-ts/test/client.contexthash.tests.ts +++ b/packages/client-ts/test/client.contexthash.tests.ts @@ -2,10 +2,8 @@ const assert = require('assert') import { Client } from '../src/index' import { AutoAccount } from '@verida/account-node' -import { StorageLink } from '@verida/storage-link' import { DIDDocument } from '@verida/did-document' import CONFIG from './config' -import { EnvironmentType, IDatabase } from '@verida/types' const CONTEXT_NAME = 'Verida Storage Node Test: Test Application 1' @@ -25,7 +23,7 @@ describe.skip('Storage context hash tests', function() { network: CONFIG.NETWORK, } }) - + account = new AutoAccount({ privateKey: PRIVATE_KEY, network: CONFIG.NETWORK, @@ -54,4 +52,4 @@ describe.skip('Storage context hash tests', function() { } }) }) -}) \ No newline at end of file +}) diff --git a/packages/client-ts/test/storage.endpoints.tests.ts b/packages/client-ts/test/storage.endpoints.tests.ts index 08fa0d83..bac142b2 100644 --- a/packages/client-ts/test/storage.endpoints.tests.ts +++ b/packages/client-ts/test/storage.endpoints.tests.ts @@ -4,11 +4,8 @@ import { Client } from '../src/index' import { AutoAccount } from '@verida/account-node' import { StorageLink } from '@verida/storage-link' import { DIDDocument } from '@verida/did-document' -import Utils from "../src/context/engines/verida/database/utils"; -//import { Wallet } from 'ethers' import CONFIG from './config' import { sleep } from './utils' -import { EnvironmentType } from '@verida/types' import { getRandomInt } from '../src/context/utils' const TEST_DB_NAME = 'TestDb_3' @@ -208,7 +205,7 @@ describe.skip('Storage endpoint tests', () => { const randInt = getRandomInt(0, 1000000) const res = await secondaryConnection.post({'second': randInt}) await sleep(5*1000) - + const res1 = await primaryConnection.get(res.id) const res2 = await secondaryConnection.get(res.id) @@ -260,6 +257,6 @@ describe.skip('Storage endpoint tests', () => { }) // @todo: add a new endpoint, let it sync then perform the same tests across all the endpoints - + }) -}) \ No newline at end of file +}) diff --git a/packages/vda-did-resolver/test/resolver.test.ts b/packages/vda-did-resolver/test/resolver.test.ts index a763232a..19bd3336 100644 --- a/packages/vda-did-resolver/test/resolver.test.ts +++ b/packages/vda-did-resolver/test/resolver.test.ts @@ -10,13 +10,11 @@ import { BlockchainAnchor, VeridaDocInterface } from '@verida/types'; const wallet = ethers.Wallet.createRandom() -let DID_ADDRESS, DID, DID_PK, DID_PRIVATE_KEY, DID_TESTNET - -DID_ADDRESS = wallet.address -DID = `did:vda:polamoy:${DID_ADDRESS}` -DID_TESTNET = `did:vda:testnet:${DID_ADDRESS}` -DID_PK = wallet.publicKey -DID_PRIVATE_KEY = wallet.privateKey +const DID_ADDRESS = wallet.address +const DID = `did:vda:polamoy:${DID_ADDRESS}` +const DID_TESTNET = `did:vda:testnet:${DID_ADDRESS}` +const DID_PK = wallet.publicKey +const DID_PRIVATE_KEY = wallet.privateKey const KNOWN_MAINNET_ADDRESS = `0xCDEdd96AfA6956f0299580225C2d9a52aca8487A` @@ -53,7 +51,7 @@ describe("DID Resolver Tests", function() { this.beforeAll(async () => { // Create the test DID const doc = new DIDDocument(DID, DID_PK) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) masterDidDoc = doc const publishedEndpoints = await veridaApi.create(doc, ENDPOINTS) @@ -135,4 +133,4 @@ describe("DID Resolver Tests", function() { this.beforeAll(async () => { // @todo: delete the DID }) -}) \ No newline at end of file +}) From 46dfa744a07e1faa74fcc64eaa11ec2f32d90e7e Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 11:51:41 +1100 Subject: [PATCH 04/12] Refactor VdaDid and BlockchainApi to utilize Signer for signing operations - Updated VdaDid and BlockchainApi classes to replace privateKey with Signer for signing operations. - Modified related methods to ensure consistent use of Signer across the codebase. - Adjusted test files to reflect changes in the API and maintain compatibility with the new signing approach. --- packages/types/src/Web3Interfaces.ts | 10 +-- .../vda-did/src/blockchain/blockchainApi.ts | 47 ++++-------- packages/vda-did/src/blockchain/helpers.ts | 8 +- packages/vda-did/src/vdaDid.ts | 75 +++++++++---------- .../test/blockchain-api-mainnet-web3.test.ts | 16 ++-- .../blockchain-api-testnet-gasless.test.ts | 28 +++---- ...kchain-api-testnet-web3-gas-config.test.ts | 30 ++++---- .../test/blockchain-api-testnet-web3.test.ts | 26 +++---- packages/vda-did/test/vda-did.test.ts | 20 ++--- 9 files changed, 122 insertions(+), 138 deletions(-) diff --git a/packages/types/src/Web3Interfaces.ts b/packages/types/src/Web3Interfaces.ts index a405c9dd..73960070 100644 --- a/packages/types/src/Web3Interfaces.ts +++ b/packages/types/src/Web3Interfaces.ts @@ -98,11 +98,8 @@ export interface Web3GaslessPostConfig { */ export interface VdaDidConfigurationOptions { identifier: string; - signKey?: string; - signer?: (data: any) => Promise; - //chainNameOrId?: string | number; + signer: Signer; blockchain: BlockchainAnchor - callType: Web3CallType; web3Options: VeridaWeb3TransactionOptions; } @@ -115,8 +112,11 @@ export interface VdaDidEndpointResponse { export type VdaDidEndpointResponses = Record // Part of VeridaSelfTransactionConfig -export interface Web3SelfTransactionConfigPart { +export type Web3SelfTransactionConfigPart = { signer?: Signer // Pre-built transaction signer that is configured to pay for gas + privateKey: string // MATIC private key that will pay for gas +} | { + signer: Signer // Pre-built transaction signer that is configured to pay for gas privateKey?: string // MATIC private key that will pay for gas } diff --git a/packages/vda-did/src/blockchain/blockchainApi.ts b/packages/vda-did/src/blockchain/blockchainApi.ts index 2bc2d6b8..24f408f8 100644 --- a/packages/vda-did/src/blockchain/blockchainApi.ts +++ b/packages/vda-did/src/blockchain/blockchainApi.ts @@ -2,8 +2,7 @@ import { getContractInfoForBlockchainAnchor, interpretIdentifier } from "@verida import { getVeridaSignWithNonce } from "./helpers" import { VdaDidConfigurationOptions, Web3GasConfiguration, BlockchainAnchor, Web3SelfTransactionConfig } from "@verida/types" import { getVeridaContract, VeridaContract } from "@verida/web3" -import { ethers } from "ethers" -import EncryptionUtils from "@verida/encryption-utils" +import { ethers, Signer } from "ethers" import { getDefaultRpcUrl } from "@verida/vda-common" export interface LookupResponse { @@ -12,7 +11,6 @@ export interface LookupResponse { } export default class BlockchainApi { - private options: VdaDidConfigurationOptions private blockchain: BlockchainAnchor private didAddress : string @@ -22,17 +20,8 @@ export default class BlockchainApi { constructor(options: VdaDidConfigurationOptions) { this.options = options - if (!this.options.signKey && !this.options.signer) { - throw new Error(`Invalid configuration. 'signKey' or 'signer' must be specified`) - } - - if (this.options.signKey && !this.options.signer) { - this.options.signer = (data: any) => { - const privateKeyArray = new Uint8Array( - Buffer.from(options.signKey!.slice(2), 'hex') - ); - return Promise.resolve(EncryptionUtils.signData(data, privateKeyArray)) - } + if (!this.options.signer) { + throw new Error(`Invalid configuration. 'signer' must be specified`) } const { address } = interpretIdentifier(options.identifier) @@ -52,7 +41,7 @@ export default class BlockchainApi { } this.vdaWeb3Client = getVeridaContract( - options.callType, + options.callType, {...contractInfo, ...options.web3Options, blockchainAnchor: this.blockchain}); @@ -99,7 +88,7 @@ export default class BlockchainApi { ) { let rawMsg = ethers.utils.solidityPack(['address', 'string'], [this.didAddress.toLowerCase(), '/']); const nonce = await this.nonceFN() - + for (let i = 0; i < endpoints.length; i++) { rawMsg = ethers.utils.solidityPack( ['bytes', 'string', 'string'], @@ -107,7 +96,7 @@ export default class BlockchainApi { ); } - return await getVeridaSignWithNonce(rawMsg, this.options.signer!, nonce); + return await getVeridaSignWithNonce(rawMsg, this.options.signer, nonce); }; /** @@ -144,19 +133,19 @@ export default class BlockchainApi { ['address', 'string', 'address', 'string'], [this.didAddress, '/setController/', controller, '/'] ); - return await getVeridaSignWithNonce(rawMsg, this.options.signer!, await this.nonceFN()); + return await getVeridaSignWithNonce(rawMsg, this.options.signer, await this.nonceFN()); }; /** * Set a controller of the {@link BlockchainApi#didAddress} to the blockchain - * @param controllerPrivateKey private key of new controller + * @param newControllerSigner Signer of new controller */ - public async setController(controllerPrivateKey: string, gasConfig?: Web3GasConfiguration) { + public async setController(newControllerSigner: Signer, gasConfig?: Web3GasConfiguration) { if (!this.options.signer) { throw new Error(`Unable to create DID. No signer specified in config.`) } - const controllerAddress = ethers.utils.computeAddress(controllerPrivateKey).toLowerCase(); + const controllerAddress = ethers.utils.computeAddress(await newControllerSigner.getAddress()).toLowerCase(); const signature = await this.getControllerSignature(controllerAddress); @@ -176,12 +165,8 @@ export default class BlockchainApi { throw new Error('Failed to set controller'); } - this.options.signer = (data: any) => { - const privateKeyArray = new Uint8Array( - Buffer.from(controllerPrivateKey.slice(2), 'hex') - ); - return Promise.resolve(EncryptionUtils.signData(data, privateKeyArray)) - } + // FIXME: Consider another way. Risk the signer is updated everywhere the options is used, which may have undesired side effects + this.options.signer = newControllerSigner } public async getController() { @@ -203,7 +188,7 @@ export default class BlockchainApi { ['address', 'string'], [this.didAddress.toLowerCase(), '/revoke/'] ); - return await getVeridaSignWithNonce(rawMsg, this.options.signer!, await this.nonceFN()); + return await getVeridaSignWithNonce(rawMsg, this.options.signer, await this.nonceFN()); }; /** @@ -213,7 +198,7 @@ export default class BlockchainApi { if (!this.options.signer) { throw new Error(`Unable to create DID. No signer specified in config.`) } - + const signature = await this.getRevokeSignature(); let response: any; if (gasConfig !== undefined) { @@ -221,7 +206,7 @@ export default class BlockchainApi { } else { response = await this.vdaWeb3Client.revoke(this.didAddress, signature); } - + if (response.success !== true) { throw new Error('Failed to revoke'); } @@ -240,4 +225,4 @@ export default class BlockchainApi { return response.data.toNumber(); } -} \ No newline at end of file +} diff --git a/packages/vda-did/src/blockchain/helpers.ts b/packages/vda-did/src/blockchain/helpers.ts index 28b1debb..faaa24a8 100644 --- a/packages/vda-did/src/blockchain/helpers.ts +++ b/packages/vda-did/src/blockchain/helpers.ts @@ -1,10 +1,10 @@ -import {BigNumberish, ethers} from 'ethers'; +import {BigNumberish, ethers, Signer} from 'ethers'; export async function getVeridaSignWithNonce( rawMsg: string, - signer: (data: any) => Promise, + signer: Signer, nonce: BigNumberish ) { rawMsg = ethers.utils.solidityPack(['bytes', 'uint256'], [rawMsg, nonce]); - return signer(rawMsg) -} \ No newline at end of file + return signer.signMessage(rawMsg) +} diff --git a/packages/vda-did/src/vdaDid.ts b/packages/vda-did/src/vdaDid.ts index 40cc6acf..2ce90427 100644 --- a/packages/vda-did/src/vdaDid.ts +++ b/packages/vda-did/src/vdaDid.ts @@ -1,13 +1,11 @@ import Axios from 'axios' -import { ethers } from 'ethers' +import { ethers, Signer } from 'ethers' import { DIDDocument } from '@verida/did-document' -import EncryptionUtils from '@verida/encryption-utils' import BlockchainApi from "./blockchain/blockchainApi"; import { interpretIdentifier } from '@verida/vda-common' import { VdaDidConfigurationOptions, VdaDidEndpointResponses } from '@verida/types' export default class VdaDid { - private options: VdaDidConfigurationOptions private blockchain: BlockchainApi private lastEndpointErrors?: VdaDidEndpointResponses @@ -19,18 +17,18 @@ export default class VdaDid { /** * Publish the first version of a DIDDocument to a list of endpoints. - * + * * If an endpoint fails to accept the DID Document, that endpoint will be ignored and won't be included in the * list of valid endpoints on chain. - * - * @param didDocument - * @param endpoints + * + * @param didDocument + * @param endpoints * @return VdaDidEndpointResponses Map of endpoints where the DID Document was successfully published */ public async create(didDocument: DIDDocument, endpoints: string[], retries: number = 3): Promise { this.lastEndpointErrors = undefined - if (!this.options.signKey) { - throw new Error(`Unable to create DID: No private key specified in config.`) + if (!this.options.signer) { + throw new Error(`Unable to create DID: No signer specified in config.`) } const doc = didDocument.export() @@ -47,7 +45,7 @@ export default class VdaDid { } // Sign the DID Document - didDocument.signProof(this.options.signKey!) + didDocument.signProof(this.options.signer) // Submit to all the endpoints const promises = [] @@ -105,18 +103,18 @@ export default class VdaDid { /** * Publish an updated version of a DIDDocument to a list of endpoints. - * + * * If an endpoint fails to accept the DID Document, that will be reflected in the response. - * + * * Note: Any failed endpoints will remain on-chain and will need to have the update re-attempted or remove the endpoint from the DID Registry - * - * @param didDocument + * + * @param didDocument * @returns VdaDidEndpointResponses Map of endpoints where the DID Document was successfully published */ - public async update(didDocument: DIDDocument, controllerPrivateKey?: string): Promise { + public async update(didDocument: DIDDocument, controllerSigner?: Signer): Promise { this.lastEndpointErrors = undefined - if (!this.options.signKey) { - throw new Error(`Unable to update DID Document. No private key specified in config.`) + if (!this.options.signer) { + throw new Error(`Unable to update DID Document. No signer specified in config.`) } const attributes = didDocument.export() @@ -124,7 +122,7 @@ export default class VdaDid { throw new Error(`Unable to update DID Document. "updated" timestamp matches "created" timestamp.`) } - didDocument.signProof(this.options.signKey) + didDocument.signProof(this.options.signer) // Fetch the endpoint list from the blockchain const response: any = await this.blockchain.lookup(didDocument.id) @@ -140,13 +138,13 @@ export default class VdaDid { // @ts-ignore if (currentController !== didDocumentController) { // Controller has changed, ensure we have a private key - if (!controllerPrivateKey) { - throw new Error(`Unable to update DID Document. Changing controller, but "controllerPrivateKey" not specified.`) + if (!controllerSigner) { + throw new Error(`Unable to update DID Document. Changing controller, but "controllerSigner" not specified.`) } // Ensure new controller in the DID Document matches the private key - const privateKeyAddress = ethers.utils.computeAddress(controllerPrivateKey).toLowerCase() - if (privateKeyAddress !== didDocumentController) { + const newControllerAddress = ethers.utils.computeAddress(await controllerSigner.getAddress()).toLowerCase() + if (newControllerAddress !== didDocumentController) { throw new Error(`Unable to update DID Document. Changing controller, but private key doesn't match controller in DID Document`) } @@ -199,7 +197,7 @@ export default class VdaDid { // If the controller doesn't match the DID, the controller may have changed if (updateController) { // If the DID controller has changed, update on-chain via `setController()` - await this.blockchain.setController(controllerPrivateKey!) + await this.blockchain.setController(controllerSigner!) } return finalEndpoints @@ -210,8 +208,8 @@ export default class VdaDid { const did = this.options.identifier.toLowerCase() const nowInMinutes = Math.round((new Date()).getTime() / 1000 / 60) const proofString = `Delete DID Document ${did} at ${nowInMinutes}` - const privateKey = new Uint8Array(Buffer.from(this.options.signKey!.substr(2),'hex')) - const signature = EncryptionUtils.signData(proofString, privateKey) + + const signature = await this.options.signer.signMessage(proofString) // Delete DID Document from all the endpoints const promises = [] @@ -254,8 +252,9 @@ export default class VdaDid { } public async delete(): Promise { - if (!this.options.signKey) { - throw new Error(`Unable to delete DID. No private key specified in config.`) + if (!this.options.signer) { + // Is it really necessary? The signer is not used in this function + throw new Error(`Unable to delete DID. No signer specified in config.`) } const did = this.options.identifier.toLowerCase() @@ -283,13 +282,14 @@ export default class VdaDid { /** * Add a new to an existing DID - * - * @param endpointUri - * @param verifyAllVersions + * + * @param endpointUri + * @param verifyAllVersions */ public async addEndpoint(endpointUri: string, verifyAllVersions=false) { - if (!this.options.signKey) { - throw new Error(`Unable to create DID. No private key specified in config.`) + if (!this.options.signer) { + // Is it really necessary? The signer is not used in this function + throw new Error(`Unable to add endpoint. No signer specified in config.`) } // 1. Fetch all versions of the DID @@ -304,7 +304,7 @@ export default class VdaDid { // 2. Call /migrate on the new endpoint // @todo: generate signature - const proofString = '' + const proofString = '' const signature = '' try { const response = await Axios.post(`${endpointUri}/migrate`, { @@ -328,10 +328,11 @@ export default class VdaDid { // @todo: Implement public async removeEndpoint(did: string, endpoint: string) { - if (!this.options.signKey) { + if (!this.options.signer) { + // Is it really necessary? The signer is not used in this function throw new Error(`Unable to create DID. No private key specified in config.`) } - + // @todo } @@ -340,8 +341,6 @@ export default class VdaDid { } private async fetchDocumentHistory(endpoints: string[]): Promise { - const documents: DIDDocument[] = [] - const endpointVersions: any = {} for (let i in endpoints) { const endpointUri = endpoints[i] @@ -367,4 +366,4 @@ export default class VdaDid { return endpointVersions[endpoints[0]] } -} \ No newline at end of file +} diff --git a/packages/vda-did/test/blockchain-api-mainnet-web3.test.ts b/packages/vda-did/test/blockchain-api-mainnet-web3.test.ts index 7f4c9d90..edcd6de2 100644 --- a/packages/vda-did/test/blockchain-api-mainnet-web3.test.ts +++ b/packages/vda-did/test/blockchain-api-mainnet-web3.test.ts @@ -28,10 +28,10 @@ const configuration = { } } -const createBlockchainAPI = (did: any) => { - return new BlockchainApi({ - identifier: `did:vda:${BlockchainAnchor.POLPOS}:${did.address}`, - signKey: did.privateKey, +const createBlockchainAPI = (wallet: Wallet) => { + return new BlockchainApi({ + identifier: `did:vda:${BlockchainAnchor.POLPOS}:${wallet.address}`, + signer: wallet, blockchain: BlockchainAnchor.POLPOS, ...configuration }) @@ -51,7 +51,7 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, + lookupResult, {didController: didWallet.address, endpoints: endPoints_A}, 'Get same endpoints'); }) @@ -61,8 +61,8 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, - {didController: didWallet.address, endpoints: endPoints_B}, + lookupResult, + {didController: didWallet.address, endpoints: endPoints_B}, 'Get updated endpoints'); }) @@ -82,4 +82,4 @@ describe('vda-did blockchain api', () => { ) }) }) -}) \ No newline at end of file +}) diff --git a/packages/vda-did/test/blockchain-api-testnet-gasless.test.ts b/packages/vda-did/test/blockchain-api-testnet-gasless.test.ts index a30346df..f6f185ba 100644 --- a/packages/vda-did/test/blockchain-api-testnet-gasless.test.ts +++ b/packages/vda-did/test/blockchain-api-testnet-gasless.test.ts @@ -19,7 +19,7 @@ if (!privateKey) { const PORT = process.env.SERVER_PORT ? process.env.SERVER_PORT : 5021; const SERVER_URL = `http://localhost:${PORT}`; -const configuration = { +const configuration = { // TODO: Add strong type to configuration callType: 'gasless', web3Options: { serverConfig: { @@ -36,10 +36,10 @@ const configuration = { } } -const createBlockchainAPI = (did: any, blockchain: BlockchainAnchor) => { - return new BlockchainApi({ - identifier: `did:vda:${blockchain}:${did.address}`, - signKey: did.privateKey, +const createBlockchainAPI = (wallet: Wallet, blockchain: BlockchainAnchor) => { + return new BlockchainApi({ + identifier: `did:vda:${blockchain}:${wallet.address}`, + signer: wallet, blockchain, ...configuration }) @@ -59,7 +59,7 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, + lookupResult, {didController: didWallet.address, endpoints: endPoints_A}, 'Get same endpoints'); }) @@ -69,8 +69,8 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, - {didController: didWallet.address, endpoints: endPoints_B}, + lookupResult, + {didController: didWallet.address, endpoints: endPoints_B}, 'Get updated endpoints'); }) @@ -96,8 +96,8 @@ describe('vda-did blockchain api', () => { it('Get endpoints successfully', async () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, - {didController:didWallet.address, endpoints:endPoints_B}, + lookupResult, + {didController:didWallet.address, endpoints:endPoints_B}, 'Get updated endpoints'); }) @@ -136,7 +136,7 @@ describe('vda-did blockchain api', () => { it('Should reject for unregistered DID', async () => { const testAPI = createBlockchainAPI(Wallet.createRandom(), BlockchainAnchor.POLAMOY); await assert.rejects( - testAPI.setController(controller.privateKey), + testAPI.setController(controller), (err) => { assert.ok(err.message.startsWith('Failed to set controller')); return true; @@ -152,12 +152,12 @@ describe('vda-did blockchain api', () => { const orgController = await testAPI.getController(); assert.equal(orgController, orgDID.address, 'Controller itself'); - await testAPI.setController(controller.privateKey); + await testAPI.setController(controller); const newController = await testAPI.getController(); assert.equal(newController, controller.address, 'Updated controller'); // Restore controller - await testAPI.setController(orgDID.privateKey); + await testAPI.setController(orgDID); }) }) @@ -190,4 +190,4 @@ describe('vda-did blockchain api', () => { ); }) }) -}) \ No newline at end of file +}) diff --git a/packages/vda-did/test/blockchain-api-testnet-web3-gas-config.test.ts b/packages/vda-did/test/blockchain-api-testnet-web3-gas-config.test.ts index 862c86ab..ed8cb1fc 100644 --- a/packages/vda-did/test/blockchain-api-testnet-web3-gas-config.test.ts +++ b/packages/vda-did/test/blockchain-api-testnet-web3-gas-config.test.ts @@ -15,23 +15,23 @@ if (!privateKey) { throw new Error('No PRIVATE_KEY in the env file'); } -const createBlockchainAPI = (didWallet: any, blockchain: BlockchainAnchor, configuration:any) => { - return new BlockchainApi({ - identifier: `did:vda:${blockchain}:${didWallet.address}`, - signKey: didWallet.privateKey, +const createBlockchainAPI = (wallet: Wallet, blockchain: BlockchainAnchor, configuration:any) => { // TODO: Add strong type to configuration + return new BlockchainApi({ + identifier: `did:vda:${blockchain}:${wallet.address}`, + signer: wallet, blockchain, ...configuration }) } -const checkResult =async (configuration: any, isSuccess = true, errMsg : string | undefined = undefined) => { +const checkResult =async (configuration: any, isSuccess = true, errMsg : string | undefined = undefined) => { // TODO: Add strong type to configuration const blockchainApi = createBlockchainAPI(didWallet, testChain, configuration); if (isSuccess) { await blockchainApi.register(endPoints_A); const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, + lookupResult, {didController: didWallet.address, endpoints: endPoints_A}, 'Get same endpoints'); } else { @@ -46,13 +46,13 @@ const checkResult =async (configuration: any, isSuccess = true, errMsg : string } else { assert.throws(f, Error); } - + } } } const checkGlobalGasConfig = async (gasOption: Record, isSuccess = true, errMsg : string | undefined = undefined) => { - const configuration = { + const configuration = { // TODO: Add strong type to configuration callType: 'web3', web3Options: { privateKey, @@ -63,7 +63,7 @@ const checkGlobalGasConfig = async (gasOption: Record, isSuccess = } const checkMethodDefaultGasConfig = async (gasOption: Record, isSuccess = true, errMsg : string | undefined = undefined) => { - const configuration = { + const configuration = { // TODO: Add strong type to configuration callType: 'web3', web3Options: { privateKey, @@ -80,7 +80,7 @@ const checkRuntimeGasConfig = async (blockchainApi:BlockchainApi, gasOption: Rec await blockchainApi.register(endPoints_A, gasOption); const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, + lookupResult, {didController: didWallet.address, endpoints: endPoints_A}, 'Get same endpoints'); } else { @@ -95,7 +95,7 @@ const checkRuntimeGasConfig = async (blockchainApi:BlockchainApi, gasOption: Rec } else { assert.throws(f, Error); } - + } } } @@ -116,7 +116,7 @@ describe('vda-did blockchain api test for different gas configurations', functio gasOption = { eip1559gasStationUrl: 'https://gasstation.polygon.technology/amoy' // eip1559gasStationUrl: 'https://gasstation.polygon.technology/amoy' - + } await checkGlobalGasConfig(gasOption, false, 'To use the station gas configuration, need to specify eip1559Mode & eip1559gasStationUrl'); }) @@ -130,7 +130,7 @@ describe('vda-did blockchain api test for different gas configurations', functio eip1559gasStationUrl: 'https://gasstation-testnet.polygon.technology/v2' } await checkGlobalGasConfig(gasOption, true); - } + } }) }) @@ -187,7 +187,7 @@ describe('vda-did blockchain api test for different gas configurations', functio } await checkMethodDefaultGasConfig(gasOption, true); }) - }) + }) }) describe('Gas configuration at runtime', function() { @@ -229,4 +229,4 @@ describe('vda-did blockchain api test for different gas configurations', functio }) }) -}) \ No newline at end of file +}) diff --git a/packages/vda-did/test/blockchain-api-testnet-web3.test.ts b/packages/vda-did/test/blockchain-api-testnet-web3.test.ts index 60cf69dc..317a0068 100644 --- a/packages/vda-did/test/blockchain-api-testnet-web3.test.ts +++ b/packages/vda-did/test/blockchain-api-testnet-web3.test.ts @@ -26,10 +26,10 @@ const configuration = { } } -const createBlockchainAPI = (did: any, blockchain: BlockchainAnchor = testChain) => { - return new BlockchainApi({ - identifier: `did:vda:${blockchain}:${did.address}`, - signKey: did.privateKey, +const createBlockchainAPI = (wallet: Wallet, blockchain: BlockchainAnchor = testChain) => { + return new BlockchainApi({ + identifier: `did:vda:${blockchain}:${wallet.address}`, + signer: wallet, blockchain, ...configuration }) @@ -49,7 +49,7 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, + lookupResult, {didController: didWallet.address, endpoints: endPoints_A}, 'Get same endpoints'); }) @@ -59,8 +59,8 @@ describe('vda-did blockchain api', () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, - {didController: didWallet.address, endpoints: endPoints_B}, + lookupResult, + {didController: didWallet.address, endpoints: endPoints_B}, 'Get updated endpoints'); }) @@ -86,8 +86,8 @@ describe('vda-did blockchain api', () => { it('Get endpoints successfully', async () => { const lookupResult = await blockchainApi.lookup(did); assert.deepEqual( - lookupResult, - {didController:didWallet.address, endpoints:endPoints_B}, + lookupResult, + {didController:didWallet.address, endpoints:endPoints_B}, 'Get updated endpoints'); }) @@ -126,7 +126,7 @@ describe('vda-did blockchain api', () => { it('Should reject for unregistered DID', async () => { const testAPI = createBlockchainAPI(Wallet.createRandom()); await assert.rejects( - testAPI.setController(controller.privateKey), + testAPI.setController(controller), err => { assert.ok(err.message.startsWith('Failed to set controller')); return true; @@ -142,12 +142,12 @@ describe('vda-did blockchain api', () => { const orgController = await testAPI.getController(); assert.equal(orgController, orgDID.address, 'Controller itself'); - await testAPI.setController(controller.privateKey); + await testAPI.setController(controller); const newController = await testAPI.getController(); assert.equal(newController, controller.address, 'Updated controller'); // Restore controller - await testAPI.setController(orgDID.privateKey); + await testAPI.setController(orgDID); }) }) @@ -180,4 +180,4 @@ describe('vda-did blockchain api', () => { ); }) }) -}) \ No newline at end of file +}) diff --git a/packages/vda-did/test/vda-did.test.ts b/packages/vda-did/test/vda-did.test.ts index ee18cda0..e02602f6 100644 --- a/packages/vda-did/test/vda-did.test.ts +++ b/packages/vda-did/test/vda-did.test.ts @@ -83,7 +83,7 @@ describe("VdaDid tests", function() { created: doc.buildTimestamp(NOW), updated: doc.buildTimestamp(NOW) }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) await veridaApi.create(doc, ENDPOINTS) @@ -92,7 +92,7 @@ describe("VdaDid tests", function() { assert.equal(err.message, `Unable to create DID: Blockchain in address doesn't match config`) } }) - + it("Success", async () => { try { const doc = new DIDDocument(DID, DID_PK) @@ -100,7 +100,7 @@ describe("VdaDid tests", function() { created: doc.buildTimestamp(NOW), updated: doc.buildTimestamp(NOW), }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) masterDidDoc = doc const publishedEndpoints = await veridaApi.create(doc, ENDPOINTS) @@ -121,7 +121,7 @@ describe("VdaDid tests", function() { created: doc.buildTimestamp(NOW), updated: doc.buildTimestamp(NOW) }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) await veridaApi.create(doc, ENDPOINTS) assert.fail(`Document created, when it shouldn't`) @@ -153,7 +153,7 @@ describe("VdaDid tests", function() { doc.setAttributes({ updated: doc.buildTimestamp(LATER) }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) await veridaApi.update(doc) assert.fail(`Document updated, when it shouldn't`) @@ -172,7 +172,7 @@ describe("VdaDid tests", function() { versionId: 3, updated: doc.buildTimestamp(LATER) }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) await veridaApi.update(doc) assert.fail(`Document updated, when it shouldn't`) @@ -191,7 +191,7 @@ describe("VdaDid tests", function() { versionId: 1, updated: doc.buildTimestamp(NOW), }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) await veridaApi.update(doc) assert.fail(`Document updated, when it shouldn't`) @@ -203,7 +203,7 @@ describe("VdaDid tests", function() { it("Fail - 1/n endpoints fail", async () => { try { const doc = new DIDDocument(DID, DID_PK) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) doc.setAttributes({ updated: doc.buildTimestamp(NOW)+1 }) @@ -222,7 +222,7 @@ describe("VdaDid tests", function() { versionId: 1, updated: doc.buildTimestamp(LATER) }) - doc.signProof(wallet.privateKey) + doc.signProof(wallet) // Verify update response is correct const response = await veridaApi.update(doc) @@ -272,4 +272,4 @@ describe("VdaDid tests", function() { // @todo Success }) -}) \ No newline at end of file +}) From 3371ec5e6d548104ab52ca4cb11b27a67ae29d88 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 15:51:21 +1100 Subject: [PATCH 05/12] Refactor DID client and wallet implementation to use VeridaDidWallet - Replaced instances of the deprecated Wallet class with VeridaDidWallet in create-account and get-account-info commands. - Updated DIDClient to utilize VeridaDidWallet for managing DID identifiers and signing operations. - Introduced utility functions for building Verida DID identifiers and added a new VeridaDidWallet class for enhanced wallet management. - Adjusted tests to reflect changes in the wallet implementation and ensure compatibility with the new structure. --- .../cli-tools/src/commands/create-account.ts | 8 +- .../src/commands/get-account-info.ts | 6 +- packages/client-ts/src/client.ts | 4 +- packages/did-client/package.json | 1 + packages/did-client/src/did-client.ts | 88 ++++++++------- packages/did-client/src/index.ts | 11 +- packages/did-client/src/utils.ts | 12 +++ packages/did-client/src/verida-did-wallet.ts | 100 ++++++++++++++++++ packages/did-client/src/wallet.ts | 8 +- packages/did-client/test/did-client.tests.ts | 49 +++++---- packages/did-client/test/utils.ts | 37 ++++--- packages/storage-link/test/did.test.ts | 23 ++-- packages/storage-link/test/utils.ts | 38 +++---- packages/types/src/IDIDClient.ts | 17 +-- packages/vda-common/src/defaults.ts | 16 ++- .../vda-did-resolver/test/resolver.test.ts | 4 +- 16 files changed, 270 insertions(+), 152 deletions(-) create mode 100644 packages/did-client/src/utils.ts create mode 100644 packages/did-client/src/verida-did-wallet.ts diff --git a/packages/cli-tools/src/commands/create-account.ts b/packages/cli-tools/src/commands/create-account.ts index 77a34350..ccc0e96f 100644 --- a/packages/cli-tools/src/commands/create-account.ts +++ b/packages/cli-tools/src/commands/create-account.ts @@ -2,7 +2,7 @@ import { Command } from 'command-line-interface'; import { CreateAccountOptions } from './interfaces'; import { AutoAccount } from '@verida/account-node'; import { Network } from '@verida/types'; -import { Wallet } from '@verida/did-client'; +import { VeridaDidWallet } from '@verida/did-client'; import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common'; import { NETWORK_STRINGS } from '../constants'; import { ethers } from 'ethers'; @@ -34,7 +34,7 @@ export const CreateAccount: Command = { } ], async handle ({ options }) { - const network = options.network + const network = options.network const randomWallet = ethers.Wallet.createRandom() const mnemonic = randomWallet.mnemonic!.phrase @@ -66,9 +66,9 @@ export const CreateAccount: Command = { } const blockchain = DefaultNetworkBlockchainAnchors[network] - const wallet = new Wallet(randomWallet.privateKey, blockchain.toString()) + const wallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(mnemonic, blockchain) console.log(`Wallet mnemonic: ${mnemonic}`) console.log(`Wallet private key: ${wallet.privateKey}`) console.log(`Wallet public key: ${wallet.publicKey}`) } - }; \ No newline at end of file +}; diff --git a/packages/cli-tools/src/commands/get-account-info.ts b/packages/cli-tools/src/commands/get-account-info.ts index f1640986..040128a3 100644 --- a/packages/cli-tools/src/commands/get-account-info.ts +++ b/packages/cli-tools/src/commands/get-account-info.ts @@ -2,7 +2,7 @@ import { Command } from 'command-line-interface'; import { GetAccountInfoOptions } from './interfaces'; import { AutoAccount } from '@verida/account-node'; import { Network } from '@verida/types'; -import { Wallet } from '@verida/did-client'; +import { VeridaDidWallet } from '@verida/did-client'; import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common'; import { NETWORK_STRINGS } from '../constants'; require('dotenv').config() @@ -51,8 +51,8 @@ export const GetAccountInfo: Command = { console.log(`DID: ${did}`) const blockchain = DefaultNetworkBlockchainAnchors[network] - const wallet = new Wallet(options.privateKey, blockchain.toString()) + const wallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(options.privateKey, blockchain) console.log(`Wallet private key: ${wallet.privateKey}`) console.log(`Wallet public key: ${wallet.publicKey}`) } - }; \ No newline at end of file + }; diff --git a/packages/client-ts/src/client.ts b/packages/client-ts/src/client.ts index bd738663..9821725a 100644 --- a/packages/client-ts/src/client.ts +++ b/packages/client-ts/src/client.ts @@ -333,14 +333,14 @@ class Client implements IClient { const signerDid = `did:vda:${sNetwork}:${sDid}`; - if (!did || signerDid.toLowerCase() == did.toLowerCase()) { + if (!did || signerDid.toLowerCase() === did.toLowerCase()) { const didDocument = await this.didClient.get(signerDid); if (!didDocument) { continue; } // Support old signature format (simple string) and new signature format (object) - const matchSig = typeof(signature) == 'string' ? signature : signature['secp256k1'] + const matchSig = typeof(signature) === 'string' ? signature : signature['secp256k1'] const validSig = didDocument.verifyContextSignature( _data, diff --git a/packages/did-client/package.json b/packages/did-client/package.json index 6ce7fae9..b1c7cb79 100644 --- a/packages/did-client/package.json +++ b/packages/did-client/package.json @@ -20,6 +20,7 @@ "@verida/did-document": "^4.4.1", "@verida/types": "^4.4.0", "@verida/vda-common": "^4.4.0", + "@verida/vda-did": "^4.4.1", "@verida/vda-did-resolver": "^4.4.2-4.4.2-pr1.0", "@verida/web3": "^4.4.0", "axios": "^0.23.0", diff --git a/packages/did-client/src/did-client.ts b/packages/did-client/src/did-client.ts index 53cd9f55..f79ae2a3 100644 --- a/packages/did-client/src/did-client.ts +++ b/packages/did-client/src/did-client.ts @@ -1,10 +1,11 @@ import { DIDDocument as VeridaDIDDocument } from "@verida/did-document" -import { default as VeridaWallet } from "./wallet" +import { VeridaDidWallet } from "./verida-did-wallet" import { getResolver } from '@verida/vda-did-resolver' import { getWeb3ConfigDefaults, getDefaultRpcUrl, DefaultNetworkBlockchainAnchors } from "@verida/vda-common" import { VdaDid } from '@verida/vda-did' import { Resolver } from 'did-resolver' -import { Web3CallType, DIDClientConfig, VdaDidEndpointResponses, Web3ResolverConfigurationOptions, Web3SelfTransactionConfig, Web3MetaTransactionConfig, VeridaWeb3TransactionOptions, Web3SelfTransactionConfigPart, IDIDClient, VeridaDocInterface, BlockchainAnchor, Network } from "@verida/types" +import { Web3CallType, DIDClientConfig, VdaDidEndpointResponses, Web3ResolverConfigurationOptions, Web3SelfTransactionConfig, Web3MetaTransactionConfig, VeridaWeb3TransactionOptions, IDIDClient, VeridaDocInterface, BlockchainAnchor } from "@verida/types" +import { Signer } from "ethers" export class DIDClient implements IDIDClient { @@ -18,7 +19,7 @@ export class DIDClient implements IDIDClient { private vdaDid?: VdaDid // Verida Wallet Info - private veridaWallet: VeridaWallet | undefined + private veridaDidWallet: VeridaDidWallet | undefined private defaultEndpoints?: string[] @@ -37,7 +38,7 @@ export class DIDClient implements IDIDClient { const resolverConfig: Web3ResolverConfigurationOptions = { timeout: config.timeout ? config.timeout : 10000 } - + resolverConfig.rpcUrl = this.getRpcUrl() const vdaDidResolver = getResolver(resolverConfig) @@ -56,52 +57,53 @@ export class DIDClient implements IDIDClient { /** * Unlock save() function by providing verida signing key. - * - * @param veridaPrivateKey Private key of a Verida Account. Used to sign transactions in the DID Registry to verify the request originated from the DID owner / controller + * + * @param signer Signer instance * @param callType Blockchain interaction mode. 'web3' | 'gasless' * @param web3Config Web3 configuration. If `web3`, you must provide `privateKey` (MATIC private key that will pay for gas). If `gasless` you must specify `endpointUrl` (URL of the meta transaction server) and any appropriate `serverConfig` and `postConfig`. */ - public authenticate( - veridaPrivateKey: string, + public async authenticate( + signer: Signer, callType: Web3CallType, - web3Config: Web3SelfTransactionConfigPart | Web3MetaTransactionConfig, + web3Config: Web3SelfTransactionConfig | Web3MetaTransactionConfig, defaultEndpoints: string[] ) { this.defaultEndpoints = defaultEndpoints - this.veridaWallet = new VeridaWallet(veridaPrivateKey, this.blockchainAnchor.toString()) + this.veridaDidWallet = await VeridaDidWallet.fromSigner(signer, this.blockchainAnchor) // @ts-ignore - if (callType == 'gasless' && !web3Config.endpointUrl) { + if (callType === 'gasless' && !web3Config.endpointUrl) { throw new Error('Gasless transactions must specify `web3config.endpointUrl`') } // @ts-ignore - if (callType == 'web3' && !web3Config.privateKey) { + if (callType === 'web3' && !web3Config.privateKey) { // TODO: Also support signer throw new Error('Web3 transactions must specify `web3config.privateKey`') } - web3Config = { - ...getWeb3ConfigDefaults(this.blockchainAnchor), - ...web3Config + const web3ConfigDefaults = getWeb3ConfigDefaults(this.blockchainAnchor) + + const web3SelfTransactionConfig: Web3SelfTransactionConfig = { + ...web3ConfigDefaults, + ...web3Config, + rpcUrl: (web3Config as Web3SelfTransactionConfig).rpcUrl ?? web3ConfigDefaults?.rpcUrl ?? this.config.rpcUrl ?? undefined } + const web3MetaTransactionConfig = web3Config as Web3MetaTransactionConfig + // @ts-ignore - let rpcUrl = web3Config.rpcUrl || this.config.rpcUrl - if (callType == 'web3' && !rpcUrl) { + if (callType == 'web3' && !web3SelfTransactionConfig.rpcUrl) { throw new Error('Web3 transactions must specify `web3config.rpcUrl`') } - const _web3Config: VeridaWeb3TransactionOptions = callType === 'gasless' ? - web3Config : - { - ...web3Config, - rpcUrl - } + const _web3Config = callType === 'gasless' ? + web3MetaTransactionConfig : + web3SelfTransactionConfig this.vdaDid = new VdaDid({ - identifier: this.veridaWallet.did, - signKey: this.veridaWallet.privateKey, + identifier: this.veridaDidWallet.did, + signer: this.veridaDidWallet.signer, blockchain: this.blockchainAnchor, callType: callType, web3Options: _web3Config @@ -109,38 +111,34 @@ export class DIDClient implements IDIDClient { } public authenticated(): boolean { - return this.veridaWallet !== undefined + return this.veridaDidWallet !== undefined } - + public getDid(): string | undefined { // Add the network into the DID, if not specified - if (this.veridaWallet === undefined) { + if (!this.veridaDidWallet) { return undefined } - - if (this.veridaWallet.did.substring(0,10) == 'did:vda:0x') { - return this.veridaWallet.did.replace(`did:vda:`, `did:vda:${this.blockchainAnchor.toString()}:`) + + if (this.veridaDidWallet.did.substring(0,10) === 'did:vda:0x') { + return this.veridaDidWallet.did.replace(`did:vda:`, `did:vda:${this.blockchainAnchor.toString()}:`) } - return this.veridaWallet.did + return this.veridaDidWallet.did } - - public getPublicKey(): string | undefined { - if (this.veridaWallet !== undefined) { - return this.veridaWallet.publicKey - } - return undefined + public getPublicKey(): string | undefined { + return this.veridaDidWallet?.publicKey } /** * Destroy this DID - * + * * Note: This can not be reversed and is written to the blockchain */ public async destroy(): Promise { if (!this.authenticated()) { - throw new Error("Unable to save DIDDocument. No private key.") + throw new Error("Unable to destroy the DID document. Not authenticated.") } return await this.vdaDid!.delete() @@ -148,13 +146,13 @@ export class DIDClient implements IDIDClient { /** * Save DIDDocument to the chain - * + * * @param document Updated DIDDocuent * @returns true if success. */ public async save(document: VeridaDIDDocument): Promise { if (!this.authenticated()) { - throw new Error("Unable to save DIDDocument. No private key.") + throw new Error("Unable to save the DID document. Not authenticated.") } // Fetch the existing doc. This creates a new, empty doc if not found @@ -199,7 +197,7 @@ export class DIDClient implements IDIDClient { }) try { - endpointResponse = await this.vdaDid!.update(document, this.veridaWallet!.privateKey) + endpointResponse = await this.vdaDid!.update(document, this.veridaDidWallet!.signer) } catch (err: any) { if (err.message == 'Unable to update DID: All endpoints failed to accept the DID Document') { this.endpointErrors = this.vdaDid!.getLastEndpointErrors() @@ -218,7 +216,7 @@ export class DIDClient implements IDIDClient { /** * Get original document loaded from blockchain. Creates a new document if it didn't exist - * + * * @returns DID Document instance */ public async get(did: string): Promise { @@ -230,4 +228,4 @@ export class DIDClient implements IDIDClient { return new VeridaDIDDocument( resolutionResult.didDocument) } -} \ No newline at end of file +} diff --git a/packages/did-client/src/index.ts b/packages/did-client/src/index.ts index 7ee4be7a..d136e46f 100644 --- a/packages/did-client/src/index.ts +++ b/packages/did-client/src/index.ts @@ -1,7 +1,4 @@ -import { DIDClient } from "./did-client" -import Wallet from "./wallet" - -export { - DIDClient, - Wallet -} \ No newline at end of file +export * from './utils' +export * from './verida-did-wallet' +export * from './wallet' +export * from './did-client' diff --git a/packages/did-client/src/utils.ts b/packages/did-client/src/utils.ts new file mode 100644 index 00000000..b09d198b --- /dev/null +++ b/packages/did-client/src/utils.ts @@ -0,0 +1,12 @@ +import { BlockchainAnchor } from "@verida/types"; + +/** + * Build a Verida DID identifier from a blockchain anchor and address + * + * @param blockchainAnchor - The blockchain network identifier (eg: 'testnet', 'mainnet') + * @param address - Ethereum address to build the DID for + * @returns A properly formatted Verida DID string (eg: 'did:vda:polpos:0x...') + */ +export function buildVeridaDidIdentifier(blockchainAnchor: BlockchainAnchor, address: string): string { + return `did:vda:${blockchainAnchor}:${address}` +} diff --git a/packages/did-client/src/verida-did-wallet.ts b/packages/did-client/src/verida-did-wallet.ts new file mode 100644 index 00000000..f6b9ecb7 --- /dev/null +++ b/packages/did-client/src/verida-did-wallet.ts @@ -0,0 +1,100 @@ +import { BlockchainAnchor } from "@verida/types" +import { utils, Wallet, Signer } from "ethers" +import { buildVeridaDidIdentifier } from "./utils" + +/** + * A wallet class that manages Verida DID identifiers and associated keys + */ +export class VeridaDidWallet { + /** The DID string identifier */ + public did: string + /** The blockchain network this DID is anchored to */ + public blockchainAnchor: BlockchainAnchor + /** The wallet address */ + public address: string + /** Signer instance used for signing messages */ + public signer: Signer + /** Optional private key, unavailable if created from a signer */ + public privateKey: string | undefined + + /** + * The constructor is intentionally private, use the static methods to create instances + * + * @param signer - Signer instance for signing messages + * @param blockchainAnchor - Blockchain network to anchor the DID + * @param address - Wallet address + * @param privateKey - Optional private key + */ + private constructor(signer: Signer, blockchainAnchor: BlockchainAnchor, address: string, privateKey: string | undefined) { + this.did = buildVeridaDidIdentifier(blockchainAnchor, address) + this.blockchainAnchor = blockchainAnchor + this.address = address + this.signer = signer + this.privateKey = privateKey + } + + /** + * Create a new random wallet + * + * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @returns New VeridaDidWallet instance + */ + public static createRandom(blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + const wallet = Wallet.createRandom() + return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.privateKey) + } + + /** + * Create a wallet from an existing signer + * + * @param signer - Signer instance to use + * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @returns New VeridaDidWallet instance + */ + public static async fromSigner(signer: Signer, blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + const address = await signer.getAddress() + return new VeridaDidWallet(signer, blockchainAnchor, address, undefined) + } + + /** + * Create a wallet from a private key or mnemonic phrase + * + * @param privateKeyOrMnemonic - Private key (0x prefixed) or mnemonic phrase + * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @returns New VeridaDidWallet instance + */ + public static fromPrivateKeyOrMnemonic(privateKeyOrMnemonic: string, blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + let wallet + if (privateKeyOrMnemonic.substr(0,2) == "0x") { + wallet = new Wallet(privateKeyOrMnemonic) + } else { + wallet = Wallet.fromMnemonic(privateKeyOrMnemonic) + } + return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.privateKey) + } + + /** The public key, same as the address */ + public get publicKey(): string { + return this.address + } + + /** The public key as a buffer */ + public get publicKeyBuffer(): Uint8Array { + return Buffer.from(this.address.substr(2), 'hex') + } + + /** The public key encoded in base58 */ + public get publicKeyBase58(): string { + return utils.base58.encode(this.address) + } + + /** The private key as a buffer if available */ + public get privateKeyBuffer(): Uint8Array | undefined { + return this.privateKey ? Buffer.from(this.privateKey.substr(2), 'hex') : undefined + } + + /** The private key encoded in base58 if available */ + public get privateKeyBase58(): string | undefined { + return this.privateKey ? utils.base58.encode(this.privateKey) : undefined + } +} diff --git a/packages/did-client/src/wallet.ts b/packages/did-client/src/wallet.ts index ab1ec667..b5ff4657 100644 --- a/packages/did-client/src/wallet.ts +++ b/packages/did-client/src/wallet.ts @@ -1,6 +1,9 @@ import { utils, Wallet as EthersWallet } from "ethers" -export default class Wallet { +/** + * @deprecated Use VeridaDidWallet instead + */ +export class Wallet { /* @ts-ignore */ private _did: string @@ -61,5 +64,4 @@ export default class Wallet { public get publicKeyBase58(): string { return utils.base58.encode(this._publicKey) } - -} \ No newline at end of file +} diff --git a/packages/did-client/test/did-client.tests.ts b/packages/did-client/test/did-client.tests.ts index dce4546d..757c1c1d 100644 --- a/packages/did-client/test/did-client.tests.ts +++ b/packages/did-client/test/did-client.tests.ts @@ -7,7 +7,8 @@ import { DIDDocument } from "@verida/did-document" import { ServiceEndpoint } from "did-resolver" import { getDIDClient } from "./utils" -import { SecureContextEndpointType } from '@verida/types' +import { Network, SecureContextEndpointType } from '@verida/types' +import { DIDClient } from '../src/did-client' const wallet = Wallet.createRandom() @@ -15,6 +16,7 @@ const address = wallet.address.toLowerCase() const did = `did:vda:testnet:${address}` const CONTEXT_NAME = 'Verida: Test DID Context' +const NETWORK = Network.BANKSIA const keyring = new Keyring(wallet.mnemonic.phrase) @@ -35,10 +37,11 @@ function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -let didClient, currentDoc +let didClient: DIDClient +let currentDoc: DIDDocument /** - * + * */ describe('DID Client tests', () => { @@ -65,7 +68,7 @@ describe('DID Client tests', () => { }) it('can add a context to an existing DID and verify', async function() { - await currentDoc.addContext(CONTEXT_NAME, keyring, wallet.privateKey, endpoints) + await currentDoc.addContext(NETWORK, CONTEXT_NAME, keyring, wallet, endpoints) // Sleep so enough time passes for the updated field to not match created await sleep(1000) @@ -87,11 +90,11 @@ describe('DID Client tests', () => { // Validate service endpoints assert.equal(savedDoc.service?.length, 2, "Have two service entries") - function validateServiceEndpoint(type, endpointUri, actual: ServiceEndpoint) { + function validateServiceEndpoint(type: SecureContextEndpointType, endpointUri: string[], actual: ServiceEndpoint | undefined) { assert.ok(actual) - assert.equal(actual.id, `${did}?context=${contextHash}&type=${type}`, "Endpoint ID matches hard coded value") - assert.equal(actual.type, type, "Type has expected value") - assert.deepEqual(actual.serviceEndpoint, endpointUri, `Endpoint (${actual.serviceEndpoint}) has expected value (${endpointUri})`) + assert.equal(actual?.id, `${did}?context=${contextHash}&type=${type}`, "Endpoint ID matches hard coded value") + assert.equal(actual?.type, type, "Type has expected value") + assert.deepEqual(actual?.serviceEndpoint, endpointUri, `Endpoint (${actual?.serviceEndpoint}) has expected value (${endpointUri})`) } const endpoint1 = doc.locateServiceEndpoint(CONTEXT_NAME, SecureContextEndpointType.DATABASE) @@ -101,11 +104,11 @@ describe('DID Client tests', () => { validateServiceEndpoint(endpoints.messaging.type, endpoints.messaging.endpointUri, endpoint2) // @todo: validate verification method - assert.equal(savedDoc.verificationMethod.length, 4, "Have four verificationMethod entries") + assert.equal(savedDoc.verificationMethod?.length, 4, "Have four verificationMethod entries") assert.deepEqual(savedDoc.verificationMethod, currentDoc.export().verificationMethod, "Verification methods match") - assert.equal(savedDoc.assertionMethod.length, 4, "Have four assertionMethod entries") - assert.equal(savedDoc.keyAgreement.length, 1, "Have one keyAgreement entries") + assert.equal(savedDoc.assertionMethod?.length, 4, "Have four assertionMethod entries") + assert.equal(savedDoc.keyAgreement?.length, 1, "Have one keyAgreement entries") }) it('can remove an existing context', async function() { @@ -118,8 +121,8 @@ describe('DID Client tests', () => { // Validate service endpoints assert.equal(data.service?.length, 0, "Have no service entries") - assert.equal(data.verificationMethod.length, 2, "Have two verificationMethod entries") - assert.equal(data.assertionMethod.length, 2, "Have two assertionMethod entries") + assert.equal(data.verificationMethod?.length, 2, "Have two verificationMethod entries") + assert.equal(data.assertionMethod?.length, 2, "Have two assertionMethod entries") const saved = await didClient.save(doc) assert.ok(saved, 'Context successfully saved to blockchain') @@ -129,14 +132,14 @@ describe('DID Client tests', () => { const chainData = chainDoc.export() assert.equal(chainData.service?.length, 0, "Have no service entries") - assert.equal(chainData.verificationMethod.length, 2, "Have two verificationMethod entries") - assert.equal(chainData.assertionMethod.length, 2, "Have two assertionMethod entries") + assert.equal(chainData.verificationMethod?.length, 2, "Have two verificationMethod entries") + assert.equal(chainData.assertionMethod?.length, 2, "Have two assertionMethod entries") }) it('can replace an existing context, not add again', async function() { try { const doc = await didClient.get(did) - await doc.addContext(CONTEXT_NAME, keyring, wallet.privateKey, endpoints) + await doc.addContext(NETWORK, CONTEXT_NAME, keyring, wallet, endpoints) // Sleep so enough time passes for the updated field to not match created await sleep(1000) @@ -144,19 +147,19 @@ describe('DID Client tests', () => { assert.ok(saved) // Add the same context and save a second time - await doc.addContext(CONTEXT_NAME, keyring, wallet.privateKey, endpoints) - + await doc.addContext(NETWORK, CONTEXT_NAME, keyring, wallet, endpoints) + // Sleep so enough time passes for the updated field to not match created await sleep(1000) - + saved = await didClient.save(doc) assert.ok(saved) const data = doc.export() - assert.equal(data.service!.length, 2, 'Have two service endpoints') - assert.equal(data.verificationMethod!.length, 4, 'Have four verification methods') - assert.equal(data.keyAgreement!.length, 1, 'Have one keyAgreement') - assert.equal(data.assertionMethod!.length, 4, 'Have four assertionMethods') + assert.equal(data.service?.length, 2, 'Have two service endpoints') + assert.equal(data.verificationMethod?.length, 4, 'Have four verification methods') + assert.equal(data.keyAgreement?.length, 1, 'Have one keyAgreement') + assert.equal(data.assertionMethod?.length, 4, 'Have four assertionMethods') } catch (err) { console.log(didClient.getLastEndpointErrors()) throw err diff --git a/packages/did-client/test/utils.ts b/packages/did-client/test/utils.ts index 76f25a54..98674fae 100644 --- a/packages/did-client/test/utils.ts +++ b/packages/did-client/test/utils.ts @@ -1,35 +1,34 @@ -import { DIDClient } from "../src/index" -// import { Wallet } from '@ethersproject/wallet' +import { DIDClient } from "../src/did-client" import { Wallet } from "ethers" -import { DIDClientConfig } from "@verida/types" +import { DIDClientConfig, Network } from "@verida/types" require('dotenv').config() -if (process.env.PRIVATE_KEY === undefined) { - throw new Error('PRIVATE_KEY not defined in env') -} -const privateKey : string = process.env.PRIVATE_KEY! +export async function getDIDClient(wallet: Wallet, didEndpoints: string[]) { + const privateKey = process.env.PRIVATE_KEY + if (!privateKey) { + throw new Error('PRIVATE_KEY not defined in env') + } -const rpcUrl = process.env[`RPC_URL`] -if (rpcUrl === undefined) { - throw new Error('RPC url is not defined in env') -} -console.log('RPC URL :', rpcUrl) + const rpcUrl = process.env[`RPC_URL`] + if (rpcUrl === undefined) { + throw new Error('RPC url is not defined in env') + } + console.log('RPC URL :', rpcUrl) -export async function getDIDClient(veridaAccount: Wallet, didEndpoints: string[]) { const config: DIDClientConfig = { - network: 'testnet', + network: Network.BANKSIA, rpcUrl: rpcUrl! } const didClient = new DIDClient(config) // Configure authenticate to talk directly to the blockchain - didClient.authenticate( - veridaAccount.privateKey, // Verida DID private key + await didClient.authenticate( + wallet, 'web3', { - privateKey, // MATIC private key that will submit transaction + privateKey, // MATIC private key that will submit transaction }, didEndpoints ) @@ -42,7 +41,7 @@ export async function getDIDClient(veridaAccount: Wallet, didEndpoints: string[] serverConfig: { headers: { 'context-name' : 'Verida Test' - } + } }, postConfig: { headers: { @@ -56,4 +55,4 @@ export async function getDIDClient(veridaAccount: Wallet, didEndpoints: string[] */ return didClient -} \ No newline at end of file +} diff --git a/packages/storage-link/test/did.test.ts b/packages/storage-link/test/did.test.ts index b38883e6..3b1c9e4e 100644 --- a/packages/storage-link/test/did.test.ts +++ b/packages/storage-link/test/did.test.ts @@ -1,29 +1,34 @@ const assert = require('assert') import { StorageLink } from '../src/index' -import { DIDClient } from '@verida/did-client' +import { DIDClient, VeridaDidWallet } from '@verida/did-client' import { CONTEXT_NAME } from './utils' import { BlockchainAnchor, Network } from '@verida/types' require('dotenv').config() const NETWORK = Network.BANKSIA const BLOCKCHAIN = BlockchainAnchor.POLAMOY - const MNEMONIC = "pumpkin salad also husband east armor online simple chair perfect used heavy" + +const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(MNEMONIC) + const didClient = new DIDClient({ blockchain: BLOCKCHAIN }) -didClient.authenticate(MNEMONIC, 'web3', { - privateKey: process.env.PRIVATE_KEY, - rpcUrl: process.env.RPC_URL -}, []) -const DID = didClient.getDid() + +const DID = veridaDidWallet.did describe('Test storage links for a DID', () => { + before(async () => { + await didClient.authenticate(veridaDidWallet.signer, 'web3', { + privateKey: process.env.PRIVATE_KEY, + rpcUrl: process.env.RPC_URL + }, []) + }) describe('Get links for an existing DID', function() { this.timeout(20000) - it('can fetch all storage links', async function() { + it('can fetch all storage links', async function () { const storageLinks = await StorageLink.getLinks(NETWORK, didClient, DID) console.log(storageLinks) @@ -46,4 +51,4 @@ describe('Test storage links for a DID', () => { }) }) }) -}) \ No newline at end of file +}) diff --git a/packages/storage-link/test/utils.ts b/packages/storage-link/test/utils.ts index c524a3b0..50c84ee2 100644 --- a/packages/storage-link/test/utils.ts +++ b/packages/storage-link/test/utils.ts @@ -1,35 +1,29 @@ import { DIDClient } from '@verida/did-client' import { Wallet } from "ethers" -import { JsonRpcProvider } from '@ethersproject/providers' -import { DIDClientConfig, BlockchainAnchor } from '@verida/types' +import { BlockchainAnchor } from '@verida/types' require('dotenv').config() -if (process.env.PRIVATE_KEY === undefined) { - throw new Error('PRIVATE_KEY not defined in env') -} -const privateKey : string = process.env.PRIVATE_KEY! - -const rpcUrl = process.env[`RPC_URL`] -if (rpcUrl === undefined) { - throw new Error('RPC url is not defined in env') -} -console.log('RPC URL :', rpcUrl) +export async function getDIDClient(wallet: Wallet) { + const privateKey = process.env.PRIVATE_KEY + if (!privateKey) { + throw new Error('PRIVATE_KEY not defined in env') + } -const provider = new JsonRpcProvider(rpcUrl); -const txSigner = new Wallet(privateKey, provider) + const rpcUrl = process.env.RPC_URL + if (!rpcUrl) { + throw new Error('RPC url is not defined in env') + } + console.log('RPC URL :', rpcUrl) -export async function getDIDClient(veridaAccount: Wallet) { - const config: DIDClientConfig = { + const didClient = new DIDClient({ blockchain: BlockchainAnchor.POLAMOY, rpcUrl - } - - const didClient = new DIDClient(config) + }) - didClient.authenticate( - veridaAccount.privateKey, + await didClient.authenticate( + wallet, 'web3', { privateKey @@ -40,4 +34,4 @@ export async function getDIDClient(veridaAccount: Wallet) { return didClient } -export const CONTEXT_NAME = 'Test App' \ No newline at end of file +export const CONTEXT_NAME = 'Test App' diff --git a/packages/types/src/IDIDClient.ts b/packages/types/src/IDIDClient.ts index 3754c749..8ea76184 100644 --- a/packages/types/src/IDIDClient.ts +++ b/packages/types/src/IDIDClient.ts @@ -1,20 +1,21 @@ +import { Signer } from "ethers"; import { IDIDDocument } from "./IDIDDocument"; -import { Web3CallType, Web3MetaTransactionConfig, Web3SelfTransactionConfigPart, VdaDidEndpointResponses } from "./Web3Interfaces"; +import { Web3CallType, VeridaWeb3TransactionOptions, VdaDidEndpointResponses } from "./Web3Interfaces"; export interface IDIDClient { authenticate( - veridaPrivateKey: string, + signer: Signer, callType: Web3CallType, - web3Config: Web3SelfTransactionConfigPart | Web3MetaTransactionConfig, + web3Config: VeridaWeb3TransactionOptions, defaultEndpoints: string[] - ): void + ): Promise - authenticated(): boolean + authenticated(): boolean - getDid(): string | undefined + getDid(): string | undefined - getPublicKey(): string | undefined + getPublicKey(): string | undefined save(document: IDIDDocument): Promise @@ -23,4 +24,4 @@ export interface IDIDClient { get(did: string): Promise getRpcUrl(): string -} \ No newline at end of file +} diff --git a/packages/vda-common/src/defaults.ts b/packages/vda-common/src/defaults.ts index 4f790e52..783332a0 100644 --- a/packages/vda-common/src/defaults.ts +++ b/packages/vda-common/src/defaults.ts @@ -1,4 +1,4 @@ -import { BlockchainAnchor, Network } from "@verida/types" +import { BlockchainAnchor, EIP1559GasMode, Network } from "@verida/types" import { RPC_URLS } from "./rpc" export const DefaultNetworkBlockchainAnchors: Record = { @@ -8,22 +8,28 @@ export const DefaultNetworkBlockchainAnchors: Record [Network.MYRTLE]: BlockchainAnchor.POLPOS } -export function getWeb3ConfigDefaults(chainName: string) { +export function getWeb3ConfigDefaults(chainName: string): { + rpcUrl: string | undefined + eip1559Mode: EIP1559GasMode + eip1559gasStationUrl: string | undefined +} | null { switch (chainName) { case 'devnet': case 'polamoy': case 'testnet': return { - rpcUrl: RPC_URLS[chainName], + rpcUrl: RPC_URLS[chainName] ?? undefined, eip1559Mode: 'fast', eip1559gasStationUrl: 'https://gasstation-testnet.polygon.technology/amoy' } case 'mainnet': case 'polpos': return { - rpcUrl: RPC_URLS[chainName], + rpcUrl: RPC_URLS[chainName] ?? undefined, eip1559Mode: 'fast', eip1559gasStationUrl: 'https://gasstation.polygon.technology/v2' } + default: + return null } -} \ No newline at end of file +} diff --git a/packages/vda-did-resolver/test/resolver.test.ts b/packages/vda-did-resolver/test/resolver.test.ts index 19bd3336..c0f03282 100644 --- a/packages/vda-did-resolver/test/resolver.test.ts +++ b/packages/vda-did-resolver/test/resolver.test.ts @@ -30,10 +30,10 @@ if (!privateKey) { } const baseConfig = getBlockchainAPIConfiguration(privateKey) -const VDA_DID_CONFIG = { +const VDA_DID_CONFIG = { // TODO: Add strong type to configuration identifier: DID, blockchain: BlockchainAnchor.POLAMOY, - signKey: DID_PRIVATE_KEY, + signer: wallet, callType: baseConfig.callType, web3Options: baseConfig.web3Options } From 53dab5303f2255d0bf58836263189b7dc38d5fbb Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 16:48:35 +1100 Subject: [PATCH 06/12] Refactor account-node package to integrate SignerAccount - Introduced SignerAccount class to manage signing operations using ethers' Signer. - Updated AutoAccount and ContextAccount to extend SignerAccount, streamlining authentication and context management. - Refactored storage linking and context consent message handling to utilize the new structure. - Added utility functions for context consent message generation. - Updated tests to ensure compatibility with the new account management approach and the integration of ethers. --- packages/account-node/package.json | 4 +- .../src/authTypes/VeridaDatabase.ts | 9 +- packages/account-node/src/auto.ts | 295 +----------- packages/account-node/src/contextAccount.ts | 23 +- packages/account-node/src/index.ts | 2 + packages/account-node/src/session-account.ts | 2 +- packages/account-node/src/signer-account.ts | 291 ++++++++++++ packages/account-node/src/utils.ts | 11 + packages/account-node/test/auto.test.ts | 8 +- packages/account-node/test/limited.test.ts | 23 +- .../account-node/test/signer-account.test.ts | 62 +++ packages/types/src/AccountInterfaces.ts | 10 +- yarn.lock | 427 ++++++++++++++++-- 13 files changed, 811 insertions(+), 356 deletions(-) create mode 100644 packages/account-node/src/signer-account.ts create mode 100644 packages/account-node/src/utils.ts create mode 100644 packages/account-node/test/signer-account.test.ts diff --git a/packages/account-node/package.json b/packages/account-node/package.json index 82deed49..73101bd5 100644 --- a/packages/account-node/package.json +++ b/packages/account-node/package.json @@ -19,13 +19,13 @@ "dependencies": { "@verida/account": "^4.4.2-4.4.2-pr1.0", "@verida/did-client": "^4.4.2-4.4.2-pr1.0", - "@verida/did-document": "^4.4.1", "@verida/encryption-utils": "^4.0.0", "@verida/keyring": "^4.4.0", "@verida/types": "^4.4.0", "@verida/vda-common": "^4.4.0", "axios": "^0.27.2", - "did-resolver": "^4.0.1" + "did-resolver": "^4.0.1", + "ethers": "^5.8.0" }, "devDependencies": { "did-jwt": "5.7.0", diff --git a/packages/account-node/src/authTypes/VeridaDatabase.ts b/packages/account-node/src/authTypes/VeridaDatabase.ts index 54da3557..27bc26e4 100644 --- a/packages/account-node/src/authTypes/VeridaDatabase.ts +++ b/packages/account-node/src/authTypes/VeridaDatabase.ts @@ -1,5 +1,4 @@ import Axios from "axios"; -import AutoAccount from "../auto"; import { AuthType } from '@verida/account' import { Account } from "@verida/account"; import { ServiceEndpoint } from 'did-resolver' @@ -8,13 +7,13 @@ import { ContextAuthorizationError, SecureContextPublicKey, VeridaDatabaseAuthCo export default class VeridaDatabaseAuthType extends AuthType { protected contextAuth?: VeridaDatabaseAuthContext - protected account: AutoAccount + protected account: Account // 5 second request timeout protected timeout: number = 10000 public constructor(account: Account, contextName: string, serviceEndpoint: ServiceEndpoint, signKey: SecureContextPublicKey) { super(account, contextName, serviceEndpoint, signKey) - this.account = account + this.account = account } public async getAuthContext(config: VeridaDatabaseAuthTypeConfig = { @@ -138,7 +137,7 @@ export default class VeridaDatabaseAuthType extends AuthType { const consentMessage = `Invalidate device for this application context: "${this.contextName}"?\n\n${did.toLowerCase()}\n${deviceId}` const signature = await this.account.sign(consentMessage) - + try { const response = await this.getAxios(this.contextName).post(`${contextAuth.endpointUri}auth/invalidateDeviceId`, { did, @@ -175,4 +174,4 @@ export default class VeridaDatabaseAuthType extends AuthType { return Axios.create(config); } -} \ No newline at end of file +} diff --git a/packages/account-node/src/auto.ts b/packages/account-node/src/auto.ts index 727f548d..2bae1554 100644 --- a/packages/account-node/src/auto.ts +++ b/packages/account-node/src/auto.ts @@ -1,295 +1,26 @@ -import { StorageLink, DIDStorageConfig } from '@verida/storage-link' -import { Keyring } from '@verida/keyring' -import { Account } from '@verida/account' - -import { DIDClient, Wallet } from '@verida/did-client' -import EncryptionUtils from "@verida/encryption-utils" -import VeridaDatabaseAuthType from "./authTypes/VeridaDatabase" -import { AccountConfig, AccountNodeConfig, AuthContext, BlockchainAnchor, SecureContextConfig, SecureContextEndpointType, SecureContextServices, VdaDidEndpointResponses, VeridaDatabaseAuthTypeConfig } from '@verida/types' -import { NodeSelector, NodeSelectorConfig, NodeSelectorParams } from './nodeSelector' -import { ServiceEndpoint } from 'did-resolver' +import { AccountConfig, AccountNodeConfig, SignerAccountConfig } from '@verida/types' +import { SignerAccount } from './signer-account' +import { Wallet } from 'ethers' +import { VeridaDidWallet } from '@verida/did-client' import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' -export function buildContextConsentMessage(did: string, contextName: string) { - const lowerCaseDid = did.toLowerCase() - return `Do you wish to unlock this storage context: "${contextName}"?\n\n${lowerCaseDid}` -} - /** * An Authenticator that automatically signs everything */ -export default class AutoAccount extends Account { - - private didClient: DIDClient - - private wallet: Wallet - private _did: string - protected accountConfig?: AccountConfig - protected autoConfig: AccountNodeConfig - protected contextAuths: Record> = {} - protected defaultNodes: string[] = [] - +export default class AutoAccount extends SignerAccount { constructor(autoConfig: AccountNodeConfig, accountConfig?: AccountConfig) { - super() - this.accountConfig = accountConfig - this.autoConfig = autoConfig - - const blockchain = DefaultNetworkBlockchainAnchors[autoConfig.network] - this.wallet = new Wallet(autoConfig.privateKey, blockchain.toString()) - this._did = this.wallet.did - - this.didClient = new DIDClient({ - ...autoConfig.didClientConfig, - network: autoConfig.network - }) - } - - public getDIDClient(): DIDClient { - return this.didClient - } - - public setAccountConfig(accountConfig: AccountConfig) { - this.accountConfig = accountConfig - } - - public getAccountConfig(): AccountConfig | undefined { - return this.accountConfig - } - - public getAutoConfig(): AccountNodeConfig { - return this.autoConfig - } - - public async keyring(contextName: string): Promise { - const did = await this.did() - const consentMessage = buildContextConsentMessage(did, contextName) - const signature = await this.sign(consentMessage) - return new Keyring(signature) - } - - // returns a compact JWS - public async sign(message: string): Promise { - return EncryptionUtils.signData(message, this.wallet.privateKeyBuffer) - } - - public async did(): Promise { - return this._did - } - - public async loadDefaultStorageNodes(countryCode?: string, numNodes: number = 3, config: NodeSelectorParams = {}): Promise { - const nodeUris = await this.getDefaultNodes(countryCode, numNodes, config) - - this.accountConfig = { - defaultDatabaseServer: { - type: 'VeridaDatabase', - endpointUri: nodeUris - }, - defaultMessageServer: { - type: 'VeridaMessage', - endpointUri: nodeUris - }, - defaultNotificationServer: { - type: 'VeridaNotification', - endpointUri: config.notificationEndpoints! ? config.notificationEndpoints! : [] - } - } - } - - private async getDefaultNodes(countryCode?: string, numNodes: number = 3, config: NodeSelectorParams = {}): Promise { - if (this.defaultNodes && this.defaultNodes.length) { - return this.defaultNodes - } - - config.network = this.autoConfig.network - config.defaultTimeout = config.defaultTimeout ? config.defaultTimeout : 5000 - config.notificationEndpoints = config.notificationEndpoints ? config.notificationEndpoints : [] - - const nodeSelector = new NodeSelector( config) - const nodeUris = await nodeSelector.selectEndpointUris(countryCode, numNodes) - this.defaultNodes = nodeUris - - return this.defaultNodes - } - - public async storageConfig(contextName: string, forceCreate?: boolean): Promise { - await this.ensureAuthenticated() - - let did = await this.did() - let storageConfig = await StorageLink.getLink(this.autoConfig.network, this.didClient, did, contextName, true) - - if (storageConfig && storageConfig.isLegacyDid) { - this._did = this._did.replace('polpos', 'mainnet') - did = this._did - } - - // Create the storage config if it doesn't exist and force create is specified - if (!storageConfig && forceCreate) { - if (!this.accountConfig) { - await this.loadDefaultStorageNodes(this.autoConfig.countryCode) - } - - const endpoints: SecureContextServices = { - databaseServer: this.accountConfig!.defaultDatabaseServer, - messageServer: this.accountConfig!.defaultMessageServer - } - - if (this.accountConfig!.defaultStorageServer) { - endpoints.storageServer = this.accountConfig!.defaultStorageServer - } - - if (this.accountConfig!.defaultNotificationServer) { - endpoints.notificationServer = this.accountConfig!.defaultNotificationServer - } + const { privateKey, ...config } = autoConfig - storageConfig = await DIDStorageConfig.generate(this, contextName, endpoints) + const wallet = new Wallet(privateKey) - // Need to determine if this is a legacy DID - try { - const didDocument = await this.didClient.get(did) - storageConfig.isLegacyDid = didDocument.id.match('mainnet') ? true : false + const blockchain = DefaultNetworkBlockchainAnchors[config.network] + const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(privateKey, blockchain) - if (storageConfig.isLegacyDid) { - this._did = this._did.replace('polpos', 'mainnet') - } - } catch (err: any) { - // DID may not exist, which means it's not a legacy DID, so no action required - if (!err.message.match('notFound')) { - // Unknown error, so rethrow - throw err - } - } - - await this.linkStorage(storageConfig) - } - - return storageConfig - } - - /** - * Link storage to this user - * - * @param storageConfig - */ - public async linkStorage(storageConfig: SecureContextConfig): Promise { - await this.ensureAuthenticated() - const keyring = await this.keyring(storageConfig.id) - const result = await StorageLink.setLink(this.autoConfig.network, this.didClient, storageConfig, keyring, this.wallet.privateKey) - - for (let i in result) { - const response = result[i] - if (response.status !== 'success') { - return false - } - } - - return true - } - - /** - * Unlink storage for this user - * - * @param contextName - */ - public async unlinkStorage(contextName: string): Promise { - await this.ensureAuthenticated() - let result = await StorageLink.unlink(this.autoConfig.network, this.didClient, contextName) - if (!result) { - return false - } - - result = result - for (let i in result) { - const response = result[i] - if (response.status !== 'success') { - return false - } + const signerConfig: SignerAccountConfig = { + ...config, + signer: wallet } - return true - } - - /** - * Link storage context service endpoint - * - */ - public async linkStorageContextService(contextName: string, endpointType: SecureContextEndpointType, serverType: string, endpointUris: string[]): Promise { - await this.ensureAuthenticated() - const result = await StorageLink.setContextService(this.autoConfig.network, this.didClient, contextName, endpointType, serverType, endpointUris) - - for (let i in result) { - const response = result[i] - if (response.status !== 'success') { - return false - } - } - - return true - } - - public async getAuthContext(contextName: string, contextConfig: SecureContextConfig, authConfig: VeridaDatabaseAuthTypeConfig, authType: string = "database"): Promise { - if (typeof(authConfig.force) == 'undefined') { - authConfig.force = false - } - - if (typeof(authConfig.endpointUri) == 'undefined') { - throw new Error('Endpoint must be specified when getting auth context') - } - - const endpointUri = authConfig.endpointUri - - // Use existing context auth instance if it exists - if (this.contextAuths[contextName] && this.contextAuths[contextName][endpointUri] && !authConfig.force && !authConfig.invalidAccessToken) { - return this.contextAuths[contextName][endpointUri].getAuthContext() - } - - const signKey = contextConfig.publicKeys.signKey - - // @todo: Currently hard code database server, need to support other service types in the future - const serviceEndpoint = contextConfig.services.databaseServer - - if (serviceEndpoint.type == "VeridaDatabase") { - if (!this.contextAuths[contextName]) { - this.contextAuths[contextName] = {} - } - - const authType = new VeridaDatabaseAuthType(this, contextName, endpointUri, signKey) - this.contextAuths[contextName][endpointUri] = authType - - return authType.getAuthContext(authConfig) - } - - throw new Error(`Unknown auth context type (${authType})`) - } - - public async disconnectDevice(contextName: string, deviceId: string="Test device"): Promise { - if (!this.contextAuths[contextName]) { - throw new Error(`Context not connected ${contextName}`) - } - - let success = true - const contextAuths = this.contextAuths[contextName] - for (let i in contextAuths) { - if (!(await contextAuths[i].disconnectDevice(deviceId))) { - success = false - } - } - - return success - } - - public async ensureAuthenticated() { - if (!this.didClient.authenticated()) { - if (!this.autoConfig.didClientConfig.didEndpoints) { - const nodeUris = await this.getDefaultNodes(this.autoConfig.countryCode) - this.autoConfig.didClientConfig.didEndpoints = nodeUris.map((item) => `${item}did/`) - } - - this.didClient.authenticate( - this.wallet.privateKey, - this.autoConfig.didClientConfig.callType, - this.autoConfig.didClientConfig.web3Config, - this.autoConfig.didClientConfig.didEndpoints! - ) - } + super(signerConfig, veridaDidWallet, accountConfig) } } diff --git a/packages/account-node/src/contextAccount.ts b/packages/account-node/src/contextAccount.ts index 71952b4d..00cc3a9f 100644 --- a/packages/account-node/src/contextAccount.ts +++ b/packages/account-node/src/contextAccount.ts @@ -1,27 +1,8 @@ import { AccountConfig, AccountNodeConfig } from "@verida/types"; import LimitedAccount from "./limited"; -import { Keyring } from "@verida/keyring"; export default class ContextAccount extends LimitedAccount { - - private contextDid: string - constructor(autoConfig: AccountNodeConfig, did: string, contextName: string, accountConfig?: AccountConfig) { - super(autoConfig, accountConfig) - this.contextDid = did.toLowerCase() - this.signingContexts = [contextName] - } - - public async keyring(contextName: string): Promise { - if (this.signingContexts.indexOf(contextName) == -1) { - throw new Error(`Account does not support context: ${contextName}`) - } - - return new Keyring(this.autoConfig.privateKey) - } - - public async did(): Promise { - return this.contextDid + super(autoConfig, accountConfig, [contextName]) } - -} \ No newline at end of file +} diff --git a/packages/account-node/src/index.ts b/packages/account-node/src/index.ts index 0dad1330..025d8b93 100644 --- a/packages/account-node/src/index.ts +++ b/packages/account-node/src/index.ts @@ -4,10 +4,12 @@ import { SessionAccount } from "./session-account" import ContextAccount from "./contextAccount" import AuthContextAccount from "./authcontext" import VeridaDatabaseAuthType from "./authTypes/VeridaDatabase" +import { SignerAccount } from "./signer-account" export * from './nodeSelector' export { AutoAccount, + SignerAccount, VeridaDatabaseAuthType, LimitedAccount, SessionAccount, diff --git a/packages/account-node/src/session-account.ts b/packages/account-node/src/session-account.ts index 2ccbd294..b81c8a26 100644 --- a/packages/account-node/src/session-account.ts +++ b/packages/account-node/src/session-account.ts @@ -4,7 +4,7 @@ import EncryptionUtils from '@verida/encryption-utils' import { AccountConfig, AuthContext, AuthTypeConfig, ContextAuthorizationError, SecureContextConfig, SessionAccountConfig, VeridaDatabaseAuthContext, VeridaDatabaseAuthTypeConfig } from '@verida/types' import { interpretIdentifier } from '@verida/vda-common' import Axios from 'axios' -import { buildContextConsentMessage } from './auto' +import { buildContextConsentMessage } from './utils' export class SessionAccount extends Account { private accountConfig?: AccountConfig diff --git a/packages/account-node/src/signer-account.ts b/packages/account-node/src/signer-account.ts new file mode 100644 index 00000000..8499015e --- /dev/null +++ b/packages/account-node/src/signer-account.ts @@ -0,0 +1,291 @@ +import { StorageLink, DIDStorageConfig } from '@verida/storage-link' +import { Keyring } from '@verida/keyring' +import { Account } from '@verida/account' + +import { DIDClient, VeridaDidWallet } from '@verida/did-client' +import VeridaDatabaseAuthType from "./authTypes/VeridaDatabase" +import { AccountConfig, AuthContext, SecureContextConfig, SecureContextEndpointType, SecureContextServices, SignerAccountConfig, VdaDidEndpointResponses, VeridaDatabaseAuthTypeConfig } from '@verida/types' +import { NodeSelector, NodeSelectorConfig, NodeSelectorParams } from './nodeSelector' +import { ServiceEndpoint } from 'did-resolver' +import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' +import { buildContextConsentMessage } from './utils' + +export class SignerAccount extends Account { + private didClient: DIDClient + private veridaDidWallet: VeridaDidWallet + protected accountConfig?: AccountConfig + protected config: SignerAccountConfig + protected contextAuths: Record> = {} + protected defaultNodes: string[] = [] + + constructor(config: SignerAccountConfig, veridaDidWallet: VeridaDidWallet, accountConfig?: AccountConfig) { + super() + this.accountConfig = accountConfig + this.config = config + + this.veridaDidWallet = veridaDidWallet + + this.didClient = new DIDClient({ + ...config.didClientConfig, + network: config.network + }) + } + + public static async create(config: SignerAccountConfig, accountConfig?: AccountConfig): Promise { + const blockchain = DefaultNetworkBlockchainAnchors[config.network] + const veridaDidWallet = await VeridaDidWallet.fromSigner(config.signer, blockchain) + + return new SignerAccount(config, veridaDidWallet, accountConfig) + } + + public getDIDClient(): DIDClient { + return this.didClient + } + + public setAccountConfig(accountConfig: AccountConfig) { + this.accountConfig = accountConfig + } + + public getAccountConfig(): AccountConfig | undefined { + return this.accountConfig + } + + public getConfig(): SignerAccountConfig { + return this.config + } + + public async keyring(contextName: string): Promise { + const did = await this.did() + const consentMessage = buildContextConsentMessage(did, contextName) + const signature = await this.sign(consentMessage) + return new Keyring(signature) + } + + public async sign(message: string): Promise { + return this.veridaDidWallet.signer.signMessage(message) + // return EncryptionUtils.signData(message, this.wallet.privateKeyBuffer) + } + + public async did(): Promise { + return this.veridaDidWallet.did + } + + public async loadDefaultStorageNodes(countryCode?: string, numNodes: number = 3, config: NodeSelectorParams = {}): Promise { + const nodeUris = await this.getDefaultNodes(countryCode, numNodes, config) + + this.accountConfig = { + defaultDatabaseServer: { + type: 'VeridaDatabase', + endpointUri: nodeUris + }, + defaultMessageServer: { + type: 'VeridaMessage', + endpointUri: nodeUris + }, + defaultNotificationServer: { + type: 'VeridaNotification', + endpointUri: config.notificationEndpoints! ? config.notificationEndpoints! : [] + } + } + } + + private async getDefaultNodes(countryCode?: string, numNodes: number = 3, config: NodeSelectorParams = {}): Promise { + if (this.defaultNodes && this.defaultNodes.length) { + return this.defaultNodes + } + + config.network = this.config.network + config.defaultTimeout = config.defaultTimeout ? config.defaultTimeout : 5000 + config.notificationEndpoints = config.notificationEndpoints ? config.notificationEndpoints : [] + + const nodeSelector = new NodeSelector( config) + const nodeUris = await nodeSelector.selectEndpointUris(countryCode, numNodes) + this.defaultNodes = nodeUris + + return this.defaultNodes + } + + public async storageConfig(contextName: string, forceCreate?: boolean): Promise { + await this.ensureAuthenticated() + + let did = await this.did() + let storageConfig = await StorageLink.getLink(this.config.network, this.didClient, did, contextName, true) + + if (storageConfig && storageConfig.isLegacyDid) { + this.veridaDidWallet.did = this.veridaDidWallet.did.replace('polpos', 'mainnet') + did = this.veridaDidWallet.did + } + + // Create the storage config if it doesn't exist and force create is specified + if (!storageConfig && forceCreate) { + if (!this.accountConfig) { + await this.loadDefaultStorageNodes(this.config.countryCode) + } + + const endpoints: SecureContextServices = { + databaseServer: this.accountConfig!.defaultDatabaseServer, + messageServer: this.accountConfig!.defaultMessageServer + } + + if (this.accountConfig!.defaultStorageServer) { + endpoints.storageServer = this.accountConfig!.defaultStorageServer + } + + if (this.accountConfig!.defaultNotificationServer) { + endpoints.notificationServer = this.accountConfig!.defaultNotificationServer + } + + storageConfig = await DIDStorageConfig.generate(this, contextName, endpoints) + + // Need to determine if this is a legacy DID + try { + const didDocument = await this.didClient.get(did) + storageConfig.isLegacyDid = didDocument.id.match('mainnet') ? true : false + + if (storageConfig.isLegacyDid) { + this.veridaDidWallet.did = this.veridaDidWallet.did.replace('polpos', 'mainnet') + } + } catch (err: any) { + // DID may not exist, which means it's not a legacy DID, so no action required + if (!err.message.match('notFound')) { + // Unknown error, so rethrow + throw err + } + } + + await this.linkStorage(storageConfig) + } + + return storageConfig + } + + /** + * Link storage to this user + * + * @param storageConfig + */ + public async linkStorage(storageConfig: SecureContextConfig): Promise { + await this.ensureAuthenticated() + const keyring = await this.keyring(storageConfig.id) + const result = await StorageLink.setLink(this.config.network, this.didClient, storageConfig, keyring, this.veridaDidWallet.signer) + + for (let i in result) { + const response = result[i] + if (response.status !== 'success') { + return false + } + } + + return true + } + + /** + * Unlink storage for this user + * + * @param contextName + */ + public async unlinkStorage(contextName: string): Promise { + await this.ensureAuthenticated() + let result = await StorageLink.unlink(this.config.network, this.didClient, contextName) + if (!result) { + return false + } + + result = result + for (let i in result) { + const response = result[i] + if (response.status !== 'success') { + return false + } + } + + return true + } + + /** + * Link storage context service endpoint + * + */ + public async linkStorageContextService(contextName: string, endpointType: SecureContextEndpointType, serverType: string, endpointUris: string[]): Promise { + await this.ensureAuthenticated() + const result = await StorageLink.setContextService(this.config.network, this.didClient, contextName, endpointType, serverType, endpointUris) + + for (let i in result) { + const response = result[i] + if (response.status !== 'success') { + return false + } + } + + return true + } + + public async getAuthContext(contextName: string, contextConfig: SecureContextConfig, authConfig: VeridaDatabaseAuthTypeConfig, authType: string = "database"): Promise { + if (typeof(authConfig.force) == 'undefined') { + authConfig.force = false + } + + if (typeof(authConfig.endpointUri) == 'undefined') { + throw new Error('Endpoint must be specified when getting auth context') + } + + const endpointUri = authConfig.endpointUri + + // Use existing context auth instance if it exists + if (this.contextAuths[contextName] && this.contextAuths[contextName][endpointUri] && !authConfig.force && !authConfig.invalidAccessToken) { + return this.contextAuths[contextName][endpointUri].getAuthContext() + } + + const signKey = contextConfig.publicKeys.signKey + + // @todo: Currently hard code database server, need to support other service types in the future + const serviceEndpoint = contextConfig.services.databaseServer + + if (serviceEndpoint.type == "VeridaDatabase") { + if (!this.contextAuths[contextName]) { + this.contextAuths[contextName] = {} + } + + const authType = new VeridaDatabaseAuthType(this, contextName, endpointUri, signKey) + this.contextAuths[contextName][endpointUri] = authType + + return authType.getAuthContext(authConfig) + } + + throw new Error(`Unknown auth context type (${authType})`) + } + + public async disconnectDevice(contextName: string, deviceId: string="Test device"): Promise { + if (!this.contextAuths[contextName]) { + throw new Error(`Context not connected ${contextName}`) + } + + let success = true + const contextAuths = this.contextAuths[contextName] + for (let i in contextAuths) { + if (!(await contextAuths[i].disconnectDevice(deviceId))) { + success = false + } + } + + return success + } + + public async ensureAuthenticated() { + if (this.didClient.authenticated()) { + return + } + + if (!this.config.didClientConfig.didEndpoints) { + const nodeUris = await this.getDefaultNodes(this.config.countryCode) + this.config.didClientConfig.didEndpoints = nodeUris.map((item) => `${item}did/`) + } + + await this.didClient.authenticate( + this.veridaDidWallet.signer, + this.config.didClientConfig.callType, + this.config.didClientConfig.web3Config, + this.config.didClientConfig.didEndpoints! + ) + } +} diff --git a/packages/account-node/src/utils.ts b/packages/account-node/src/utils.ts new file mode 100644 index 00000000..d2c8f62d --- /dev/null +++ b/packages/account-node/src/utils.ts @@ -0,0 +1,11 @@ +/** + * Build a context consent message for a given DID and context name + * + * @param did - The DID to include in the message + * @param contextName - The name of the context to include in the message + * @returns A formatted message string + */ +export function buildContextConsentMessage(did: string, contextName: string) { + const lowerCaseDid = did.toLowerCase() + return `Do you wish to unlock this storage context: "${contextName}"?\n\n${lowerCaseDid}` +} diff --git a/packages/account-node/test/auto.test.ts b/packages/account-node/test/auto.test.ts index 7c1308e5..fdc6d12d 100644 --- a/packages/account-node/test/auto.test.ts +++ b/packages/account-node/test/auto.test.ts @@ -3,7 +3,7 @@ const assert = require('assert') import { AutoAccount } from "../src/index" import { decodeJWT } from 'did-jwt' //import CONFIG from './config' -import { AccountNodeDIDClientConfig, EnvironmentType } from "@verida/types" +import { AccountNodeDIDClientConfig, Network } from "@verida/types" const MNEMONIC = 'next awake illegal system analyst border core forum wheat frost hen patch' const APPLICATION_NAME = 'Verida Test: DIDJWT' @@ -22,7 +22,7 @@ describe('Auto account tests', () => { it('verify did-jwt', async function () { const account = new AutoAccount({ - environment: EnvironmentType.TESTNET, + network: Network.BANKSIA, privateKey: MNEMONIC, didClientConfig: DID_CLIENT_CONFIG }) @@ -41,7 +41,7 @@ describe('Auto account tests', () => { it('can reopen the same did account with the same mnemonic and did', async () => { const account1 = new AutoAccount({ - environment: EnvironmentType.TESTNET, + network: Network.BANKSIA, privateKey: MNEMONIC, didClientConfig: DID_CLIENT_CONFIG }) @@ -49,7 +49,7 @@ describe('Auto account tests', () => { const did1 = await account1.did() const account2 = new AutoAccount({ - environment: EnvironmentType.TESTNET, + network: Network.BANKSIA, privateKey: MNEMONIC, didClientConfig: DID_CLIENT_CONFIG }) diff --git a/packages/account-node/test/limited.test.ts b/packages/account-node/test/limited.test.ts index 9f010c05..d8005457 100644 --- a/packages/account-node/test/limited.test.ts +++ b/packages/account-node/test/limited.test.ts @@ -1,10 +1,11 @@ const assert = require('assert') import { LimitedAccount } from "../src/index" -import { DIDClient } from "@verida/did-client" -import { AccountNodeDIDClientConfig, EnvironmentType } from "@verida/types" +import { DIDClient, VeridaDidWallet } from "@verida/did-client" +import { AccountNodeDIDClientConfig, BlockchainAnchor, Network } from "@verida/types" require('dotenv').config() const MNEMONIC = 'next awake illegal system analyst border core forum wheat frost hen patch' +const wallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(MNEMONIC, BlockchainAnchor.POLAMOY) const DID_CLIENT_CONFIG: AccountNodeDIDClientConfig = { //privateKey: CONFIG.networkPrivateKey, @@ -14,29 +15,29 @@ const DID_CLIENT_CONFIG: AccountNodeDIDClientConfig = { } const didClient = new DIDClient({ - network: EnvironmentType.TESTNET + network: Network.BANKSIA }) -didClient.authenticate(MNEMONIC, 'web3', { - privateKey: process.env.PRIVATE_KEY, - rpcUrl: process.env.RPC_URL -}, []) -const DID = didClient.getDid() const VALID_CONTEXT = 'Verida Test: Valid Context' const INVALID_CONTEXT = 'Verida Test: Invalid Context' - describe('Limited account tests', () => { + before(async () => { + await didClient.authenticate(wallet.signer, 'web3', { + privateKey: process.env.PRIVATE_KEY, + rpcUrl: process.env.RPC_URL + }, []) + }) describe('Basic tests', function() { this.timeout(100000) it('Won\'t fetch keyring for an unsupported context', async function() { const account = new LimitedAccount({ - environment: EnvironmentType.TESTNET, + network: Network.BANKSIA, privateKey: MNEMONIC, didClientConfig: DID_CLIENT_CONFIG - }, [VALID_CONTEXT]) + }, undefined, [VALID_CONTEXT]) const validKeyring = await account.keyring(VALID_CONTEXT) assert.ok(validKeyring, "Have a valid keyring") diff --git a/packages/account-node/test/signer-account.test.ts b/packages/account-node/test/signer-account.test.ts new file mode 100644 index 00000000..31838c57 --- /dev/null +++ b/packages/account-node/test/signer-account.test.ts @@ -0,0 +1,62 @@ +'use strict' +const assert = require('assert') +import { SignerAccount } from "../src/signer-account" +import { decodeJWT } from 'did-jwt' +//import CONFIG from './config' +import { AccountNodeDIDClientConfig, Network } from "@verida/types" +const MNEMONIC = 'next awake illegal system analyst border core forum wheat frost hen patch' + +const APPLICATION_NAME = 'Verida Test: DIDJWT' + +const DID_CLIENT_CONFIG: AccountNodeDIDClientConfig = { + //privateKey: CONFIG.networkPrivateKey, + callType: 'web3', + web3Config: {}, + didEndpoints: [] +} + +describe('Auto account tests', () => { + + describe('Basic tests', function () { + this.timeout(100000) + + it('verify did-jwt', async function () { + const account = new SignerAccount({ + network: Network.BANKSIA, + privateKey: MNEMONIC, + didClientConfig: DID_CLIENT_CONFIG + }) + const didJwt = await account.createDidJwt(APPLICATION_NAME, { + hello: 'world' + }) + + const decoded: any = decodeJWT(didJwt) + const did = await account.did() + + assert.equal(decoded.payload.aud, did, 'Decoded AUD matches') + assert.equal(decoded.payload.iss, did, 'Decoded ISS matches') + assert.equal(decoded.payload.data.hello, 'world', 'Decoded data matches') + assert.equal(decoded.payload.context, APPLICATION_NAME, 'Decoded context matches') + }) + + it('can reopen the same did account with the same mnemonic and did', async () => { + const account1 = new SignerAccount({ + network: Network.BANKSIA, + privateKey: MNEMONIC, + didClientConfig: DID_CLIENT_CONFIG + }) + + const did1 = await account1.did() + + const account2 = new SignerAccount({ + network: Network.BANKSIA, + privateKey: MNEMONIC, + didClientConfig: DID_CLIENT_CONFIG + }) + + const did2 = await account2.did() + assert.equal(did1, did2, 'both dids match') + }) + + }) +}) diff --git a/packages/types/src/AccountInterfaces.ts b/packages/types/src/AccountInterfaces.ts index b78f4224..f38f88a1 100644 --- a/packages/types/src/AccountInterfaces.ts +++ b/packages/types/src/AccountInterfaces.ts @@ -3,6 +3,7 @@ import { SecureContextEndpoint } from './DocumentInterfaces' import { SecureContextConfig, SecureContextPublicKey } from './StorageLinkInterfaces' import { DIDClientConfig, Network } from './NetworkInterfaces' import { Web3CallType, Web3MetaTransactionConfig, Web3SelfTransactionConfig } from './Web3Interfaces' +import { Signer } from 'ethers' export interface AccountConfig { defaultDatabaseServer: SecureContextEndpoint, @@ -39,14 +40,19 @@ export class ContextAuthorizationError extends Error { } } -export interface AccountNodeConfig { - privateKey: string, // or mnemonic +export interface SignerAccountConfig { + signer: Signer, network: Network, didClientConfig: AccountNodeDIDClientConfig + /** @deprecated */ options?: any countryCode?: string } +export interface AccountNodeConfig extends Omit { + privateKey: string, // or mnemonic +} + export type ContextSession = { did: string contextName: string diff --git a/yarn.lock b/yarn.lock index 0f66648c..58264af1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,6 +84,21 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/abi@5.8.0", "@ethersproject/abi@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.8.0.tgz#e79bb51940ac35fe6f3262d7fe2cdb25ad5f07d9" + integrity sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" @@ -97,6 +112,19 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" +"@ethersproject/abstract-provider@5.8.0", "@ethersproject/abstract-provider@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz#7581f9be601afa1d02b95d26b9d9840926a35b0c" + integrity sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/networks" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/web" "^5.8.0" + "@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" @@ -108,6 +136,17 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" +"@ethersproject/abstract-signer@5.8.0", "@ethersproject/abstract-signer@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz#8d7417e95e4094c1797a9762e6789c7356db0754" + integrity sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" @@ -119,6 +158,17 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" +"@ethersproject/address@5.8.0", "@ethersproject/address@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.8.0.tgz#3007a2c352eee566ad745dca1dbbebdb50a6a983" + integrity sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" @@ -126,6 +176,13 @@ dependencies: "@ethersproject/bytes" "^5.7.0" +"@ethersproject/base64@5.8.0", "@ethersproject/base64@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.8.0.tgz#61c669c648f6e6aad002c228465d52ac93ee83eb" + integrity sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" @@ -134,6 +191,14 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" +"@ethersproject/basex@5.8.0", "@ethersproject/basex@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.8.0.tgz#1d279a90c4be84d1c1139114a1f844869e57d03a" + integrity sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" @@ -143,6 +208,15 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" +"@ethersproject/bignumber@5.8.0", "@ethersproject/bignumber@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.8.0.tgz#c381d178f9eeb370923d389284efa19f69efa5d7" + integrity sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + bn.js "^5.2.1" + "@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" @@ -150,6 +224,13 @@ dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/bytes@5.8.0", "@ethersproject/bytes@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.8.0.tgz#9074820e1cac7507a34372cadeb035461463be34" + integrity sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" @@ -157,6 +238,13 @@ dependencies: "@ethersproject/bignumber" "^5.7.0" +"@ethersproject/constants@5.8.0", "@ethersproject/constants@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.8.0.tgz#12f31c2f4317b113a4c19de94e50933648c90704" + integrity sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" @@ -173,6 +261,22 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" +"@ethersproject/contracts@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.8.0.tgz#243a38a2e4aa3e757215ea64e276f8a8c9d8ed73" + integrity sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ== + dependencies: + "@ethersproject/abi" "^5.8.0" + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" @@ -188,6 +292,21 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/hash@5.8.0", "@ethersproject/hash@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.8.0.tgz#b8893d4629b7f8462a90102572f8cd65a0192b4c" + integrity sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" @@ -206,6 +325,24 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" +"@ethersproject/hdnode@5.8.0", "@ethersproject/hdnode@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.8.0.tgz#a51ae2a50bcd48ef6fd108c64cbae5e6ff34a761" + integrity sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/basex" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/pbkdf2" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/sha2" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/wordlists" "^5.8.0" + "@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" @@ -225,6 +362,25 @@ aes-js "3.0.0" scrypt-js "3.0.1" +"@ethersproject/json-wallets@5.8.0", "@ethersproject/json-wallets@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.8.0.tgz#d18de0a4cf0f185f232eb3c17d5e0744d97eb8c9" + integrity sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w== + dependencies: + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/hdnode" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/pbkdf2" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/random" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + "@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" @@ -233,11 +389,24 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" +"@ethersproject/keccak256@5.8.0", "@ethersproject/keccak256@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.8.0.tgz#d2123a379567faf2d75d2aaea074ffd4df349e6a" + integrity sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng== + dependencies: + "@ethersproject/bytes" "^5.8.0" + js-sha3 "0.8.0" + "@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== +"@ethersproject/logger@5.8.0", "@ethersproject/logger@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.8.0.tgz#f0232968a4f87d29623a0481690a2732662713d6" + integrity sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA== + "@ethersproject/networks@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.0.tgz#df72a392f1a63a57f87210515695a31a245845ad" @@ -252,6 +421,13 @@ dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/networks@5.8.0", "@ethersproject/networks@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.8.0.tgz#8b4517a3139380cba9fb00b63ffad0a979671fde" + integrity sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" @@ -260,6 +436,14 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" +"@ethersproject/pbkdf2@5.8.0", "@ethersproject/pbkdf2@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.8.0.tgz#cd2621130e5dd51f6a0172e63a6e4a0c0a0ec37e" + integrity sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/sha2" "^5.8.0" + "@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" @@ -267,6 +451,13 @@ dependencies: "@ethersproject/logger" "^5.7.0" +"@ethersproject/properties@5.8.0", "@ethersproject/properties@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.8.0.tgz#405a8affb6311a49a91dabd96aeeae24f477020e" + integrity sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw== + dependencies: + "@ethersproject/logger" "^5.8.0" + "@ethersproject/providers@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.0.tgz#a885cfc7650a64385e7b03ac86fe9c2d4a9c2c63" @@ -319,6 +510,32 @@ bech32 "1.1.4" ws "7.4.6" +"@ethersproject/providers@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.8.0.tgz#6c2ae354f7f96ee150439f7de06236928bc04cb4" + integrity sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/base64" "^5.8.0" + "@ethersproject/basex" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/networks" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/random" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/sha2" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/web" "^5.8.0" + bech32 "1.1.4" + ws "8.18.0" + "@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" @@ -327,6 +544,14 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/random@5.8.0", "@ethersproject/random@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.8.0.tgz#1bced04d49449f37c6437c701735a1a022f0057a" + integrity sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" @@ -335,6 +560,14 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/rlp@5.8.0", "@ethersproject/rlp@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.8.0.tgz#5a0d49f61bc53e051532a5179472779141451de5" + integrity sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" @@ -344,6 +577,15 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" +"@ethersproject/sha2@5.8.0", "@ethersproject/sha2@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.8.0.tgz#8954a613bb78dac9b46829c0a95de561ef74e5e1" + integrity sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + hash.js "1.1.7" + "@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" @@ -356,6 +598,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/signing-key@5.8.0", "@ethersproject/signing-key@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.8.0.tgz#9797e02c717b68239c6349394ea85febf8893119" + integrity sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + bn.js "^5.2.1" + elliptic "6.6.1" + hash.js "1.1.7" + "@ethersproject/solidity@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" @@ -368,6 +622,18 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/solidity@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.8.0.tgz#429bb9fcf5521307a9448d7358c26b93695379b9" + integrity sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/sha2" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" @@ -377,6 +643,15 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/strings@5.8.0", "@ethersproject/strings@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.8.0.tgz#ad79fafbf0bd272d9765603215ac74fd7953908f" + integrity sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" @@ -392,6 +667,21 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" +"@ethersproject/transactions@5.8.0", "@ethersproject/transactions@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.8.0.tgz#1e518822403abc99def5a043d1c6f6fe0007e46b" + integrity sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg== + dependencies: + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/rlp" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + "@ethersproject/units@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" @@ -401,6 +691,15 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" +"@ethersproject/units@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.8.0.tgz#c12f34ba7c3a2de0e9fa0ed0ee32f3e46c5c2c6a" + integrity sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ== + dependencies: + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/constants" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/wallet@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" @@ -422,6 +721,27 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" +"@ethersproject/wallet@5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.8.0.tgz#49c300d10872e6986d953e8310dc33d440da8127" + integrity sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA== + dependencies: + "@ethersproject/abstract-provider" "^5.8.0" + "@ethersproject/abstract-signer" "^5.8.0" + "@ethersproject/address" "^5.8.0" + "@ethersproject/bignumber" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/hdnode" "^5.8.0" + "@ethersproject/json-wallets" "^5.8.0" + "@ethersproject/keccak256" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/random" "^5.8.0" + "@ethersproject/signing-key" "^5.8.0" + "@ethersproject/transactions" "^5.8.0" + "@ethersproject/wordlists" "^5.8.0" + "@ethersproject/web@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.0.tgz#40850c05260edad8b54827923bbad23d96aac0bc" @@ -444,6 +764,17 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/web@5.8.0", "@ethersproject/web@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.8.0.tgz#3e54badc0013b7a801463a7008a87988efce8a37" + integrity sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw== + dependencies: + "@ethersproject/base64" "^5.8.0" + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" @@ -455,6 +786,17 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" +"@ethersproject/wordlists@5.8.0", "@ethersproject/wordlists@^5.8.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.8.0.tgz#7a5654ee8d1bb1f4dbe43f91d217356d650ad821" + integrity sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg== + dependencies: + "@ethersproject/bytes" "^5.8.0" + "@ethersproject/hash" "^5.8.0" + "@ethersproject/logger" "^5.8.0" + "@ethersproject/properties" "^5.8.0" + "@ethersproject/strings" "^5.8.0" + "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -2991,6 +3333,19 @@ elliptic@6.5.4: minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" +elliptic@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + elliptic@^6.5.4: version "6.5.5" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.5.tgz#c715e09f78b6923977610d4c2346d6ce22e6dded" @@ -3449,6 +3804,42 @@ ethers@5.7.2, ethers@^5.5.1, ethers@^5.7.0, ethers@^5.7.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" +ethers@^5.8.0: + version "5.8.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.8.0.tgz#97858dc4d4c74afce83ea7562fe9493cedb4d377" + integrity sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg== + dependencies: + "@ethersproject/abi" "5.8.0" + "@ethersproject/abstract-provider" "5.8.0" + "@ethersproject/abstract-signer" "5.8.0" + "@ethersproject/address" "5.8.0" + "@ethersproject/base64" "5.8.0" + "@ethersproject/basex" "5.8.0" + "@ethersproject/bignumber" "5.8.0" + "@ethersproject/bytes" "5.8.0" + "@ethersproject/constants" "5.8.0" + "@ethersproject/contracts" "5.8.0" + "@ethersproject/hash" "5.8.0" + "@ethersproject/hdnode" "5.8.0" + "@ethersproject/json-wallets" "5.8.0" + "@ethersproject/keccak256" "5.8.0" + "@ethersproject/logger" "5.8.0" + "@ethersproject/networks" "5.8.0" + "@ethersproject/pbkdf2" "5.8.0" + "@ethersproject/properties" "5.8.0" + "@ethersproject/providers" "5.8.0" + "@ethersproject/random" "5.8.0" + "@ethersproject/rlp" "5.8.0" + "@ethersproject/sha2" "5.8.0" + "@ethersproject/signing-key" "5.8.0" + "@ethersproject/solidity" "5.8.0" + "@ethersproject/strings" "5.8.0" + "@ethersproject/transactions" "5.8.0" + "@ethersproject/units" "5.8.0" + "@ethersproject/wallet" "5.8.0" + "@ethersproject/web" "5.8.0" + "@ethersproject/wordlists" "5.8.0" + ethers@^6.13.1: version "6.13.1" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.1.tgz#2b9f9c7455cde9d38b30fe6589972eb083652961" @@ -7432,7 +7823,7 @@ string-similarity@4.0.4: resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -7449,15 +7840,6 @@ string-similarity@4.0.4: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -7523,7 +7905,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -7544,13 +7926,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -8342,7 +8717,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -8369,15 +8744,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -8457,6 +8823,11 @@ ws@8.17.1: resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +ws@8.18.0: + version "8.18.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc" + integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw== + xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From b1cb7569e6eb6a0b424a5a103c18dd1888fbf02f Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 27 Mar 2025 17:32:07 +1100 Subject: [PATCH 07/12] Refactor account-node package to implement WalletAccount class - Introduced WalletAccount class to manage wallet operations and integrate with VeridaDidWallet. - Updated AutoAccount to extend WalletAccount, enhancing authentication and signing capabilities. - Refactored related configurations and methods to utilize the new WalletAccount structure. - Added tests for WalletAccount to ensure functionality and compatibility with existing features. --- package.json | 3 +- packages/account-node/src/auto.ts | 12 +++---- packages/account-node/src/index.ts | 4 +-- .../{signer-account.ts => wallet-account.ts} | 33 ++++++++++++++----- ...account.test.ts => wallet-account.test.ts} | 18 ++++++---- packages/types/src/AccountInterfaces.ts | 8 ++--- 6 files changed, 47 insertions(+), 31 deletions(-) rename packages/account-node/src/{signer-account.ts => wallet-account.ts} (90%) rename packages/account-node/test/{signer-account.test.ts => wallet-account.test.ts} (76%) diff --git a/package.json b/package.json index ef72e54d..8f0510fc 100644 --- a/package.json +++ b/package.json @@ -15,5 +15,6 @@ }, "workspaces": [ "packages/*" - ] + ], + "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" } diff --git a/packages/account-node/src/auto.ts b/packages/account-node/src/auto.ts index 2bae1554..35b99887 100644 --- a/packages/account-node/src/auto.ts +++ b/packages/account-node/src/auto.ts @@ -1,13 +1,13 @@ -import { AccountConfig, AccountNodeConfig, SignerAccountConfig } from '@verida/types' -import { SignerAccount } from './signer-account' +import { AccountConfig, AccountNodeConfig } from '@verida/types' import { Wallet } from 'ethers' import { VeridaDidWallet } from '@verida/did-client' import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' +import { WalletAccount, WalletAccountConfig } from './wallet-account' /** * An Authenticator that automatically signs everything */ -export default class AutoAccount extends SignerAccount { +export default class AutoAccount extends WalletAccount { constructor(autoConfig: AccountNodeConfig, accountConfig?: AccountConfig) { const { privateKey, ...config } = autoConfig @@ -16,11 +16,11 @@ export default class AutoAccount extends SignerAccount { const blockchain = DefaultNetworkBlockchainAnchors[config.network] const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(privateKey, blockchain) - const signerConfig: SignerAccountConfig = { + const walletAccountConfig: WalletAccountConfig = { ...config, - signer: wallet + veridaDidWallet } - super(signerConfig, veridaDidWallet, accountConfig) + super(walletAccountConfig, accountConfig) } } diff --git a/packages/account-node/src/index.ts b/packages/account-node/src/index.ts index 025d8b93..4d38a797 100644 --- a/packages/account-node/src/index.ts +++ b/packages/account-node/src/index.ts @@ -4,12 +4,12 @@ import { SessionAccount } from "./session-account" import ContextAccount from "./contextAccount" import AuthContextAccount from "./authcontext" import VeridaDatabaseAuthType from "./authTypes/VeridaDatabase" -import { SignerAccount } from "./signer-account" +import { WalletAccount } from './wallet-account' export * from './nodeSelector' export { AutoAccount, - SignerAccount, + WalletAccount, VeridaDatabaseAuthType, LimitedAccount, SessionAccount, diff --git a/packages/account-node/src/signer-account.ts b/packages/account-node/src/wallet-account.ts similarity index 90% rename from packages/account-node/src/signer-account.ts rename to packages/account-node/src/wallet-account.ts index 8499015e..40fc2e65 100644 --- a/packages/account-node/src/signer-account.ts +++ b/packages/account-node/src/wallet-account.ts @@ -4,26 +4,36 @@ import { Account } from '@verida/account' import { DIDClient, VeridaDidWallet } from '@verida/did-client' import VeridaDatabaseAuthType from "./authTypes/VeridaDatabase" -import { AccountConfig, AuthContext, SecureContextConfig, SecureContextEndpointType, SecureContextServices, SignerAccountConfig, VdaDidEndpointResponses, VeridaDatabaseAuthTypeConfig } from '@verida/types' +import { AccountConfig, AccountNodeDIDClientConfig, AuthContext, Network, SecureContextConfig, SecureContextEndpointType, SecureContextServices, VdaDidEndpointResponses, VeridaDatabaseAuthTypeConfig } from '@verida/types' import { NodeSelector, NodeSelectorConfig, NodeSelectorParams } from './nodeSelector' import { ServiceEndpoint } from 'did-resolver' import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' import { buildContextConsentMessage } from './utils' +import { Signer } from 'ethers' + +export interface WalletAccountConfig { + veridaDidWallet: VeridaDidWallet, + network: Network, + didClientConfig: AccountNodeDIDClientConfig + /** @deprecated */ + options?: any + countryCode?: string +} -export class SignerAccount extends Account { +export class WalletAccount extends Account { private didClient: DIDClient private veridaDidWallet: VeridaDidWallet protected accountConfig?: AccountConfig - protected config: SignerAccountConfig + protected config: WalletAccountConfig protected contextAuths: Record> = {} protected defaultNodes: string[] = [] - constructor(config: SignerAccountConfig, veridaDidWallet: VeridaDidWallet, accountConfig?: AccountConfig) { + constructor(config: WalletAccountConfig, accountConfig?: AccountConfig) { super() this.accountConfig = accountConfig this.config = config - this.veridaDidWallet = veridaDidWallet + this.veridaDidWallet = config.veridaDidWallet this.didClient = new DIDClient({ ...config.didClientConfig, @@ -31,11 +41,16 @@ export class SignerAccount extends Account { }) } - public static async create(config: SignerAccountConfig, accountConfig?: AccountConfig): Promise { + public static async createFromSigner(signer: Signer, config: Omit, accountConfig?: AccountConfig): Promise { const blockchain = DefaultNetworkBlockchainAnchors[config.network] - const veridaDidWallet = await VeridaDidWallet.fromSigner(config.signer, blockchain) + const veridaDidWallet = await VeridaDidWallet.fromSigner(signer, blockchain) + + const walletAccountConfig: WalletAccountConfig = { + ...config, + veridaDidWallet + } - return new SignerAccount(config, veridaDidWallet, accountConfig) + return new WalletAccount(walletAccountConfig, accountConfig) } public getDIDClient(): DIDClient { @@ -50,7 +65,7 @@ export class SignerAccount extends Account { return this.accountConfig } - public getConfig(): SignerAccountConfig { + public getConfig(): WalletAccountConfig { return this.config } diff --git a/packages/account-node/test/signer-account.test.ts b/packages/account-node/test/wallet-account.test.ts similarity index 76% rename from packages/account-node/test/signer-account.test.ts rename to packages/account-node/test/wallet-account.test.ts index 31838c57..7d831d8a 100644 --- a/packages/account-node/test/signer-account.test.ts +++ b/packages/account-node/test/wallet-account.test.ts @@ -1,11 +1,15 @@ 'use strict' const assert = require('assert') -import { SignerAccount } from "../src/signer-account" +import { WalletAccount } from "../src/wallet-account" import { decodeJWT } from 'did-jwt' //import CONFIG from './config' import { AccountNodeDIDClientConfig, Network } from "@verida/types" +import { VeridaDidWallet } from "@verida/did-client" +import { DefaultNetworkBlockchainAnchors } from "@verida/vda-common" const MNEMONIC = 'next awake illegal system analyst border core forum wheat frost hen patch' +const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(MNEMONIC, DefaultNetworkBlockchainAnchors[Network.BANKSIA]) + const APPLICATION_NAME = 'Verida Test: DIDJWT' const DID_CLIENT_CONFIG: AccountNodeDIDClientConfig = { @@ -21,9 +25,9 @@ describe('Auto account tests', () => { this.timeout(100000) it('verify did-jwt', async function () { - const account = new SignerAccount({ + const account = new WalletAccount({ network: Network.BANKSIA, - privateKey: MNEMONIC, + veridaDidWallet, didClientConfig: DID_CLIENT_CONFIG }) const didJwt = await account.createDidJwt(APPLICATION_NAME, { @@ -40,17 +44,17 @@ describe('Auto account tests', () => { }) it('can reopen the same did account with the same mnemonic and did', async () => { - const account1 = new SignerAccount({ + const account1 = new WalletAccount({ network: Network.BANKSIA, - privateKey: MNEMONIC, + veridaDidWallet, didClientConfig: DID_CLIENT_CONFIG }) const did1 = await account1.did() - const account2 = new SignerAccount({ + const account2 = new WalletAccount({ network: Network.BANKSIA, - privateKey: MNEMONIC, + veridaDidWallet, didClientConfig: DID_CLIENT_CONFIG }) diff --git a/packages/types/src/AccountInterfaces.ts b/packages/types/src/AccountInterfaces.ts index f38f88a1..0eade423 100644 --- a/packages/types/src/AccountInterfaces.ts +++ b/packages/types/src/AccountInterfaces.ts @@ -40,8 +40,8 @@ export class ContextAuthorizationError extends Error { } } -export interface SignerAccountConfig { - signer: Signer, +export interface AccountNodeConfig { + privateKey: string, // or mnemonic network: Network, didClientConfig: AccountNodeDIDClientConfig /** @deprecated */ @@ -49,10 +49,6 @@ export interface SignerAccountConfig { countryCode?: string } -export interface AccountNodeConfig extends Omit { - privateKey: string, // or mnemonic -} - export type ContextSession = { did: string contextName: string From d66092274b1a258d81e5303cc4a57193e605eed1 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Fri, 28 Mar 2025 12:51:46 +1100 Subject: [PATCH 08/12] Clean unnecessary wallet instance --- packages/account-node/src/auto.ts | 3 --- packages/account-node/test/auto.test.ts | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/account-node/src/auto.ts b/packages/account-node/src/auto.ts index 35b99887..4739f26a 100644 --- a/packages/account-node/src/auto.ts +++ b/packages/account-node/src/auto.ts @@ -1,5 +1,4 @@ import { AccountConfig, AccountNodeConfig } from '@verida/types' -import { Wallet } from 'ethers' import { VeridaDidWallet } from '@verida/did-client' import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' import { WalletAccount, WalletAccountConfig } from './wallet-account' @@ -11,8 +10,6 @@ export default class AutoAccount extends WalletAccount { constructor(autoConfig: AccountNodeConfig, accountConfig?: AccountConfig) { const { privateKey, ...config } = autoConfig - const wallet = new Wallet(privateKey) - const blockchain = DefaultNetworkBlockchainAnchors[config.network] const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(privateKey, blockchain) diff --git a/packages/account-node/test/auto.test.ts b/packages/account-node/test/auto.test.ts index fdc6d12d..509b1e5d 100644 --- a/packages/account-node/test/auto.test.ts +++ b/packages/account-node/test/auto.test.ts @@ -4,6 +4,7 @@ import { AutoAccount } from "../src/index" import { decodeJWT } from 'did-jwt' //import CONFIG from './config' import { AccountNodeDIDClientConfig, Network } from "@verida/types" + const MNEMONIC = 'next awake illegal system analyst border core forum wheat frost hen patch' const APPLICATION_NAME = 'Verida Test: DIDJWT' From 26bff584b612091dd73dd0d346a398b84775ab52 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Fri, 28 Mar 2025 12:52:11 +1100 Subject: [PATCH 09/12] Fix typo in WalletAccount and update config type --- packages/account-node/src/wallet-account.ts | 4 +--- packages/account-node/test/wallet-account.test.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/account-node/src/wallet-account.ts b/packages/account-node/src/wallet-account.ts index 40fc2e65..dd1469b6 100644 --- a/packages/account-node/src/wallet-account.ts +++ b/packages/account-node/src/wallet-account.ts @@ -11,12 +11,10 @@ import { DefaultNetworkBlockchainAnchors } from '@verida/vda-common' import { buildContextConsentMessage } from './utils' import { Signer } from 'ethers' -export interface WalletAccountConfig { +export type WalletAccountConfig = { veridaDidWallet: VeridaDidWallet, network: Network, didClientConfig: AccountNodeDIDClientConfig - /** @deprecated */ - options?: any countryCode?: string } diff --git a/packages/account-node/test/wallet-account.test.ts b/packages/account-node/test/wallet-account.test.ts index 7d831d8a..369f5cf1 100644 --- a/packages/account-node/test/wallet-account.test.ts +++ b/packages/account-node/test/wallet-account.test.ts @@ -19,7 +19,7 @@ const DID_CLIENT_CONFIG: AccountNodeDIDClientConfig = { didEndpoints: [] } -describe('Auto account tests', () => { +describe('WalletAccount tests', () => { describe('Basic tests', function () { this.timeout(100000) From 504a7f9d2243778e6e1691480a95fe27d573f3d9 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Fri, 28 Mar 2025 12:52:25 +1100 Subject: [PATCH 10/12] Create unit test for VeridaDidWallet --- packages/did-client/src/verida-did-wallet.ts | 12 +-- .../test/verrida-did-wallet.test.ts | 95 +++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 packages/did-client/test/verrida-did-wallet.test.ts diff --git a/packages/did-client/src/verida-did-wallet.ts b/packages/did-client/src/verida-did-wallet.ts index f6b9ecb7..3cd4b063 100644 --- a/packages/did-client/src/verida-did-wallet.ts +++ b/packages/did-client/src/verida-did-wallet.ts @@ -36,10 +36,10 @@ export class VeridaDidWallet { /** * Create a new random wallet * - * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @param blockchainAnchor - Blockchain network to anchor the DID * @returns New VeridaDidWallet instance */ - public static createRandom(blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + public static createRandom(blockchainAnchor: BlockchainAnchor) { const wallet = Wallet.createRandom() return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.privateKey) } @@ -48,10 +48,10 @@ export class VeridaDidWallet { * Create a wallet from an existing signer * * @param signer - Signer instance to use - * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @param blockchainAnchor - Blockchain network to anchor the DID * @returns New VeridaDidWallet instance */ - public static async fromSigner(signer: Signer, blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + public static async fromSigner(signer: Signer, blockchainAnchor: BlockchainAnchor) { const address = await signer.getAddress() return new VeridaDidWallet(signer, blockchainAnchor, address, undefined) } @@ -60,10 +60,10 @@ export class VeridaDidWallet { * Create a wallet from a private key or mnemonic phrase * * @param privateKeyOrMnemonic - Private key (0x prefixed) or mnemonic phrase - * @param blockchainAnchor - Blockchain network to anchor the DID (defaults to POLPOS) + * @param blockchainAnchor - Blockchain network to anchor the DID * @returns New VeridaDidWallet instance */ - public static fromPrivateKeyOrMnemonic(privateKeyOrMnemonic: string, blockchainAnchor: BlockchainAnchor = BlockchainAnchor.POLPOS) { + public static fromPrivateKeyOrMnemonic(privateKeyOrMnemonic: string, blockchainAnchor: BlockchainAnchor) { let wallet if (privateKeyOrMnemonic.substr(0,2) == "0x") { wallet = new Wallet(privateKeyOrMnemonic) diff --git a/packages/did-client/test/verrida-did-wallet.test.ts b/packages/did-client/test/verrida-did-wallet.test.ts new file mode 100644 index 00000000..259084ac --- /dev/null +++ b/packages/did-client/test/verrida-did-wallet.test.ts @@ -0,0 +1,95 @@ +import { expect } from 'chai' +import { VeridaDidWallet } from '../src/verida-did-wallet' +import { BlockchainAnchor } from '@verida/types' +import { Wallet } from 'ethers' + +describe('VeridaDidWallet', () => { + describe('createRandom()', () => { + it('should create a new random wallet', () => { + const veridaDidWallet = VeridaDidWallet.createRandom(BlockchainAnchor.POLAMOY) + + expect(veridaDidWallet.did).to.be.a('string') + expect(veridaDidWallet.did).to.include('did:vda:polamoy:') + expect(veridaDidWallet.blockchainAnchor).to.equal(BlockchainAnchor.POLAMOY) + expect(veridaDidWallet.address).to.match(/^0x[a-fA-F0-9]{40}$/) + expect(veridaDidWallet.privateKey).to.match(/^0x[a-fA-F0-9]{64}$/) + expect(veridaDidWallet.publicKey).to.equal(veridaDidWallet.address) + expect(veridaDidWallet.signer).to.not.be.undefined + }) + }) + + describe('fromSigner()', () => { + it('should create a wallet from an existing signer', async () => { + const signer = Wallet.createRandom() + const veridaDidWallet = await VeridaDidWallet.fromSigner(signer, BlockchainAnchor.POLAMOY) + + expect(veridaDidWallet.did).to.be.a('string') + expect(veridaDidWallet.did).to.include('did:vda:polamoy:') + expect(veridaDidWallet.blockchainAnchor).to.equal(BlockchainAnchor.POLAMOY) + expect(veridaDidWallet.address).to.equal(signer.address) + expect(veridaDidWallet.publicKey).to.equal(veridaDidWallet.address) + expect(veridaDidWallet.privateKey).to.be.undefined + expect(veridaDidWallet.signer).to.equal(signer) + }) + }) + + describe('fromPrivateKeyOrMnemonic()', () => { + it('should create a wallet from a private key', () => { + const originalWallet = Wallet.createRandom() + const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(originalWallet.privateKey, BlockchainAnchor.POLAMOY) + + expect(veridaDidWallet.did).to.be.a('string') + expect(veridaDidWallet.did).to.include('did:vda:polamoy:') + expect(veridaDidWallet.blockchainAnchor).to.equal(BlockchainAnchor.POLAMOY) + expect(veridaDidWallet.address).to.equal(originalWallet.address) + expect(veridaDidWallet.privateKey).to.equal(originalWallet.privateKey) + expect(veridaDidWallet.publicKey).to.equal(veridaDidWallet.address) + expect(veridaDidWallet.signer).to.not.be.undefined + }) + + it('should create a wallet from a mnemonic', () => { + const originalWallet = Wallet.createRandom() + const veridaDidWallet = VeridaDidWallet.fromPrivateKeyOrMnemonic(originalWallet.mnemonic.phrase, BlockchainAnchor.POLAMOY) + + expect(veridaDidWallet.did).to.be.a('string') + expect(veridaDidWallet.did).to.include('did:vda:polamoy:') + expect(veridaDidWallet.blockchainAnchor).to.equal(BlockchainAnchor.POLAMOY) + expect(veridaDidWallet.address).to.equal(originalWallet.address) + expect(veridaDidWallet.privateKey).to.equal(originalWallet.privateKey) + expect(veridaDidWallet.publicKey).to.equal(veridaDidWallet.address) + expect(veridaDidWallet.signer).to.not.be.undefined + }) + }) + + describe('Key encodings', () => { + let wallet: VeridaDidWallet + + beforeEach(() => { + wallet = VeridaDidWallet.createRandom(BlockchainAnchor.POLAMOY) + }) + + it('should provide public key as buffer', () => { + expect(wallet.publicKeyBuffer).to.be.instanceof(Uint8Array) + expect(wallet.publicKeyBuffer.length).to.equal(20) // 20 bytes for address + }) + + it('should provide public key in base58', () => { + expect(wallet.publicKeyBase58).to.be.a('string') + }) + + it('should provide private key as buffer', () => { + expect(wallet.privateKeyBuffer).to.be.instanceof(Uint8Array) + expect(wallet.privateKeyBuffer!.length).to.equal(32) // 32 bytes for private key + }) + + it('should provide private key in base58', () => { + expect(wallet.privateKeyBase58).to.be.a('string') + }) + + it('should handle undefined private key for signer-based wallets', async () => { + const signerWallet = await VeridaDidWallet.fromSigner(Wallet.createRandom(), BlockchainAnchor.POLAMOY) + expect(signerWallet.privateKeyBuffer).to.be.undefined + expect(signerWallet.privateKeyBase58).to.be.undefined + }) + }) +}) From e4995588e9e3a12a6b4471cbde12c57215fccb69 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Wed, 2 Apr 2025 15:44:29 +1100 Subject: [PATCH 11/12] Fix incorrect publicKey in VeridaDidWallet and fix await of signProof --- packages/did-client/src/verida-did-wallet.ts | 26 ++++++++++---------- packages/encryption-utils/src/index.ts | 16 ++++++------ packages/vda-did/src/vdaDid.ts | 4 +-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/packages/did-client/src/verida-did-wallet.ts b/packages/did-client/src/verida-did-wallet.ts index 3cd4b063..fa8d9ac8 100644 --- a/packages/did-client/src/verida-did-wallet.ts +++ b/packages/did-client/src/verida-did-wallet.ts @@ -16,6 +16,8 @@ export class VeridaDidWallet { public signer: Signer /** Optional private key, unavailable if created from a signer */ public privateKey: string | undefined + /** Optional private key, unavailable if created from a signer */ + public publicKey: string | undefined /** * The constructor is intentionally private, use the static methods to create instances @@ -25,12 +27,13 @@ export class VeridaDidWallet { * @param address - Wallet address * @param privateKey - Optional private key */ - private constructor(signer: Signer, blockchainAnchor: BlockchainAnchor, address: string, privateKey: string | undefined) { + private constructor(signer: Signer, blockchainAnchor: BlockchainAnchor, address: string, publicKey: string | undefined, privateKey: string | undefined) { this.did = buildVeridaDidIdentifier(blockchainAnchor, address) this.blockchainAnchor = blockchainAnchor this.address = address this.signer = signer this.privateKey = privateKey + this.publicKey = publicKey } /** @@ -41,7 +44,7 @@ export class VeridaDidWallet { */ public static createRandom(blockchainAnchor: BlockchainAnchor) { const wallet = Wallet.createRandom() - return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.privateKey) + return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.publicKey, wallet.privateKey) } /** @@ -53,7 +56,9 @@ export class VeridaDidWallet { */ public static async fromSigner(signer: Signer, blockchainAnchor: BlockchainAnchor) { const address = await signer.getAddress() - return new VeridaDidWallet(signer, blockchainAnchor, address, undefined) + + // For security purpose, the public and private keys are not exposed by a signer + return new VeridaDidWallet(signer, blockchainAnchor, address, undefined, undefined) } /** @@ -70,22 +75,17 @@ export class VeridaDidWallet { } else { wallet = Wallet.fromMnemonic(privateKeyOrMnemonic) } - return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.privateKey) - } - - /** The public key, same as the address */ - public get publicKey(): string { - return this.address + return new VeridaDidWallet(wallet, blockchainAnchor, wallet.address, wallet.publicKey, wallet.privateKey) } /** The public key as a buffer */ - public get publicKeyBuffer(): Uint8Array { - return Buffer.from(this.address.substr(2), 'hex') + public get publicKeyBuffer(): Uint8Array | undefined { + return this.publicKey ? Buffer.from(this.publicKey.substr(2), 'hex') : undefined } /** The public key encoded in base58 */ - public get publicKeyBase58(): string { - return utils.base58.encode(this.address) + public get publicKeyBase58(): string | undefined { + return this.publicKey ? utils.base58.encode(this.publicKey) : undefined } /** The private key as a buffer if available */ diff --git a/packages/encryption-utils/src/index.ts b/packages/encryption-utils/src/index.ts index 350cb54f..faa86ceb 100644 --- a/packages/encryption-utils/src/index.ts +++ b/packages/encryption-utils/src/index.ts @@ -15,7 +15,7 @@ const newKey = (length: number) => randomBytes(length ? length : secretbox.keyLe /** * Utilizes `tweetnacl` for symmetric and asymmetric encryption. - * + * * Utilizes `keccak256` algorithm to hash signed data and `secp256k1` signature algorithm for the resulting signature. */ export default class EncryptionUtils { @@ -123,11 +123,11 @@ export default class EncryptionUtils { } /** - * - * @param data - * @param signature + * + * @param data + * @param signature * @param publicKey Hex encoded public key or public key in shortened address format - * @returns + * @returns */ static verifySig(data: any, signature: string, publicKeyOrAddress: string) { const signerAddress = EncryptionUtils.getSigner(data, signature) @@ -135,7 +135,7 @@ export default class EncryptionUtils { return true } - const expectedAddress = utils.computeAddress(publicKeyOrAddress) + const expectedAddress = publicKeyOrAddress.length === 132 ? utils.computeAddress(publicKeyOrAddress) : publicKeyOrAddress return signerAddress.toLowerCase() == expectedAddress.toLowerCase() } @@ -162,7 +162,7 @@ export default class EncryptionUtils { return encodeBase64(data) } - static hash(data: any) { + static hash(data: any) { if (typeof(data) === 'string') { if (!isHexString(data)) { data = utils.toUtf8Bytes(data) @@ -199,4 +199,4 @@ export default class EncryptionUtils { const add = utils.computeAddress(publicKeyHex) return add } -} \ No newline at end of file +} diff --git a/packages/vda-did/src/vdaDid.ts b/packages/vda-did/src/vdaDid.ts index 2ce90427..bf37e06d 100644 --- a/packages/vda-did/src/vdaDid.ts +++ b/packages/vda-did/src/vdaDid.ts @@ -45,7 +45,7 @@ export default class VdaDid { } // Sign the DID Document - didDocument.signProof(this.options.signer) + await didDocument.signProof(this.options.signer) // Submit to all the endpoints const promises = [] @@ -122,7 +122,7 @@ export default class VdaDid { throw new Error(`Unable to update DID Document. "updated" timestamp matches "created" timestamp.`) } - didDocument.signProof(this.options.signer) + await didDocument.signProof(this.options.signer) // Fetch the endpoint list from the blockchain const response: any = await this.blockchain.lookup(didDocument.id) From 9bdd066287233fb707ddd0817948ba3a7e9d12ec Mon Sep 17 00:00:00 2001 From: aurelticot Date: Wed, 2 Apr 2025 15:49:29 +1100 Subject: [PATCH 12/12] Add comments to DID document for future work on Signer --- packages/did-document/src/did-document.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/did-document/src/did-document.ts b/packages/did-document/src/did-document.ts index 28b03dd8..deefee71 100644 --- a/packages/did-document/src/did-document.ts +++ b/packages/did-document/src/did-document.ts @@ -22,6 +22,7 @@ export default class DIDDocument implements IDIDDocument { // We are creating a new DID Document // Make sure we have a public key if (!publicKeyHex || publicKeyHex.length != 132) { + // TODO: If the public key is mandatory, don't make it optional in the arguments throw new Error('Unable to create DID Document. Invalid or non-existent public key.') } @@ -44,14 +45,17 @@ export default class DIDDocument implements IDIDDocument { `${this.doc.id}#controller`, this.doc.id ] + this.doc.verificationMethod = [ // From vda-did-resolver/resolver.ts #322 { id: `${this.doc.id}#controller`, type: VerificationMethodTypes.EcdsaSecp256k1RecoveryMethod2020, controller: this.doc.id, + // FIXME: Remove the `@` + use the actual chainId number rather than the hex version blockchainAccountId: `@eip155:${chainId}:${address}`, }, + // TODO: Challenge adding a verification method with the public key, as it is not always available, from a signer for instance. { id: this.doc.id, type: "EcdsaSecp256k1VerificationKey2019",