From f99ee4103c086d51c3944153428e20a32af4062c Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Fri, 26 Jul 2024 13:31:08 +0600 Subject: [PATCH 1/2] fix: Cere Wallet signer not triggering 3rd party extensions consent --- packages/blockchain/src/Blockchain.ts | 48 +++++++-------- .../blockchain/src/Signer/CereWalletSigner.ts | 58 ++++++++++++------- 2 files changed, 63 insertions(+), 43 deletions(-) diff --git a/packages/blockchain/src/Blockchain.ts b/packages/blockchain/src/Blockchain.ts index 3878ed77..8e2d7fce 100644 --- a/packages/blockchain/src/Blockchain.ts +++ b/packages/blockchain/src/Blockchain.ts @@ -1,5 +1,5 @@ import { ApiPromise, WsProvider } from '@polkadot/api'; -import { AddressOrPair, SignerOptions } from '@polkadot/api/types'; +import { AddressOrPair, SignerOptions, ApiOptions } from '@polkadot/api/types'; import { Index, AccountInfo } from '@polkadot/types/interfaces'; import { SubmittableExtrinsic } from '@polkadot/api-base/types'; import { formatBalance } from '@polkadot/util'; @@ -22,6 +22,7 @@ export type BlockchainConnectOptions = } | { wsEndpoint: string; + apiOptions?: Omit; }; /** @@ -39,56 +40,57 @@ export type BlockchainConnectOptions = * ``` */ export class Blockchain { - private readonly apiPromise: ApiPromise; + readonly api: ApiPromise; /** * The DDC Nodes pallet. * * @category Pallets */ - public readonly ddcNodes: DDCNodesPallet; + readonly ddcNodes: DDCNodesPallet; /** * The DDC Clusters pallet. * * @category Pallets */ - public readonly ddcClusters: DDCClustersPallet; + readonly ddcClusters: DDCClustersPallet; /** * The DDC Staking pallet. * * @category Pallets */ - public readonly ddcStaking: DDCStakingPallet; + readonly ddcStaking: DDCStakingPallet; /** * The DDC Customers pallet. * * @category Pallets */ - public readonly ddcCustomers: DDCCustomersPallet; + readonly ddcCustomers: DDCCustomersPallet; /** * The DDC Cluster government pallet. * * @category Pallets */ - public readonly ddcClustersGov: DDCClustersGovPallet; + readonly ddcClustersGov: DDCClustersGovPallet; constructor(options: BlockchainConnectOptions) { - this.apiPromise = + this.api = 'apiPromise' in options ? options.apiPromise : new ApiPromise({ provider: new WsProvider(options.wsEndpoint), + ...options.apiOptions, }); - this.ddcNodes = new DDCNodesPallet(this.apiPromise); - this.ddcClusters = new DDCClustersPallet(this.apiPromise); - this.ddcStaking = new DDCStakingPallet(this.apiPromise); - this.ddcCustomers = new DDCCustomersPallet(this.apiPromise); - this.ddcClustersGov = new DDCClustersGovPallet(this.apiPromise); + this.ddcNodes = new DDCNodesPallet(this.api); + this.ddcClusters = new DDCClustersPallet(this.api); + this.ddcStaking = new DDCStakingPallet(this.api); + this.ddcCustomers = new DDCCustomersPallet(this.api); + this.ddcClustersGov = new DDCClustersGovPallet(this.api); } /** @@ -121,7 +123,7 @@ export class Blockchain { * ``` */ async isReady() { - await this.apiPromise.isReady; + await this.api.isReady; return true; } @@ -130,7 +132,7 @@ export class Blockchain { * The decimals of the chain's native token. */ get chainDecimals() { - const [decimals] = this.apiPromise.registry.chainDecimals; + const [decimals] = this.api.registry.chainDecimals; return decimals; } @@ -149,7 +151,7 @@ export class Blockchain { * ``` */ async getNextNonce(address: string | AccountId) { - const nonce = await this.apiPromise.rpc.system.accountNextIndex(address); + const nonce = await this.api.rpc.system.accountNextIndex(address); return nonce.toNumber(); } @@ -204,7 +206,7 @@ export class Blockchain { let errorMessage: string; if (result.dispatchError.isModule) { - const decoded = this.apiPromise.registry.findMetaError(result.dispatchError.asModule); + const decoded = this.api.registry.findMetaError(result.dispatchError.asModule); errorMessage = `${decoded.section}.${decoded.name}: ${decoded.docs.join(' ')}`; } else { errorMessage = result.dispatchError.toString(); @@ -238,7 +240,7 @@ export class Blockchain { * ``` */ batchSend(sendables: Sendable[], options: SendOptions) { - return this.send(this.apiPromise.tx.utility.batch(sendables), options); + return this.send(this.api.tx.utility.batch(sendables), options); } /** @@ -263,11 +265,11 @@ export class Blockchain { * ``` */ batchAllSend(sendables: Sendable[], options: SendOptions) { - return this.send(this.apiPromise.tx.utility.batchAll(sendables), options); + return this.send(this.api.tx.utility.batchAll(sendables), options); } sudo(sendable: Sendable) { - return this.apiPromise.tx.sudo.sudo(sendable) as Sendable; + return this.api.tx.sudo.sudo(sendable) as Sendable; } /** @@ -281,7 +283,7 @@ export class Blockchain { * ``` */ disconnect() { - return this.apiPromise.disconnect(); + return this.api.disconnect(); } formatBalance(balance: string | number | bigint, withUnit: boolean | string = 'CERE') { @@ -302,7 +304,7 @@ export class Blockchain { * ``` */ async getAccountFreeBalance(accountId: AccountId) { - const { data } = await this.apiPromise.query.system.account(accountId); + const { data } = await this.api.query.system.account(accountId); return data.free.toBigInt(); } @@ -320,7 +322,7 @@ export class Blockchain { * ``` */ async getCurrentBlockNumber() { - const { number } = await this.apiPromise.rpc.chain.getHeader(); + const { number } = await this.api.rpc.chain.getHeader(); return number.toNumber(); } } diff --git a/packages/blockchain/src/Signer/CereWalletSigner.ts b/packages/blockchain/src/Signer/CereWalletSigner.ts index 07854b1d..91acf3ae 100644 --- a/packages/blockchain/src/Signer/CereWalletSigner.ts +++ b/packages/blockchain/src/Signer/CereWalletSigner.ts @@ -1,13 +1,11 @@ -import type { EmbedWallet, PermissionRequest } from '@cere/embed-wallet'; -import { inject } from '@cere/embed-wallet-inject'; +import type { EmbedWallet, WalletConnectOptions } from '@cere/embed-wallet'; -import { Web3Signer } from './Web3Signer'; +import { Signer, SignerType } from './Signer'; +import { Signer } from '@polkadot/types/types'; const CERE_WALLET_EXTENSION = 'Cere Wallet'; -export type CereWalletSignerOptions = { - permissions?: PermissionRequest; -}; +export type CereWalletSignerOptions = WalletConnectOptions; /** * Signer that uses Cere Wallet to sign messages. @@ -28,29 +26,49 @@ export type CereWalletSignerOptions = { * console.log(signature); * ``` */ -export class CereWalletSigner extends Web3Signer { - protected wallet: EmbedWallet; +export class CereWalletSigner extends Signer { + readonly type = 'ed25519'; + readonly isLocked = false; + + private currentAddress?: string; + private currentPublicKey?: Uint8Array; constructor( - wallet: EmbedWallet, + private wallet: EmbedWallet, private options: CereWalletSignerOptions = {}, ) { - super({ extensions: [CERE_WALLET_EXTENSION] }); + super(); + } + + get address() { + if (!this.currentAddress) { + throw new Error('Cere Wallet signer is not ready'); + } - this.wallet = wallet; + return this.currentAddress; } - async connect() { - await inject(this.wallet, { - name: CERE_WALLET_EXTENSION, - autoConnect: true, - permissions: this.options.permissions || { - ed25519_signRaw: {}, // Request permission to sign messages in the login process - }, - }); + get publicKey() { + if (!this.currentPublicKey) { + throw new Error('Cere Wallet signer is not ready'); + } + + return this.currentPublicKey; + } - await super.connect(); + getSigner(): Promise {} + isReady(): Promise { + return Promise.resolve(true); + } + + async sign(data: string): Promise { + return this.wallet.sign(data); + } + + async unlock(passphrase?: string) {} + + async connect() { return this; } } From 25bb663ad1f82e08681ea12cdbfdbf86c3e5910e Mon Sep 17 00:00:00 2001 From: Sergey Kambalin Date: Mon, 29 Jul 2024 13:57:40 +0600 Subject: [PATCH 2/2] fix: Cere Wallet signer do not request 3rd party wallet connection --- package-lock.json | 16 ++--- packages/blockchain/package.json | 2 +- .../blockchain/src/Signer/CereWalletSigner.ts | 64 ++++++++++--------- packages/ddc-client/src/DdcClient.ts | 53 +++++++++------ playground/package.json | 2 +- 5 files changed, 77 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index 734c415e..47b7ec45 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2022,9 +2022,9 @@ "link": true }, "node_modules/@cere/embed-wallet": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@cere/embed-wallet/-/embed-wallet-0.17.0.tgz", - "integrity": "sha512-Cw+INQaTHDdW0bjQ+m3rk0QVDaU9nm9ajNzGIpDxz9DATduoEzaa+qGxYypAxa8Jj8pSQ7pM/unoBMGZTKhE1g==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@cere/embed-wallet/-/embed-wallet-0.20.1.tgz", + "integrity": "sha512-rDE1PcQVQd1p0qonxDqZLokBzFlLK+yI4LBnJfsIcKk9Yt2yq405G5GO5YYJ+28oikS7Nkkx6Hn5yDLNNMAa3Q==", "dependencies": { "@cere/torus-embed": "0.2.8", "@types/bn.js": "^5.1.1", @@ -2033,9 +2033,9 @@ } }, "node_modules/@cere/embed-wallet-inject": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/@cere/embed-wallet-inject/-/embed-wallet-inject-0.18.2.tgz", - "integrity": "sha512-olJyqYF+Mwu6cpSvevUXMI5swcoazPLKLjgqtcIOd2mlq24DhxeOcFkNVS3Gntq7HmVmoSJXTuTbdk0fY3ViBg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@cere/embed-wallet-inject/-/embed-wallet-inject-0.20.1.tgz", + "integrity": "sha512-xvbvIuKNuwnODHXcy9Jq+nBOJ7FucjRMU+U8ZLbIwbwjcPLsZY7AjMUGgzPMLfmjnBSYdHcXZredTakzYyvVhw==", "dependencies": { "@polkadot/extension-inject": "^0.46.6" }, @@ -20890,7 +20890,7 @@ "version": "2.10.0", "license": "Apache-2.0", "dependencies": { - "@cere/embed-wallet-inject": "^0.18.2", + "@cere/embed-wallet-inject": "^0.20.1", "@polkadot/api": "^10.11.2", "@polkadot/api-base": "^10.11.2", "@polkadot/api-contract": "^10.11.2", @@ -21043,7 +21043,7 @@ "dependencies": { "@cere-ddc-sdk/blockchain": "2.10.0", "@cere-ddc-sdk/ddc-client": "2.12.0", - "@cere/embed-wallet": "^0.17.0", + "@cere/embed-wallet": "^0.20.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.15.16", diff --git a/packages/blockchain/package.json b/packages/blockchain/package.json index f9c98d98..9e1e99ac 100644 --- a/packages/blockchain/package.json +++ b/packages/blockchain/package.json @@ -31,7 +31,7 @@ "author": "Cere Network & { + connectOptions?: WalletConnectOptions; +}; /** * Signer that uses Cere Wallet to sign messages. @@ -26,49 +30,51 @@ export type CereWalletSignerOptions = WalletConnectOptions; * console.log(signature); * ``` */ -export class CereWalletSigner extends Signer { - readonly type = 'ed25519'; - readonly isLocked = false; - - private currentAddress?: string; - private currentPublicKey?: Uint8Array; +export class CereWalletSigner extends Web3Signer { + private extensionPromise: Promise; constructor( private wallet: EmbedWallet, private options: CereWalletSignerOptions = {}, ) { - super(); - } + super(options); - get address() { - if (!this.currentAddress) { - throw new Error('Cere Wallet signer is not ready'); - } + this.extensionPromise = enable(this.wallet, { autoConnect: false }).then((injected) => { + injected.accounts.get().then(this.setAccount); + injected.accounts.subscribe(this.setAccount); - return this.currentAddress; + return { ...injected, name: CERE_WALLET_EXTENSION, version: '0.20.0' }; + }); } - get publicKey() { - if (!this.currentPublicKey) { - throw new Error('Cere Wallet signer is not ready'); - } + private setAccount = ([account]: InjectedAccount[]) => { + this.injectedAccount = account; + }; - return this.currentPublicKey; + protected async getInjector() { + return this.extensionPromise; } - getSigner(): Promise {} + /** + * @inheritdoc + */ + async isReady() { + await cryptoWaitReady(); - isReady(): Promise { - return Promise.resolve(true); - } + if (this.injectedAccount) { + return true; + } + + if (this.autoConnect) { + await this.connect(this.options.connectOptions); + } - async sign(data: string): Promise { - return this.wallet.sign(data); + return true; } - async unlock(passphrase?: string) {} + async connect(options?: WalletConnectOptions) { + await this.wallet.connect(options); - async connect() { return this; } } diff --git a/packages/ddc-client/src/DdcClient.ts b/packages/ddc-client/src/DdcClient.ts index 35bb27db..f2417cc6 100644 --- a/packages/ddc-client/src/DdcClient.ts +++ b/packages/ddc-client/src/DdcClient.ts @@ -38,13 +38,30 @@ type DepositBalanceOptions = { * It provides methods to manage buckets, grant access, and store and read files and DAG nodes. */ export class DdcClient { - protected constructor( - private readonly ddcNode: NodeInterface, - private readonly blockchain: Blockchain, - private readonly fileStorage: FileStorage, - private readonly signer: Signer, - private readonly logger: Logger, - ) { + private readonly ddcNode: NodeInterface; + private readonly blockchain: Blockchain; + private readonly fileStorage: FileStorage; + private readonly signer: Signer; + private readonly logger: Logger; + + constructor(uriOrSigner: Signer | string, config: DdcClientConfig = DEFAULT_PRESET) { + const logger = createLogger('DdcClient', config); + const blockchain = + typeof config.blockchain === 'string' ? new Blockchain({ wsEndpoint: config.blockchain }) : config.blockchain; + + const signer = typeof uriOrSigner === 'string' ? new UriSigner(uriOrSigner) : uriOrSigner; + const router = config.nodes + ? new Router({ signer, nodes: config.nodes, logger }) + : new Router({ signer, blockchain, logger }); + + this.blockchain = blockchain; + this.signer = signer; + this.logger = logger; + this.ddcNode = new BalancedNode({ ...config, router, logger }); + this.fileStorage = new FileStorage(router, { ...config, logger }); + + logger.debug(config, 'DdcClient created'); + bindErrorLogger(this, this.logger, [ 'getBalance', 'depositBalance', @@ -80,27 +97,21 @@ export class DdcClient { * ``` */ static async create(uriOrSigner: Signer | string, config: DdcClientConfig = DEFAULT_PRESET) { - const logger = createLogger('DdcClient', config); - const signer = typeof uriOrSigner === 'string' ? new UriSigner(uriOrSigner) : uriOrSigner; - const blockchain = - typeof config.blockchain === 'string' - ? await Blockchain.connect({ wsEndpoint: config.blockchain }) - : config.blockchain; + const client = new DdcClient(uriOrSigner, config); - const router = config.nodes - ? new Router({ signer, nodes: config.nodes, logger }) - : new Router({ signer, blockchain, logger }); + return client.connect(); + } - const ddcNode = new BalancedNode({ ...config, router, logger }); - const fileStorage = new FileStorage(router, { ...config, logger }); + async connect() { + await this.blockchain.isReady(); - logger.debug(config, 'DdcClient created'); - - return new DdcClient(ddcNode, blockchain, fileStorage, signer, logger); + return this; } async disconnect() { await this.blockchain.disconnect(); + + return this; } /** diff --git a/playground/package.json b/playground/package.json index fe223717..4c08a995 100644 --- a/playground/package.json +++ b/playground/package.json @@ -13,7 +13,7 @@ "dependencies": { "@cere-ddc-sdk/blockchain": "2.10.0", "@cere-ddc-sdk/ddc-client": "2.12.0", - "@cere/embed-wallet": "^0.17.0", + "@cere/embed-wallet": "^0.20.1", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.15.16",