From 9a16db3b78f3978dbb2016c2285e9c8b21db6004 Mon Sep 17 00:00:00 2001 From: AnhMTV Date: Thu, 6 Jun 2024 09:11:30 +0700 Subject: [PATCH 01/43] Update EVM provider folder structure --- .../page/{SubWalleEvmProvider.ts => evm/OpenBitEvmProvider.ts} | 0 packages/extension-base/src/page/index.ts | 2 +- packages/extension-koni-ui/src/contexts/InjectContext.tsx | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename packages/extension-base/src/page/{SubWalleEvmProvider.ts => evm/OpenBitEvmProvider.ts} (100%) diff --git a/packages/extension-base/src/page/SubWalleEvmProvider.ts b/packages/extension-base/src/page/evm/OpenBitEvmProvider.ts similarity index 100% rename from packages/extension-base/src/page/SubWalleEvmProvider.ts rename to packages/extension-base/src/page/evm/OpenBitEvmProvider.ts diff --git a/packages/extension-base/src/page/index.ts b/packages/extension-base/src/page/index.ts index 616ac992ecf..6577b763270 100644 --- a/packages/extension-base/src/page/index.ts +++ b/packages/extension-base/src/page/index.ts @@ -5,7 +5,7 @@ import type { MessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithNul import { ProviderError } from '@subwallet/extension-base/background/errors/ProviderError'; import { ProviderErrorType } from '@subwallet/extension-base/background/KoniTypes'; -import { OpenBitEvmProvider } from '@subwallet/extension-base/page/SubWalleEvmProvider'; +import { OpenBitEvmProvider } from '@subwallet/extension-base/page/evm/OpenBitEvmProvider'; import { EvmProvider } from '@subwallet/extension-inject/types'; import { MESSAGE_ORIGIN_PAGE } from '../defaults'; diff --git a/packages/extension-koni-ui/src/contexts/InjectContext.tsx b/packages/extension-koni-ui/src/contexts/InjectContext.tsx index 90c772514c6..5da47aceebb 100644 --- a/packages/extension-koni-ui/src/contexts/InjectContext.tsx +++ b/packages/extension-koni-ui/src/contexts/InjectContext.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { OpenBitEvmProvider } from '@subwallet/extension-base/page/SubWalleEvmProvider'; +import { OpenBitEvmProvider } from '@subwallet/extension-base/page/evm/OpenBitEvmProvider'; import { addLazy } from '@subwallet/extension-base/utils'; import { EvmProvider, Injected, InjectedAccountWithMeta, InjectedWindowProvider, Unsubcall } from '@subwallet/extension-inject/types'; import { DisconnectExtensionModal } from '@subwallet/extension-koni-ui/components'; From 148107d4ec2ea95b541aa884de25e0a9a1ef5bc8 Mon Sep 17 00:00:00 2001 From: AnhMTV Date: Mon, 10 Jun 2024 17:21:06 +0700 Subject: [PATCH 02/43] Init provider core --- packages/extension-base/package.json | 5 +- .../src/background/KoniTypes.ts | 12 + .../background/errors/BitcoinProviderError.ts | 50 ++++ .../extension-base/src/background/types.ts | 2 +- .../src/koni/background/handlers/Tabs.ts | 105 +++++-- .../src/page/bitcoin/OpenBitProvider.ts | 62 ++++ packages/extension-base/src/page/index.ts | 81 +----- packages/extension-base/src/page/message.ts | 74 +++++ packages/extension-base/src/page/params.ts | 4 + .../src/page/{ => substrate}/Accounts.ts | 2 +- .../src/page/{ => substrate}/Injected.ts | 0 .../src/page/{ => substrate}/Metadata.ts | 0 .../{ => substrate}/PostMessageProvider.ts | 0 .../src/page/{ => substrate}/Signer.ts | 0 packages/extension-inject/src/bundle.ts | 8 + packages/extension-inject/src/types.ts | 2 + packages/extension-koni/src/page.ts | 7 +- yarn.lock | 274 +++++++++++++++++- 18 files changed, 585 insertions(+), 103 deletions(-) create mode 100644 packages/extension-base/src/background/errors/BitcoinProviderError.ts create mode 100644 packages/extension-base/src/page/bitcoin/OpenBitProvider.ts create mode 100644 packages/extension-base/src/page/message.ts create mode 100644 packages/extension-base/src/page/params.ts rename packages/extension-base/src/page/{ => substrate}/Accounts.ts (95%) rename packages/extension-base/src/page/{ => substrate}/Injected.ts (100%) rename packages/extension-base/src/page/{ => substrate}/Metadata.ts (100%) rename packages/extension-base/src/page/{ => substrate}/PostMessageProvider.ts (100%) rename packages/extension-base/src/page/{ => substrate}/Signer.ts (100%) diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json index 64ca8dac457..34b1a6d43bc 100644 --- a/packages/extension-base/package.json +++ b/packages/extension-base/package.json @@ -48,6 +48,7 @@ "@polkadot/x-global": "^12.6.1", "@reduxjs/toolkit": "^1.9.1", "@sora-substrate/type-definitions": "^1.17.7", + "@stacks/connect": "^7.7.1", "@substrate/connect": "^0.7.26", "@subwallet/chain-list": "0.2.43", "@subwallet/extension-base": "^1.1.43-0", @@ -89,9 +90,11 @@ "web3-core-subscriptions": "1.10.0", "web3-eth": "1.10.0", "web3-eth-contract": "^1.10.0", - "web3-utils": "^1.10.0" + "web3-utils": "^1.10.0", + "yup": "^1.4.0" }, "devDependencies": { + "@btckit/types": "^0.0.19", "@types/uuid": "^9.0.1" } } diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 14ede7769f5..bca485eb0de 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1287,6 +1287,16 @@ export enum EvmProviderErrorType { INTERNAL_ERROR = 'INTERNAL_ERROR', } +export enum BitcoinProviderErrorType { + USER_REJECTED_REQUEST = 'USER_REJECTED_REQUEST', + UNAUTHORIZED = 'UNAUTHORIZED', + UNSUPPORTED_METHOD = 'UNSUPPORTED_METHOD', + DISCONNECTED = 'DISCONNECTED', + CHAIN_DISCONNECTED = 'CHAIN_DISCONNECTED', + INVALID_PARAMS = 'INVALID_PARAMS', + INTERNAL_ERROR = 'INTERNAL_ERROR', +} + export interface EvmSendTransactionParams { from: string; to?: string; @@ -2497,6 +2507,8 @@ export interface KoniRequestSignatures { 'pri(account.external.reject)': [RequestRejectExternalRequest, ResponseRejectExternalRequest]; 'pri(account.external.resolve)': [RequestResolveExternalRequest, ResponseResolveExternalRequest]; + 'bitcoin(request)': [RequestArguments, unknown]; + // Evm 'evm(events.subscribe)': [RequestEvmEvents, boolean, EvmEvent]; 'evm(request)': [RequestArguments, unknown]; diff --git a/packages/extension-base/src/background/errors/BitcoinProviderError.ts b/packages/extension-base/src/background/errors/BitcoinProviderError.ts new file mode 100644 index 00000000000..6938b287b99 --- /dev/null +++ b/packages/extension-base/src/background/errors/BitcoinProviderError.ts @@ -0,0 +1,50 @@ +// Copyright 2019-2022 @subwallet/extension-koni authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { SWError } from '@subwallet/extension-base/background/errors/SWError'; +import { BitcoinProviderErrorType } from '@subwallet/extension-base/background/KoniTypes'; +import { detectTranslate } from '@subwallet/extension-base/utils'; +import { t } from 'i18next'; + +const defaultErrorMap: Record = { + USER_REJECTED_REQUEST: { + message: detectTranslate('User Rejected Request'), + code: 4001 + }, + UNAUTHORIZED: { + message: detectTranslate('Failed to sign'), + code: 4100 + }, + UNSUPPORTED_METHOD: { + message: detectTranslate('Unsupported Method'), + code: 4200 + }, + DISCONNECTED: { + message: detectTranslate('Network is disconnected'), + code: 4900 + }, + CHAIN_DISCONNECTED: { + message: detectTranslate('Network is disconnected'), + code: 4901 + }, + INVALID_PARAMS: { + message: detectTranslate('Undefined error. Please contact OpenBit support'), + code: -32602 + }, + INTERNAL_ERROR: { + message: detectTranslate('Undefined error. Please contact OpenBit support'), + code: -32603 + } +}; + +export class BitcoinProviderError extends SWError { + override errorType: BitcoinProviderErrorType; + + constructor (errorType: BitcoinProviderErrorType, errMessage?: string, data?: unknown) { + const { code, message } = defaultErrorMap[errorType]; + const finalMessage = errMessage || t(message || '') || errorType; + + super(errorType, finalMessage, code, data); + this.errorType = errorType; + } +} diff --git a/packages/extension-base/src/background/types.ts b/packages/extension-base/src/background/types.ts index 7c0130f9f57..f074f7d3b54 100644 --- a/packages/extension-base/src/background/types.ts +++ b/packages/extension-base/src/background/types.ts @@ -195,7 +195,7 @@ export interface TransportRequestMessage { request: RequestTypes[TMessageType]; } -export type AccountAuthType = 'substrate' | 'evm' | 'both'; +export type AccountAuthType = 'substrate' | 'evm' | 'both' | 'bitcoin'; export interface RequestAuthorizeTab { origin: string; diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index be3803c075b..5c1fc5dcb8c 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -4,11 +4,12 @@ import type { InjectedAccount } from '@subwallet/extension-inject/types'; import { _AssetType } from '@subwallet/chain-list/types'; +import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError'; import { EvmProviderError } from '@subwallet/extension-base/background/errors/EvmProviderError'; import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; import { createSubscription, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AddNetworkRequestExternal, AddTokenRequestExternal, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; +import { AddNetworkRequestExternal, AddTokenRequestExternal, BitcoinProviderErrorType, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; import RequestBytesSign from '@subwallet/extension-base/background/RequestBytesSign'; import RequestExtrinsicSign from '@subwallet/extension-base/background/RequestExtrinsicSign'; import { AccountAuthType, MessageTypes, RequestAccountList, RequestAccountSubscribe, RequestAccountUnsubscribe, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '@subwallet/extension-base/background/types'; @@ -1058,27 +1059,75 @@ export default class KoniTabs { return await this.#koniState.addTokenConfirm(id, url, tokenInfo); } - public async handle (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise { - console.log('handle', type, request, url, port); + async bitcoinGetAddress (url: string, request: RequestArguments): Promise { + try { + const result = this.#koniState.authorizeUrlV2(url, { + origin: '', + accountAuthType: 'bitcoin' + }); - if (type === 'pub(phishing.redirectIfDenied)') { - return this.redirectIfPhishing(url); + return result; + } catch (e) { + throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST); } + } - // Wait for account ready and chain ready - await Promise.all([this.#koniState.eventService.waitAccountReady, this.#koniState.eventService.waitChainReady]); + private async handleBitcoinRequest (id: string, url: string, request: RequestArguments, port: chrome.runtime.Port): Promise { + const { method } = request; - if (type !== 'pub(authorize.tabV2)' && !this.isEvmPublicRequest(type, request as RequestArguments)) { + try { + switch (method) { + case 'getAddress': + return await this.bitcoinGetAddress(url, request); + + default: + return this.performWeb3Method(id, url, request); + } + } catch (e) { + // @ts-ignore + if (e.code) { + throw e; + } else { + console.error(e); + throw new BitcoinProviderError(BitcoinProviderErrorType.INTERNAL_ERROR, e?.toString()); + } + } + } + + public async handleBitcoin (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise { + switch (type) { + case 'bitcoin(request)': + return await this.handleBitcoinRequest(id, url, request as RequestArguments, port); + default: + throw new Error(`Unable to handle message of type ${type}`); + } + } + + public async handleEvm (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise { + if (!this.isEvmPublicRequest(type, request as RequestArguments)) { await this.#koniState.ensureUrlAuthorizedV2(url) .catch((e: Error) => { - if (type.startsWith('evm')) { - throw new EvmProviderError(EvmProviderErrorType.INTERNAL_ERROR, e.message); - } else { - throw e; - } + throw new EvmProviderError(EvmProviderErrorType.INTERNAL_ERROR, e.message); }); } + switch (type) { + case 'evm(events.subscribe)': + return await this.evmSubscribeEvents(url, id, port); + case 'evm(request)': + return await this.handleEvmRequest(id, url, request as RequestArguments); + case 'evm(provider.send)': + return await this.handleEvmSend(id, url, port, request as RequestEvmProviderSend); + default: + throw new Error(`Unable to handle message of type ${type}`); + } + } + + public async handleSubstrate (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise { + if (type !== 'pub(authorize.tabV2)' && !this.isEvmPublicRequest(type, request as RequestArguments)) { + await this.#koniState.ensureUrlAuthorizedV2(url); + } + switch (type) { /// Clone from PolkadotJs case 'pub(bytes.sign)': @@ -1117,23 +1166,39 @@ export default class KoniTabs { case 'pub(token.add)': return this.addPspToken(id, url, request as RequestAddPspToken); - /// case 'pub(authorize.tabV2)': return this.authorizeV2(url, request as RequestAuthorizeTab); + case 'pub(accounts.listV2)': return this.accountsListV2(url, request as RequestAccountList); + case 'pub(accounts.subscribeV2)': return this.accountsSubscribeV2(url, request as RequestAccountSubscribe, id, port); + case 'pub(accounts.unsubscribe)': return this.accountsUnsubscribe(url, request as RequestAccountUnsubscribe); - case 'evm(events.subscribe)': - return await this.evmSubscribeEvents(url, id, port); - case 'evm(request)': - return await this.handleEvmRequest(id, url, request as RequestArguments); - case 'evm(provider.send)': - return await this.handleEvmSend(id, url, port, request as RequestEvmProviderSend); + default: throw new Error(`Unable to handle message of type ${type}`); } } + + public async handle (id: string, type: TMessageType, request: RequestTypes[TMessageType], url: string, port: chrome.runtime.Port): Promise { + if (type === 'pub(phishing.redirectIfDenied)') { + return this.redirectIfPhishing(url); + } + + // Wait for account ready and chain ready + await Promise.all([this.#koniState.eventService.waitAccountReady, this.#koniState.eventService.waitChainReady]); + + if (type.startsWith('bitcoin(')) { + return this.handleBitcoin(id, type, request, url, port); + } else if (type.startsWith('evm(')) { + return this.handleEvm(id, type, request, url, port); + } else if (type.startsWith('pub(')) { + return this.handleSubstrate(id, type, request, url, port); + } else { + throw new Error(`Unable to handle message of type ${type}`); + } + } } diff --git a/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts b/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts new file mode 100644 index 00000000000..5ffffa64fa9 --- /dev/null +++ b/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts @@ -0,0 +1,62 @@ +// Copyright 2019-2022 @subwallet/extension authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { StacksProvider } from '@stacks/connect'; +import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError'; +import { version } from '@subwallet/extension-base/page/params'; + +import { sendMessage } from '../message'; + +export interface OpenBitProviderType extends StacksProvider { + isOpenBit: boolean; +} + +export const OpenBitProvider: OpenBitProviderType = { + isOpenBit: true, + getURL: () => { + // Implement this method + throw new Error('Method not implemented.'); + }, + authenticationRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + signatureRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + structuredDataSignatureRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + transactionRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + psbtRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + profileUpdateRequest: (payload: string) => { + // Implement this method + throw new Error('Method not implemented.'); + }, + request: (method: string, params?: any[] | undefined) => { + // Implement this method + return new Promise((resolve, reject) => { + sendMessage('bitcoin(request)', { method, params }) + .then((result) => { + resolve(result as Record); + }) + .catch((e: BitcoinProviderError) => { + reject(e); + }); + }); + }, + getProductInfo: () => { + return { + name: 'OpenBit', + version: version + }; + } +}; diff --git a/packages/extension-base/src/page/index.ts b/packages/extension-base/src/page/index.ts index 6577b763270..f9fcb76fb8a 100644 --- a/packages/extension-base/src/page/index.ts +++ b/packages/extension-base/src/page/index.ts @@ -1,57 +1,12 @@ -// Copyright 2019-2022 @polkadot/extension authors & contributors +// Copyright 2019-2022 @subwallet/extension authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { MessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithNullRequest, MessageTypesWithSubscriptions, RequestTypes, ResponseTypes, SubscriptionMessageTypes, TransportRequestMessage, TransportResponseMessage } from '../background/types'; - -import { ProviderError } from '@subwallet/extension-base/background/errors/ProviderError'; -import { ProviderErrorType } from '@subwallet/extension-base/background/KoniTypes'; import { OpenBitEvmProvider } from '@subwallet/extension-base/page/evm/OpenBitEvmProvider'; import { EvmProvider } from '@subwallet/extension-inject/types'; -import { MESSAGE_ORIGIN_PAGE } from '../defaults'; -import { getId } from '../utils/getId'; -import Injected from './Injected'; -// when sending a message from the injector to the extension, we -// - create an event - this we send to the loader -// - the loader takes this event and uses port.postMessage to background -// - on response, the loader creates a reponse event -// - this injector, listens on the events, maps it to the original -// - resolves/rejects the promise with the result (or sub data) - -export interface Handler { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - resolve: (data?: any) => void; - reject: (error: Error) => void; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - subscriber?: (data: any) => void; -} - -export type Handlers = Record; - -const handlers: Handlers = {}; - -// a generic message sender that creates an event, returning a promise that will -// resolve once the event is resolved (by the response listener just below this) -export function sendMessage(message: TMessageType): Promise; -export function sendMessage(message: TMessageType, request: RequestTypes[TMessageType]): Promise; -export function sendMessage(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise; - -export function sendMessage (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise { - return new Promise((resolve, reject): void => { - const id = getId(); - - handlers[id] = { reject, resolve, subscriber }; - - const transportRequestMessage: TransportRequestMessage = { - id, - message, - origin: MESSAGE_ORIGIN_PAGE, - request: request || null as RequestTypes[TMessageType] - }; - - window.postMessage(transportRequestMessage, '*'); - }); -} +import Injected from './substrate/Injected'; +import { sendMessage } from './message'; +import { version } from './params'; // the enable function, called by the dapp to allow access export async function enable (origin: string): Promise { @@ -60,29 +15,9 @@ export async function enable (origin: string): Promise { return new Injected(sendMessage); } -export function handleResponse (data: TransportResponseMessage & { subscription?: string }): void { - const handler = handlers[data.id]; - - if (!handler) { - console.error(`Unknown response: ${JSON.stringify(data)}`); - - return; - } - - if (!handler.subscriber) { - delete handlers[data.id]; - } - - if (data.subscription) { - // eslint-disable-next-line @typescript-eslint/ban-types - (handler.subscriber as Function)(data.subscription); - } else if (data.error) { - handler.reject(new ProviderError(ProviderErrorType.INTERNAL_ERROR, data.error, data.errorCode)); - } else { - handler.resolve(data.response); - } -} - -export function initEvmProvider (version: string): EvmProvider { +export function initEvmProvider (): EvmProvider { return new OpenBitEvmProvider(sendMessage, version); } + +export * from './message'; +export * from './bitcoin/OpenBitProvider'; diff --git a/packages/extension-base/src/page/message.ts b/packages/extension-base/src/page/message.ts new file mode 100644 index 00000000000..702609a51fd --- /dev/null +++ b/packages/extension-base/src/page/message.ts @@ -0,0 +1,74 @@ +// Copyright 2019-2022 @subwallet/extension authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { MessageTypes, MessageTypesWithNoSubscriptions, MessageTypesWithNullRequest, MessageTypesWithSubscriptions, RequestTypes, ResponseTypes, SubscriptionMessageTypes, TransportRequestMessage, TransportResponseMessage } from '../background/types'; + +import { ProviderError } from '@subwallet/extension-base/background/errors/ProviderError'; +import { ProviderErrorType } from '@subwallet/extension-base/background/KoniTypes'; + +import { MESSAGE_ORIGIN_PAGE } from '../defaults'; +import { getId } from '../utils/getId'; +// when sending a message from the injector to the extension, we +// - create an event - this we send to the loader +// - the loader takes this event and uses port.postMessage to background +// - on response, the loader creates a reponse event +// - this injector, listens on the events, maps it to the original +// - resolves/rejects the promise with the result (or sub data) + +export interface Handler { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + resolve: (data?: any) => void; + reject: (error: Error) => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + subscriber?: (data: any) => void; +} + +export type Handlers = Record; + +const handlers: Handlers = {}; + +// a generic message sender that creates an event, returning a promise that will +// resolve once the event is resolved (by the response listener just below this) +export function sendMessage(message: TMessageType): Promise; +export function sendMessage(message: TMessageType, request: RequestTypes[TMessageType]): Promise; +export function sendMessage(message: TMessageType, request: RequestTypes[TMessageType], subscriber: (data: SubscriptionMessageTypes[TMessageType]) => void): Promise; + +export function sendMessage (message: TMessageType, request?: RequestTypes[TMessageType], subscriber?: (data: unknown) => void): Promise { + return new Promise((resolve, reject): void => { + const id = getId(); + + handlers[id] = { reject, resolve, subscriber }; + + const transportRequestMessage: TransportRequestMessage = { + id, + message, + origin: MESSAGE_ORIGIN_PAGE, + request: request || null as RequestTypes[TMessageType] + }; + + window.postMessage(transportRequestMessage, '*'); + }); +} + +export function handleResponse (data: TransportResponseMessage & { subscription?: string }): void { + const handler = handlers[data.id]; + + if (!handler) { + console.error(`Unknown response: ${JSON.stringify(data)}`); + + return; + } + + if (!handler.subscriber) { + delete handlers[data.id]; + } + + if (data.subscription) { + // eslint-disable-next-line @typescript-eslint/ban-types + (handler.subscriber as Function)(data.subscription); + } else if (data.error) { + handler.reject(new ProviderError(ProviderErrorType.INTERNAL_ERROR, data.error, data.errorCode)); + } else { + handler.resolve(data.response); + } +} diff --git a/packages/extension-base/src/page/params.ts b/packages/extension-base/src/page/params.ts new file mode 100644 index 00000000000..993a8df1db2 --- /dev/null +++ b/packages/extension-base/src/page/params.ts @@ -0,0 +1,4 @@ +// Copyright 2019-2022 @subwallet/extension authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export const version = process.env.PKG_VERSION as string || '0.0.0'; diff --git a/packages/extension-base/src/page/Accounts.ts b/packages/extension-base/src/page/substrate/Accounts.ts similarity index 95% rename from packages/extension-base/src/page/Accounts.ts rename to packages/extension-base/src/page/substrate/Accounts.ts index 92639a678c2..a9f0cca3fb6 100644 --- a/packages/extension-base/src/page/Accounts.ts +++ b/packages/extension-base/src/page/substrate/Accounts.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { InjectedAccount, InjectedAccounts, Unsubcall } from '@subwallet/extension-inject/types'; -import type { SendRequest } from './types'; +import type { SendRequest } from '../types'; // External to class, this.# is not private enough (yet) let sendRequest: SendRequest; diff --git a/packages/extension-base/src/page/Injected.ts b/packages/extension-base/src/page/substrate/Injected.ts similarity index 100% rename from packages/extension-base/src/page/Injected.ts rename to packages/extension-base/src/page/substrate/Injected.ts diff --git a/packages/extension-base/src/page/Metadata.ts b/packages/extension-base/src/page/substrate/Metadata.ts similarity index 100% rename from packages/extension-base/src/page/Metadata.ts rename to packages/extension-base/src/page/substrate/Metadata.ts diff --git a/packages/extension-base/src/page/PostMessageProvider.ts b/packages/extension-base/src/page/substrate/PostMessageProvider.ts similarity index 100% rename from packages/extension-base/src/page/PostMessageProvider.ts rename to packages/extension-base/src/page/substrate/PostMessageProvider.ts diff --git a/packages/extension-base/src/page/Signer.ts b/packages/extension-base/src/page/substrate/Signer.ts similarity index 100% rename from packages/extension-base/src/page/Signer.ts rename to packages/extension-base/src/page/substrate/Signer.ts diff --git a/packages/extension-inject/src/bundle.ts b/packages/extension-inject/src/bundle.ts index f9741102824..f38b4534895 100644 --- a/packages/extension-inject/src/bundle.ts +++ b/packages/extension-inject/src/bundle.ts @@ -3,6 +3,8 @@ import type { Injected, InjectedWindow, InjectOptions } from './types'; +import { OpenBitProvider } from '@subwallet/extension-base/page'; + import { EIP6963ProviderDetail, EIP6963ProviderInfo, EvmProvider } from './types'; export { packageInfo } from './packageInfo'; @@ -91,3 +93,9 @@ export const inject6963EIP = (provider: EvmProvider) => { announceProvider(); }; + +export function injectBitcoinProvider () { + const windowInject = window as Window & InjectedWindow; + + windowInject.OpenBitProvider = OpenBitProvider; +} diff --git a/packages/extension-inject/src/types.ts b/packages/extension-inject/src/types.ts index b861d90b453..f6cc41f2b52 100644 --- a/packages/extension-inject/src/types.ts +++ b/packages/extension-inject/src/types.ts @@ -5,6 +5,7 @@ import type { Signer as InjectedSigner } from '@polkadot/api/types'; import type { ProviderInterface } from '@polkadot/rpc-provider/types'; import type { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types'; +import { OpenBitProviderType } from '@subwallet/extension-base/page'; import { KeypairType } from '@subwallet/keyring/types'; // eslint-disable-next-line no-undef @@ -115,6 +116,7 @@ export interface InjectedWindow extends This { injectedWeb3: Record; ethereum: EvmProvider; OpenBit: EvmProvider; + OpenBitProvider: OpenBitProviderType; } export type InjectedExtension = InjectedExtensionInfo & Injected; diff --git a/packages/extension-koni/src/page.ts b/packages/extension-koni/src/page.ts index 31dd53a8510..c90a38c269d 100644 --- a/packages/extension-koni/src/page.ts +++ b/packages/extension-koni/src/page.ts @@ -6,16 +6,15 @@ import type { Message } from '@subwallet/extension-base/types'; import { MESSAGE_ORIGIN_CONTENT } from '@subwallet/extension-base/defaults'; import { handleResponse, initEvmProvider } from '@subwallet/extension-base/page'; -import { injectEvmExtension } from '@subwallet/extension-inject'; - -const version = process.env.PKG_VERSION as string; +import { injectBitcoinProvider, injectEvmExtension } from '@subwallet/extension-inject'; function inject () { // injectExtension(enable, { // name: 'subwallet-js', // version: version // }); - injectEvmExtension(initEvmProvider(version)); + injectEvmExtension(initEvmProvider()); + injectBitcoinProvider(); } // setup a response listener (events created by the loader for extension responses) diff --git a/yarn.lock b/yarn.lock index 41abfa79901..6027b665413 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1839,6 +1839,13 @@ __metadata: languageName: node linkType: hard +"@btckit/types@npm:^0.0.19": + version: 0.0.19 + resolution: "@btckit/types@npm:0.0.19" + checksum: b13bcda6b53b9d6ea76a9a9602efabecb26c36ec4d4439a7ae7e7e68ba8b4903411932f394599e55a824d88b518a9b7ad115f764e03ae8204eaa6b6107106591 + languageName: node + linkType: hard + "@coinbase/cbpay-js@npm:^1.7.0": version: 1.7.0 resolution: "@coinbase/cbpay-js@npm:1.7.0" @@ -3790,6 +3797,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:1.1.5, @noble/hashes@npm:~1.1.1": + version: 1.1.5 + resolution: "@noble/hashes@npm:1.1.5" + checksum: de3f095a7ac1cbf5b4b3d09f193288d4f2eec35fbadf2ed9fd7e47d8a3042fef410052ba62dc0296a185f994c11192f5357fdb1bd9178c905efd82e946c53b00 + languageName: node + linkType: hard + "@noble/hashes@npm:1.3.3, @noble/hashes@npm:^1.3.3, @noble/hashes@npm:~1.3.2": version: 1.3.3 resolution: "@noble/hashes@npm:1.3.3" @@ -3797,6 +3811,13 @@ __metadata: languageName: node linkType: hard +"@noble/hashes@npm:^1.1.2": + version: 1.4.0 + resolution: "@noble/hashes@npm:1.4.0" + checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.2.0": version: 1.3.0 resolution: "@noble/hashes@npm:1.3.0" @@ -3804,7 +3825,7 @@ __metadata: languageName: node linkType: hard -"@noble/secp256k1@npm:1.7.1": +"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.6.3": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" checksum: d2301f1f7690368d8409a3152450458f27e54df47e3f917292de3de82c298770890c2de7c967d237eff9c95b70af485389a9695f73eb05a43e2bd562d18b18cb @@ -5841,6 +5862,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.0": + version: 1.1.6 + resolution: "@scure/base@npm:1.1.6" + checksum: d6deaae91deba99e87939af9e55d80edba302674983f32bba57f942e22b1726a83c62dc50d8f4370a5d5d35a212dda167fb169f4b0d0c297488d8604608fc3d3 + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.3": version: 1.3.3 resolution: "@scure/bip32@npm:1.3.3" @@ -5852,6 +5880,16 @@ __metadata: languageName: node linkType: hard +"@scure/bip39@npm:1.1.0": + version: 1.1.0 + resolution: "@scure/bip39@npm:1.1.0" + dependencies: + "@noble/hashes": ~1.1.1 + "@scure/base": ~1.1.0 + checksum: c4361406f092a45e511dc572c89f497af6665ad81cb3fd7bf78e6772f357f7ae885e129ef0b985cb3496a460b4811318f77bc61634d9b0a8446079a801b6003c + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.2": version: 1.2.2 resolution: "@scure/bip39@npm:1.2.2" @@ -6175,6 +6213,117 @@ __metadata: languageName: node linkType: hard +"@stacks/auth@npm:^6.1.1": + version: 6.15.0 + resolution: "@stacks/auth@npm:6.15.0" + dependencies: + "@stacks/common": ^6.13.0 + "@stacks/encryption": ^6.15.0 + "@stacks/network": ^6.13.0 + "@stacks/profile": ^6.15.0 + cross-fetch: ^3.1.5 + jsontokens: ^4.0.1 + checksum: bb891ac8d7b232a2ab234fff46dc488a9d99cd9899fd03af610573f43d394b59c6c427a67e1da792d7c6a8f885c80a172ca796bc80f01914b9e0264a5a6a9675 + languageName: node + linkType: hard + +"@stacks/common@npm:^6.13.0": + version: 6.13.0 + resolution: "@stacks/common@npm:6.13.0" + dependencies: + "@types/bn.js": ^5.1.0 + "@types/node": ^18.0.4 + checksum: 57a6b9494fa64e8301e6969bf5318921265dd346587bcd431c34ffdd25c4448ff9ceb1f85d79025948761a248d1cd1c5a702c719bbcf4394ed99a9a959626fa8 + languageName: node + linkType: hard + +"@stacks/connect-ui@npm:6.4.1": + version: 6.4.1 + resolution: "@stacks/connect-ui@npm:6.4.1" + dependencies: + "@stencil/core": ^2.17.1 + checksum: 1dee5e017cd13c9aa2591538bec9d830f92a0e17f4f684c3ee2ee9235ed3d8fb8a662d37ce6c06d5624f0d9409af5da9111449b2bb8259052d47d6ac5a343227 + languageName: node + linkType: hard + +"@stacks/connect@npm:^7.7.1": + version: 7.7.1 + resolution: "@stacks/connect@npm:7.7.1" + dependencies: + "@stacks/auth": ^6.1.1 + "@stacks/connect-ui": 6.4.1 + "@stacks/network": ^6.1.1 + "@stacks/profile": ^6.1.1 + "@stacks/transactions": ^6.1.1 + jsontokens: ^4.0.1 + checksum: 478f102376c5a687cb444690046dd83655e82a295731ac87406d76823088604cd973d583606f7462b0c1c478044a788724534e7b3c1f98b500567795434a44cd + languageName: node + linkType: hard + +"@stacks/encryption@npm:^6.15.0": + version: 6.15.0 + resolution: "@stacks/encryption@npm:6.15.0" + dependencies: + "@noble/hashes": 1.1.5 + "@noble/secp256k1": 1.7.1 + "@scure/bip39": 1.1.0 + "@stacks/common": ^6.13.0 + "@types/node": ^18.0.4 + base64-js: ^1.5.1 + bs58: ^5.0.0 + ripemd160-min: ^0.0.6 + varuint-bitcoin: ^1.1.2 + checksum: fb3c5f451456b4f4ac44f77fc1eb911129d0a17accbce619c121e41e349063edcb52a9c8a7b922ef257abdfb301daf7939ff6e7c8959f9799c99a853182292a0 + languageName: node + linkType: hard + +"@stacks/network@npm:^6.1.1, @stacks/network@npm:^6.13.0": + version: 6.13.0 + resolution: "@stacks/network@npm:6.13.0" + dependencies: + "@stacks/common": ^6.13.0 + cross-fetch: ^3.1.5 + checksum: cc5990005f6c518008a9885086ee89cadf6e3de3b813c6e6097a37878f1f7ad6edf48dae4899ac275e4944089b683aa67018be2855da2f5ba69ccef1cda2c30f + languageName: node + linkType: hard + +"@stacks/profile@npm:^6.1.1, @stacks/profile@npm:^6.15.0": + version: 6.15.0 + resolution: "@stacks/profile@npm:6.15.0" + dependencies: + "@stacks/common": ^6.13.0 + "@stacks/network": ^6.13.0 + "@stacks/transactions": ^6.15.0 + jsontokens: ^4.0.1 + schema-inspector: ^2.0.2 + zone-file: ^2.0.0-beta.3 + checksum: c8e1c369b27fe33b48756c6ee7c578491e9a574a0e7b33a47afc24deba797571c445cccd07c79129a697990f36f243754277eec739fbdfff43ecee311a81db44 + languageName: node + linkType: hard + +"@stacks/transactions@npm:^6.1.1, @stacks/transactions@npm:^6.15.0": + version: 6.15.0 + resolution: "@stacks/transactions@npm:6.15.0" + dependencies: + "@noble/hashes": 1.1.5 + "@noble/secp256k1": 1.7.1 + "@stacks/common": ^6.13.0 + "@stacks/network": ^6.13.0 + c32check: ^2.0.0 + lodash.clonedeep: ^4.5.0 + checksum: dd59560a4acc069621570e091df66eac1133b2fcc21d44eb865f179ccd7b1845d1e2f3f41bd48590fe2bc703ad3a19a424aec56e6d8325242c146461bbda15de + languageName: node + linkType: hard + +"@stencil/core@npm:^2.17.1": + version: 2.22.3 + resolution: "@stencil/core@npm:2.22.3" + bin: + stencil: bin/stencil + checksum: b43fd0c0e10c8f6831821635de650f49c8d94eab75aa8e9214b723511cfd1f90fbf1ad33a79b4d2f165ef9ca6bee02839aac7b0a234ec6082b5959f98069f397 + languageName: node + linkType: hard + "@subsocial/definitions@npm:0.8.13": version: 0.8.13 resolution: "@subsocial/definitions@npm:0.8.13" @@ -6259,6 +6408,7 @@ __metadata: "@acala-network/api": ^5.0.2 "@apollo/client": ^3.7.14 "@azns/resolver-core": ^1.4.0 + "@btckit/types": ^0.0.19 "@equilab/api": ~1.14.25 "@ethereumjs/common": ^4.1.0 "@ethereumjs/tx": ^5.1.0 @@ -6284,6 +6434,7 @@ __metadata: "@polkadot/x-global": ^12.6.1 "@reduxjs/toolkit": ^1.9.1 "@sora-substrate/type-definitions": ^1.17.7 + "@stacks/connect": ^7.7.1 "@substrate/connect": ^0.7.26 "@subwallet/chain-list": 0.2.43 "@subwallet/extension-base": ^1.1.43-0 @@ -6327,6 +6478,7 @@ __metadata: web3-eth: 1.10.0 web3-eth-contract: ^1.10.0 web3-utils: ^1.10.0 + yup: ^1.4.0 languageName: unknown linkType: soft @@ -7315,6 +7467,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.0.4": + version: 18.19.33 + resolution: "@types/node@npm:18.19.33" + dependencies: + undici-types: ~5.26.4 + checksum: b6db87d095bc541d64a410fa323a35c22c6113220b71b608bbe810b2397932d0f0a51c3c0f3ef90c20d8180a1502d950a7c5314b907e182d9cc10b36efd2a44e + languageName: node + linkType: hard + "@types/parse-json@npm:^4.0.0": version: 4.0.0 resolution: "@types/parse-json@npm:4.0.0" @@ -9484,7 +9645,7 @@ __metadata: languageName: node linkType: hard -"async@npm:^2.6.1": +"async@npm:^2.6.1, async@npm:~2.6.3": version: 2.6.4 resolution: "async@npm:2.6.4" dependencies: @@ -10357,7 +10518,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -10966,6 +11127,16 @@ __metadata: languageName: node linkType: hard +"c32check@npm:^2.0.0": + version: 2.0.0 + resolution: "c32check@npm:2.0.0" + dependencies: + "@noble/hashes": ^1.1.2 + base-x: ^4.0.0 + checksum: af555f5d5cb14780936ea2f055d0013f57046200483c53b992e64ce8b2ef7041f66cd81e873b2a2f5bb5e863033a9f4c3877e254e8d6db9a9a55cd9d1c61d9b2 + languageName: node + linkType: hard + "cacache@npm:^16.1.0": version: 16.1.3 resolution: "cacache@npm:16.1.3" @@ -11938,6 +12109,15 @@ __metadata: languageName: node linkType: hard +"cross-fetch@npm:^3.1.5": + version: 3.1.8 + resolution: "cross-fetch@npm:3.1.8" + dependencies: + node-fetch: ^2.6.12 + checksum: 78f993fa099eaaa041122ab037fe9503ecbbcb9daef234d1d2e0b9230a983f64d645d088c464e21a247b825a08dc444a6e7064adfa93536d3a9454b4745b3632 + languageName: node + linkType: hard + "cross-fetch@npm:^4.0.0": version: 4.0.0 resolution: "cross-fetch@npm:4.0.0" @@ -18726,6 +18906,17 @@ __metadata: languageName: node linkType: hard +"jsontokens@npm:^4.0.1": + version: 4.0.1 + resolution: "jsontokens@npm:4.0.1" + dependencies: + "@noble/hashes": ^1.1.2 + "@noble/secp256k1": ^1.6.3 + base64-js: ^1.5.1 + checksum: 5396294d1b24acb9f7a3b3501ed25bc8ea2e8730a2e371c3b8886c1691829e7114dbeb856c14b88ea8379796f2572661f3175562e2ee3d40c42dbd70dc769427 + languageName: node + linkType: hard + "jsprim@npm:^1.2.2": version: 1.4.2 resolution: "jsprim@npm:1.4.2" @@ -19068,6 +19259,13 @@ __metadata: languageName: node linkType: hard +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 + languageName: node + linkType: hard + "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -21997,6 +22195,13 @@ __metadata: languageName: node linkType: hard +"property-expr@npm:^2.0.5": + version: 2.0.6 + resolution: "property-expr@npm:2.0.6" + checksum: 89977f4bb230736c1876f460dd7ca9328034502fd92e738deb40516d16564b850c0bbc4e052c3df88b5b8cd58e51c93b46a94bea049a3f23f4a022c038864cab + languageName: node + linkType: hard + "property-information@npm:^6.0.0": version: 6.4.0 resolution: "property-information@npm:6.4.0" @@ -24073,6 +24278,13 @@ __metadata: languageName: node linkType: hard +"ripemd160-min@npm:^0.0.6": + version: 0.0.6 + resolution: "ripemd160-min@npm:0.0.6" + checksum: 3253fec273aee407e736df0baf69f90c65f56573d6fc537532041112e7c09a2f665ee2e618ef4a88eb494923d36614322eac26ddf35a504fcfedb422fd414c75 + languageName: node + linkType: hard + "ripemd160@npm:^2.0.0, ripemd160@npm:^2.0.1": version: 2.0.2 resolution: "ripemd160@npm:2.0.2" @@ -24336,6 +24548,15 @@ __metadata: languageName: node linkType: hard +"schema-inspector@npm:^2.0.2": + version: 2.1.0 + resolution: "schema-inspector@npm:2.1.0" + dependencies: + async: ~2.6.3 + checksum: e40cb003dc978f38b3af413b44a0696ef1122b720c88e47e0d71bbdea8f3e5d5dd3aa0fc9461930d05e2b2bdb7120b949e6c1380333717fd14ce145430da03c6 + languageName: node + linkType: hard + "schema-utils@npm:^2.6.5": version: 2.7.1 resolution: "schema-utils@npm:2.7.1" @@ -25765,6 +25986,13 @@ __metadata: languageName: node linkType: hard +"tiny-case@npm:^1.0.3": + version: 1.0.3 + resolution: "tiny-case@npm:1.0.3" + checksum: 3f7a30c39d5b0e1bc097b0b271bec14eb5b836093db034f35a0de26c14422380b50dc12bfd37498cf35b192f5df06f28a710712c87ead68872a9e37ad6f6049d + languageName: node + linkType: hard + "tiny-secp256k1@npm:^2.2.3": version: 2.2.3 resolution: "tiny-secp256k1@npm:2.2.3" @@ -25853,6 +26081,13 @@ __metadata: languageName: node linkType: hard +"toposort@npm:^2.0.2": + version: 2.0.2 + resolution: "toposort@npm:2.0.2" + checksum: d64c74b570391c9432873f48e231b439ee56bc49f7cb9780b505cfdf5cb832f808d0bae072515d93834dd6bceca5bb34448b5b4b408335e4d4716eaf68195dcb + languageName: node + linkType: hard + "tough-cookie@npm:^4.0.0, tough-cookie@npm:^4.1.2": version: 4.1.2 resolution: "tough-cookie@npm:4.1.2" @@ -26080,6 +26315,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^2.19.0": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: a4ef07ece297c9fba78fc1bd6d85dff4472fe043ede98bd4710d2615d15776902b595abf62bd78339ed6278f021235fb28a96361f8be86ed754f778973a0d278 + languageName: node + linkType: hard + "type-is@npm:~1.6.18": version: 1.6.18 resolution: "type-is@npm:1.6.18" @@ -26203,6 +26445,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "unenv@npm:^1.9.0": version: 1.9.0 resolution: "unenv@npm:1.9.0" @@ -28005,6 +28254,18 @@ __metadata: languageName: node linkType: hard +"yup@npm:^1.4.0": + version: 1.4.0 + resolution: "yup@npm:1.4.0" + dependencies: + property-expr: ^2.0.5 + tiny-case: ^1.0.3 + toposort: ^2.0.2 + type-fest: ^2.19.0 + checksum: 20a2ee0c1e891979ca16b34805b3a3be9ab4bea6ea3d2f9005b998b4dc992d0e4d7b53e5f4d8d9423420046630fb44fdf0ecf7e83bc34dd83392bca046c5229d + languageName: node + linkType: hard + "zen-observable-ts@npm:^1.2.5": version: 1.2.5 resolution: "zen-observable-ts@npm:1.2.5" @@ -28021,6 +28282,13 @@ __metadata: languageName: node linkType: hard +"zone-file@npm:^2.0.0-beta.3": + version: 2.0.0-beta.3 + resolution: "zone-file@npm:2.0.0-beta.3" + checksum: f51596def8165100a6f860e7c10b85a7f0bcef948210353c3db93f0760a3f2c9f0385deafe8ea5562445e5a646a19c59a6c0469fb273bae786a7ac7a77b49114 + languageName: node + linkType: hard + "zwitch@npm:^2.0.0": version: 2.0.4 resolution: "zwitch@npm:2.0.4" From c5019c5419d8c38ca4a0fae22ee7810e774722b8 Mon Sep 17 00:00:00 2001 From: lw Date: Tue, 11 Jun 2024 18:37:16 +0700 Subject: [PATCH 03/43] [Issue-85] Update getAddresses method for dApp provider --- .../src/background/KoniTypes.ts | 7 +++ .../src/koni/background/handlers/Tabs.ts | 62 ++++++++++++++++--- .../variants/AuthorizeConfirmation.tsx | 7 ++- .../Account/Item/AccountItemBase.tsx | 12 ++-- .../Account/Item/AccountItemWithName.tsx | 25 +++++++- 5 files changed, 96 insertions(+), 17 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index bca485eb0de..0002af9bede 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -73,6 +73,13 @@ export interface AuthRequestV2 extends Resolver { accountAuthType: AccountAuthType; } +export type AuthAddress = { + address: string; + publicKey?: string; + isTestnet?: boolean; + addressType: 'p2tr' | 'p2wpkh' | 'p2sh' | 'ethereum' | 'unknown'; +} + /// Manage Auth // Get Auth diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index 5c1fc5dcb8c..f7036cc741d 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -9,7 +9,7 @@ import { EvmProviderError } from '@subwallet/extension-base/background/errors/Ev import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; import { createSubscription, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AddNetworkRequestExternal, AddTokenRequestExternal, BitcoinProviderErrorType, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; +import { AddNetworkRequestExternal, AddTokenRequestExternal, AuthAddress, BitcoinProviderErrorType, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; import RequestBytesSign from '@subwallet/extension-base/background/RequestBytesSign'; import RequestExtrinsicSign from '@subwallet/extension-base/background/RequestExtrinsicSign'; import { AccountAuthType, MessageTypes, RequestAccountList, RequestAccountSubscribe, RequestAccountUnsubscribe, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '@subwallet/extension-base/background/types'; @@ -23,6 +23,7 @@ import { AuthUrls } from '@subwallet/extension-base/services/request-service/typ import { DEFAULT_CHAIN_PATROL_ENABLE } from '@subwallet/extension-base/services/setting-service/constants'; import { canDerive, getEVMChainInfo, stripUrl } from '@subwallet/extension-base/utils'; import { InjectedMetadataKnown, MetadataDef, ProviderMeta } from '@subwallet/extension-inject/types'; +import { getKeypairTypeByAddress } from '@subwallet/keyring'; import { KeypairType, KeyringPair } from '@subwallet/keyring/types'; import keyring from '@subwallet/ui-keyring'; import { SingleAddress, SubjectInfo } from '@subwallet/ui-keyring/observable/types'; @@ -36,7 +37,7 @@ import { JsonRpcPayload } from 'web3-core-helpers'; import { checkIfDenied } from '@polkadot/phishing'; import { JsonRpcResponse } from '@polkadot/rpc-provider/types'; import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; -import { assert, isNumber } from '@polkadot/util'; +import { assert, hexStripPrefix, isNumber, u8aToHex } from '@polkadot/util'; interface AccountSub { subscription: Subscription; @@ -80,6 +81,47 @@ function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: })); } +const getAuthAddresses = (addresses: string[]) => { + const result: AuthAddress[] = []; + + addresses.forEach((address) => { + const pair = keyring.getPair(address); + + if (pair.meta.isReadOnly) { + return; + } + + const keypairType = getKeypairTypeByAddress(address); + + if (!['ethereum', 'bitcoin-84', 'bitcoin-86', 'bittest-84', 'bittest-86'].includes(keypairType)) { + return; + } + + result.push({ + address, + publicKey: hexStripPrefix(u8aToHex(pair.publicKey)), + isTestnet: ['bittest-84', 'bittest-86'].includes(keypairType), + addressType: (() => { + if (keypairType === 'ethereum') { + return 'ethereum'; + } + + if (['bitcoin-86', 'bittest-86'].includes(keypairType)) { + return 'p2tr'; + } + + if (['bitcoin-84', 'bittest-84'].includes(keypairType)) { + return 'p2sh'; + } + + return 'unknown'; + })() + }); + }); + + return result; +}; + interface ChainPatrolResponse { reason: string; reports: Array<{ createdAt: string, id: number }>; @@ -1059,14 +1101,20 @@ export default class KoniTabs { return await this.#koniState.addTokenConfirm(id, url, tokenInfo); } - async bitcoinGetAddress (url: string, request: RequestArguments): Promise { + async bitcoinGetAddresses (url: string, request: RequestArguments): Promise { try { - const result = this.#koniState.authorizeUrlV2(url, { + await this.#koniState.authorizeUrlV2(url, { origin: '', accountAuthType: 'bitcoin' }); - return result; + const authInfo = await this.getAuthInfo(url); + + if (!authInfo) { + return []; + } + + return getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])); } catch (e) { throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST); } @@ -1077,8 +1125,8 @@ export default class KoniTabs { try { switch (method) { - case 'getAddress': - return await this.bitcoinGetAddress(url, request); + case 'getAddresses': + return await this.bitcoinGetAddresses(url, request); default: return this.performWeb3Method(id, url, request); diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx index dad59304aea..e95b7091cec 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx @@ -37,14 +37,16 @@ async function handleBlock ({ id }: AuthorizeRequest) { } export const filterAuthorizeAccounts = (accounts: AccountJson[], accountAuthType: AccountAuthType) => { - let rs = [...accounts]; + let rs = [...accounts.filter((a) => !a.isReadOnly)]; // rs = rs.filter((acc) => acc.isReadOnly !== true); if (accountAuthType === 'evm') { rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type === 'ethereum')); } else if (accountAuthType === 'substrate') { - rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type !== 'ethereum')); + rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type && ['ed25519', 'sr25519', 'ecdsa'].includes(acc.type))); + } else if (accountAuthType === 'bitcoin') { + rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type && ['bitcoin-86', 'bitcoin-84', 'bittest-84', 'bittest-86'].includes(acc.type))); } else { rs = rs.filter((acc) => !isAccountAll(acc.address)); } @@ -229,6 +231,7 @@ function Component ({ className, request }: Props) { isSelected={selectedMap[item.address]} key={item.address} onClick={onAccountSelect(item.address)} + proxyId={item.proxyId} showUnselectIcon /> ))} diff --git a/packages/extension-koni-ui/src/components/Account/Item/AccountItemBase.tsx b/packages/extension-koni-ui/src/components/Account/Item/AccountItemBase.tsx index 81f753fb101..3425856955a 100644 --- a/packages/extension-koni-ui/src/components/Account/Item/AccountItemBase.tsx +++ b/packages/extension-koni-ui/src/components/Account/Item/AccountItemBase.tsx @@ -21,7 +21,7 @@ export interface AccountItemBaseProps extends Omit = (props: AccountItemBaseProps) => { - const { address, genesisHash, isSelected, onClick, preventPrefix, proxyId, rightItem, showUnselectIcon, type: givenType } = props; + const { address, genesisHash, isSelected, leftItem, onClick, preventPrefix, proxyId, rightItem, showUnselectIcon, type: givenType } = props; const { address: avatarAddress } = useAccountAvatarInfo(address ?? '', preventPrefix, genesisHash, givenType); const { token } = useTheme() as Theme; @@ -46,10 +46,12 @@ const Component: React.FC = (props: AccountItemBaseProps) avatarIdentPrefix={0} {...props} address={avatarAddress ?? ''} - leftItem={} + leftItem={leftItem || ( + + )} onPressItem={onClick} rightItem={_rightItem} /> diff --git a/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx b/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx index 925b08a6354..8b40a784c02 100644 --- a/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx +++ b/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { AbstractAddressJson } from '@subwallet/extension-base/background/types'; -import { AccountProxyAvatar } from '@subwallet/extension-koni-ui/components'; -import AvatarGroup from '@subwallet/extension-koni-ui/components/Account/Info/AvatarGroup'; +import { AccountProxyAvatar, AccountProxyAvatarGroup } from '@subwallet/extension-koni-ui/components'; import AccountItemBase, { AccountItemBaseProps } from '@subwallet/extension-koni-ui/components/Account/Item/AccountItemBase'; import { isAccountAll, toShort } from '@subwallet/extension-koni-ui/utils'; import CN from 'classnames'; @@ -23,6 +22,26 @@ const Component: React.FC = (props: Props) => { const isAll = isAccountAll(address); const { t } = useTranslation(); + const accountProxies = useMemo(() => { + if (!accounts) { + return undefined; + } + + const proxies: string[] = []; + + accounts.forEach((a) => { + if (a.proxyId && !proxies.includes(a.proxyId)) { + proxies.push(a.proxyId); + } + }); + + return proxies.map((p) => ({ + proxyId: p + })); + }, [accounts]); + + console.log('accountProxies', accountProxies, isAll); + const showFallback = useMemo(() => { if (isAll) { return false; @@ -41,7 +60,7 @@ const Component: React.FC = (props: Props) => { address={address} className={CN('account-item-with-name', props.className)} leftItem={isAll - ? + ? : Date: Tue, 11 Jun 2024 19:51:35 +0700 Subject: [PATCH 04/43] [Issue-85] Update getAddresses type --- .../src/background/KoniTypes.ts | 4 +++- .../src/koni/background/handlers/Tabs.ts | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 0002af9bede..f2f56ead9f5 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -76,8 +76,10 @@ export interface AuthRequestV2 extends Resolver { export type AuthAddress = { address: string; publicKey?: string; + tweakedPublicKey?: string; + derivationPath?: string; isTestnet?: boolean; - addressType: 'p2tr' | 'p2wpkh' | 'p2sh' | 'ethereum' | 'unknown'; + type: 'p2tr' | 'p2wpkh' | 'p2sh' | 'ethereum' | 'unknown'; } /// Manage Auth diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index f7036cc741d..41f1616cefd 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -23,7 +23,7 @@ import { AuthUrls } from '@subwallet/extension-base/services/request-service/typ import { DEFAULT_CHAIN_PATROL_ENABLE } from '@subwallet/extension-base/services/setting-service/constants'; import { canDerive, getEVMChainInfo, stripUrl } from '@subwallet/extension-base/utils'; import { InjectedMetadataKnown, MetadataDef, ProviderMeta } from '@subwallet/extension-inject/types'; -import { getKeypairTypeByAddress } from '@subwallet/keyring'; +import { getDerivePath, getKeypairTypeByAddress } from '@subwallet/keyring'; import { KeypairType, KeyringPair } from '@subwallet/keyring/types'; import keyring from '@subwallet/ui-keyring'; import { SingleAddress, SubjectInfo } from '@subwallet/ui-keyring/observable/types'; @@ -97,11 +97,14 @@ const getAuthAddresses = (addresses: string[]) => { return; } - result.push({ + const deriFunc = getDerivePath(keypairType); + const index = parseInt((pair.meta.suri as string)?.split('//')[1]) || 0; + + const item: AuthAddress = { address, + derivationPath: deriFunc(index), publicKey: hexStripPrefix(u8aToHex(pair.publicKey)), - isTestnet: ['bittest-84', 'bittest-86'].includes(keypairType), - addressType: (() => { + type: (() => { if (keypairType === 'ethereum') { return 'ethereum'; } @@ -116,7 +119,17 @@ const getAuthAddresses = (addresses: string[]) => { return 'unknown'; })() - }); + }; + + if (['bittest-84', 'bittest-86'].includes(keypairType)) { + item.isTestnet = true; + } + + if (pair.publicKey.length !== 32) { + item.tweakedPublicKey = hexStripPrefix(u8aToHex(pair.publicKey.slice(1, 33))); + } + + result.push(item); }); return result; From d682f4b71c15dd59eed28409a8d884d088239cd7 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Wed, 12 Jun 2024 15:27:13 +0700 Subject: [PATCH 05/43] Add method signMessage for bitcoin provider --- .../src/background/KoniTypes.ts | 7 +- .../src/koni/background/handlers/State.ts | 57 +++++++++++- .../src/koni/background/handlers/Tabs.ts | 45 ++++++++++ .../handler/BitcoinRequestHandler.ts | 14 ++- .../src/Popup/Confirmations/index.tsx | 9 +- .../variants/BitcoinSignatureConfirmation.tsx | 89 +++++++++++++++++++ .../src/Popup/Confirmations/variants/index.ts | 1 + 7 files changed, 215 insertions(+), 7 deletions(-) create mode 100644 packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index f2f56ead9f5..a6872cbf901 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1362,6 +1362,11 @@ export interface ConfirmationsQueueItemOptions { networkKey?: string; } +export interface SignMessageBitcoinResult { + signature: string; + address: string; +} + export interface ConfirmationsQueueItem extends ConfirmationsQueueItemOptions, ConfirmationRequestBase { payload: T; payloadJson: string; @@ -1426,7 +1431,7 @@ export interface ConfirmationDefinitions { } export interface ConfirmationDefinitionsBitcoin { - bitcoinSignatureRequest: [ConfirmationsQueueItem, ConfirmationResult], + bitcoinSignatureRequest: [ConfirmationsQueueItem, ConfirmationResult], bitcoinSendTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult], bitcoinWatchTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult] } diff --git a/packages/extension-base/src/koni/background/handlers/State.ts b/packages/extension-base/src/koni/background/handlers/State.ts index ff065cb7058..66efa107d14 100644 --- a/packages/extension-base/src/koni/background/handlers/State.ts +++ b/packages/extension-base/src/koni/background/handlers/State.ts @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from '@subwallet/chain-list/types'; +import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError'; import { EvmProviderError } from '@subwallet/extension-base/background/errors/EvmProviderError'; import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { isSubscriptionRunning, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AccountRefMap, AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, BasicTxErrorType, ChainStakingMetadata, ChainType, ConfirmationsQueue, CrowdloanItem, CrowdloanJson, CurrentAccountInfo, CurrentAccountProxyInfo, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCheckPublicAndSecretKey, ServiceInfo, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes'; +import { AccountRefMap, AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, BasicTxErrorType, BitcoinProviderErrorType, BitcoinSignatureRequest, ChainStakingMetadata, ChainType, ConfirmationsQueue, CrowdloanItem, CrowdloanJson, CurrentAccountInfo, CurrentAccountProxyInfo, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCheckPublicAndSecretKey, ServiceInfo, SignMessageBitcoinResult, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes'; import { AccountJson, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning } from '@subwallet/extension-base/background/types'; import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH, MANTA_PAY_BALANCE_INTERVAL } from '@subwallet/extension-base/constants'; import { BalanceService } from '@subwallet/extension-base/services/balance-service'; @@ -42,6 +43,7 @@ import { isAccountAll, keyringGetAccounts, stripUrl, targetIsWeb } from '@subwal import { isContractAddress, parseContractInput } from '@subwallet/extension-base/utils/eth/parseTransaction'; import { createPromiseHandler } from '@subwallet/extension-base/utils/promise'; import { MetadataDef, ProviderMeta } from '@subwallet/extension-inject/types'; +import { isBitcoinAddress } from '@subwallet/keyring'; import { decodePair } from '@subwallet/keyring/pair/decode'; import { KeypairType } from '@subwallet/keyring/types'; import { keyring } from '@subwallet/ui-keyring'; @@ -1166,6 +1168,59 @@ export default class KoniState { } as ApiMap; } + public async bitcoinSign (id: string, url: string, method: string, params: any, allowedAccounts: string[]): Promise { + const { address, message } = params as Record; + + if (address === '' || !message) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found address or payload to sign')); + } + + if (!isBitcoinAddress(address)) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Invalid bitcoin address')); + } + + // Check sign abiblity + if (!allowedAccounts.find((acc) => (acc.toLowerCase() === address.toLowerCase()))) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('You have rescinded allowance for this account in wallet')); + } + + const pair = keyring.getPair(address); + + if (!pair) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Unable to find account')); + } + + const account: AccountJson = { address: pair.address, ...pair.meta }; + + const hashPayload = ''; + const canSign = !account.isExternal; + + const signPayload: BitcoinSignatureRequest = { + account: account, + payload: message as unknown, + payloadJson: message, + hashPayload, + canSign, + id + }; + + return this.requestService.addConfirmationBitcoin(id, url, 'bitcoinSignatureRequest', signPayload, { + requiredPassword: false, + address + }) + .then(({ isApproved, payload }) => { + if (isApproved) { + if (payload) { + return payload; + } else { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found signature')); + } + } else { + throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST); + } + }); + } + public refreshSubstrateApi (key: string) { this.chainService.refreshSubstrateApi(key); diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index 41f1616cefd..bddfd0d61b8 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -62,6 +62,8 @@ function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: keyringTypes = ['ed25519', 'sr25519', 'ecdsa']; } else if (accountAuthType === 'evm') { keyringTypes = ['ethereum']; + } else if (accountAuthType === 'bitcoin') { + keyringTypes = ['bitcoin-84', 'bitcoin-86', 'bittest-84', 'bittest-86']; } const authTypeFilter = ({ type }: SingleAddress) => (!!type && keyringTypes.includes(type)); @@ -1133,6 +1135,46 @@ export default class KoniTabs { } } + private async getBitcoinCurrentAccount (url: string): Promise { + return await new Promise((resolve) => { + this.getAuthInfo(url).then((authInfo) => { + const allAccounts = this.#koniState.keyringService.accounts; + const accountList = transformAccountsV2(allAccounts, false, authInfo, 'bitcoin').map((a) => a.address); + let accounts: string[] = []; + + const address = this.#koniState.keyringService.currentAccount.address; + + if (address === ALL_ACCOUNT_KEY || !address) { + accounts = accountList; + } else { + if (accountList.includes(address)) { + const result = accountList.filter((adr) => adr !== address); + + result.unshift(address); + accounts = result; + } else { + accounts = accountList; + } + } + + resolve(accounts); + }).catch(console.error); + }); + } + + private async bitcoinSign (id: string, url: string, { method, params }: RequestArguments) { + const allowedAccounts = (await this.getBitcoinCurrentAccount(url)); + + console.log(allowedAccounts, 'allow'); + const signResult = await this.#koniState.bitcoinSign(id, url, method, params, allowedAccounts); + + if (signResult) { + return signResult; + } else { + throw new EvmProviderError(EvmProviderErrorType.INVALID_PARAMS, 'Failed to sign message'); + } + } + private async handleBitcoinRequest (id: string, url: string, request: RequestArguments, port: chrome.runtime.Port): Promise { const { method } = request; @@ -1141,6 +1183,9 @@ export default class KoniTabs { case 'getAddresses': return await this.bitcoinGetAddresses(url, request); + case 'signMessage': + return await this.bitcoinSign(id, url, request); + default: return this.performWeb3Method(id, url, request); } diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index 1cd5ddafa8d..56b137223b3 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-base authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { ConfirmationDefinitionsBitcoin, ConfirmationsQueueBitcoin, ConfirmationsQueueItemOptions, ConfirmationTypeBitcoin, RequestConfirmationCompleteBitcoin } from '@subwallet/extension-base/background/KoniTypes'; +import { ConfirmationDefinitionsBitcoin, ConfirmationsQueueBitcoin, ConfirmationsQueueItemOptions, ConfirmationTypeBitcoin, RequestConfirmationCompleteBitcoin, SignMessageBitcoinResult } from '@subwallet/extension-base/background/KoniTypes'; import { ConfirmationRequestBase, Resolver } from '@subwallet/extension-base/background/types'; import RequestService from '@subwallet/extension-base/services/request-service'; import { isInternalRequest } from '@subwallet/extension-base/utils/request'; @@ -134,7 +134,7 @@ export default class BitcoinRequestHandler { this.confirmationsQueueSubjectBitcoin.next(confirmations); } - signMessageBitcoin (confirmation: ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest'][0]): string { + signMessageBitcoin (confirmation: ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest'][0]): SignMessageBitcoinResult { const { account, payload } = confirmation.payload; const address = account.address; const pair = keyring.getPair(address); @@ -146,13 +146,19 @@ export default class BitcoinRequestHandler { // Check if payload is a string if (typeof payload === 'string') { // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface - return pair.bitcoin.signMessage(payload, false); // Assuming compressed = false + return { + signature: pair.bitcoin.signMessage(payload, false), + address + }; // Assuming compressed = false } else if (payload instanceof Uint8Array) { // Check if payload is a byte array (Uint8Array) // Convert Uint8Array to string const payloadString = Buffer.from(payload).toString('hex'); // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface - return pair.bitcoin.signMessage(payloadString, false); // Assuming compressed = false + return { + signature: pair.bitcoin.signMessage(payloadString, false), + address + }; // Assuming compressed = false } else { // Handle the case where payload is invalid throw new Error('Invalid payload type'); diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index 4d116ece5b2..d9b9a6b8e06 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -18,7 +18,7 @@ import styled from 'styled-components'; import { SignerPayloadJSON } from '@polkadot/types/types'; import { ConfirmationHeader } from './parts'; -import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; +import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, BitcoinSignatureConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; type Props = ThemeProps @@ -142,6 +142,13 @@ const Component = function ({ className }: Props) { type={confirmation.type} /> ); + case 'bitcoinSignatureRequest': + return ( + + ); case 'authorizeRequest': return ( diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx new file mode 100644 index 00000000000..a194c57f6b4 --- /dev/null +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx @@ -0,0 +1,89 @@ +// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { BitcoinSignatureRequest, ConfirmationsQueueItem } from '@subwallet/extension-base/background/KoniTypes'; +import { AccountItemWithName, ConfirmationGeneralInfo, MetaInfo, ViewDetailIcon } from '@subwallet/extension-koni-ui/components'; +import { useOpenDetailModal } from '@subwallet/extension-koni-ui/hooks'; +import { BitcoinSignArea } from '@subwallet/extension-koni-ui/Popup/Confirmations/parts'; +import { BitcoinSignatureSupportType, ThemeProps } from '@subwallet/extension-koni-ui/types'; +import { Button } from '@subwallet/react-ui'; +import CN from 'classnames'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { BaseDetailModal } from '../parts'; + +interface Props extends ThemeProps { + type: BitcoinSignatureSupportType + request: ConfirmationsQueueItem +} + +function Component ({ className, request, type }: Props) { + const { id, payload } = request; + const { t } = useTranslation(); + const { account } = payload; + + const onClickDetail = useOpenDetailModal(); + + console.log('213123123'); + + return ( + <> +
+ +
+ {t('Signature required')} +
+
+ {t('You are approving a request with the following account')} +
+ +
+ +
+
+ + + + {request.payload.payload as string} + + + + ); +} + +const BitcoinSignatureConfirmation = styled(Component)(({ theme: { token } }: ThemeProps) => ({ + '.account-list': { + '.__prop-label': { + marginRight: token.marginMD, + width: '50%', + float: 'left' + } + }, + + '.__label': { + textAlign: 'left' + } +})); + +export default BitcoinSignatureConfirmation; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts index f3795bba6f6..5b9d9684c3f 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts @@ -12,3 +12,4 @@ export { default as NotSupportConfirmation } from './NotSupportConfirmation'; export { default as SignConfirmation } from './SignConfirmation'; export { default as TransactionConfirmation } from './Transaction'; export { default as NotSupportWCConfirmation } from './NotSupportWCConfirmation'; +export { default as BitcoinSignatureConfirmation } from './BitcoinSignatureConfirmation'; From 50fe756edc6921f2aa5f2fa44849f53018dc9858 Mon Sep 17 00:00:00 2001 From: lw Date: Wed, 12 Jun 2024 16:27:15 +0700 Subject: [PATCH 06/43] [Issue-85] Add type bitcoinSignPsbtRequest --- packages/extension-base/src/background/KoniTypes.ts | 4 +++- packages/extension-base/src/koni/background/handlers/Tabs.ts | 1 - .../services/request-service/handler/BitcoinRequestHandler.ts | 3 ++- packages/extension-koni-ui/src/Popup/Confirmations/index.tsx | 2 +- .../Confirmations/variants/BitcoinSignatureConfirmation.tsx | 2 -- .../src/Popup/Confirmations/variants/Transaction/index.tsx | 4 ++-- .../src/components/Account/Item/AccountItemWithName.tsx | 2 -- packages/extension-koni-ui/src/stores/base/RequestState.ts | 2 ++ packages/extension-koni-ui/src/types/confirmation.ts | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index a6872cbf901..800d21afd6c 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1355,6 +1355,7 @@ export type BitcoinSendTransactionRequest = BitcoinSignRequest export type EvmWatchTransactionRequest = EvmSendTransactionRequest; export type BitcoinWatchTransactionRequest = BitcoinSendTransactionRequest; +export type BitcoinSignPsbtRequest = BitcoinSendTransactionRequest; export interface ConfirmationsQueueItemOptions { requiredPassword?: boolean; @@ -1433,7 +1434,8 @@ export interface ConfirmationDefinitions { export interface ConfirmationDefinitionsBitcoin { bitcoinSignatureRequest: [ConfirmationsQueueItem, ConfirmationResult], bitcoinSendTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult], - bitcoinWatchTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult] + bitcoinWatchTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult], + bitcoinSignPsbtRequest: [ConfirmationsQueueItem, ConfirmationResult], } export type ConfirmationType = keyof ConfirmationDefinitions; diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index bddfd0d61b8..547b0d45aa4 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -1165,7 +1165,6 @@ export default class KoniTabs { private async bitcoinSign (id: string, url: string, { method, params }: RequestArguments) { const allowedAccounts = (await this.getBitcoinCurrentAccount(url)); - console.log(allowedAccounts, 'allow'); const signResult = await this.#koniState.bitcoinSign(id, url, method, params, allowedAccounts); if (signResult) { diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index 56b137223b3..650d6de2363 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -19,7 +19,8 @@ export default class BitcoinRequestHandler { private readonly confirmationsQueueSubjectBitcoin = new BehaviorSubject({ bitcoinSignatureRequest: {}, bitcoinSendTransactionRequest: {}, - bitcoinWatchTransactionRequest: {} + bitcoinWatchTransactionRequest: {}, + bitcoinSignPsbtRequest: {} }); private readonly confirmationsPromiseMap: Record, validator?: (rs: any) => Error | undefined }> = {}; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index d9b9a6b8e06..1cc6418b6ee 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -94,7 +94,7 @@ const Component = function ({ className }: Props) { canSign = request.payload.canSign; isMessage = confirmation.type === 'evmSignatureRequest'; } else if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest'].includes(confirmation.type)) { - const request = confirmation.item as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest' | 'bitcoinSendTransactionRequest' | 'bitcoinWatchTransactionRequest'][0]; + const request = confirmation.item as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest' | 'bitcoinSendTransactionRequest' | 'bitcoinWatchTransactionRequest' | 'bitcoinSignPsbtRequest'][0]; account = request.payload.account; canSign = request.payload.canSign; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx index a194c57f6b4..4cb70f36baf 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx @@ -26,8 +26,6 @@ function Component ({ className, request, type }: Props) { const onClickDetail = useOpenDetailModal(); - console.log('213123123'); - return ( <>
diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/Transaction/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/Transaction/index.tsx index 3c16eea3ade..6cbb99063d5 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/Transaction/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/Transaction/index.tsx @@ -141,11 +141,11 @@ const Component: React.FC = (props: Props) => { ) } { - (type === 'bitcoinSignatureRequest' || type === 'bitcoinSendTransactionRequest' || type === 'bitcoinWatchTransactionRequest') && ( + (type === 'bitcoinSignatureRequest' || type === 'bitcoinSendTransactionRequest' || type === 'bitcoinWatchTransactionRequest' || type === 'bitcoinSignPsbtRequest') && ( ) diff --git a/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx b/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx index 8b40a784c02..8b52052ed2f 100644 --- a/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx +++ b/packages/extension-koni-ui/src/components/Account/Item/AccountItemWithName.tsx @@ -40,8 +40,6 @@ const Component: React.FC = (props: Props) => { })); }, [accounts]); - console.log('accountProxies', accountProxies, isAll); - const showFallback = useMemo(() => { if (isAll) { return false; diff --git a/packages/extension-koni-ui/src/stores/base/RequestState.ts b/packages/extension-koni-ui/src/stores/base/RequestState.ts index 6d343e9ba1c..4aeed8de6c5 100644 --- a/packages/extension-koni-ui/src/stores/base/RequestState.ts +++ b/packages/extension-koni-ui/src/stores/base/RequestState.ts @@ -30,6 +30,7 @@ const initialState: RequestState = { bitcoinSignatureRequest: {}, bitcoinSendTransactionRequest: {}, bitcoinWatchTransactionRequest: {}, + bitcoinSignPsbtRequest: {}, // Summary Info reduxStatus: ReduxStatus.INIT, @@ -51,6 +52,7 @@ export const CONFIRMATIONS_FIELDS: Array = [ 'bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest', + 'bitcoinSignPsbtRequest', 'connectWCRequest', 'notSupportWCRequest' ]; diff --git a/packages/extension-koni-ui/src/types/confirmation.ts b/packages/extension-koni-ui/src/types/confirmation.ts index c6c1e7167ce..38eb485f926 100644 --- a/packages/extension-koni-ui/src/types/confirmation.ts +++ b/packages/extension-koni-ui/src/types/confirmation.ts @@ -4,4 +4,4 @@ import { ConfirmationDefinitions, ConfirmationDefinitionsBitcoin } from '@subwallet/extension-base/background/KoniTypes'; export type EvmSignatureSupportType = keyof Pick; -export type BitcoinSignatureSupportType = keyof Pick; +export type BitcoinSignatureSupportType = keyof Pick; From c36ae34fad834dd0bf8d5b4dc0a3fb86f1f82044 Mon Sep 17 00:00:00 2001 From: lw Date: Wed, 12 Jun 2024 18:40:14 +0700 Subject: [PATCH 07/43] [Issue-85] Init BitcoinSignPsbtConfirmation --- .../src/Popup/Confirmations/index.tsx | 9 +- .../variants/BitcoinSignPsbtConfirmation.tsx | 88 +++++++++++++++++++ .../variants/BitcoinSignatureConfirmation.tsx | 1 + .../src/Popup/Confirmations/variants/index.ts | 1 + 4 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index 1cc6418b6ee..ef7cad8b7ab 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -18,7 +18,7 @@ import styled from 'styled-components'; import { SignerPayloadJSON } from '@polkadot/types/types'; import { ConfirmationHeader } from './parts'; -import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, BitcoinSignatureConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; +import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, BitcoinSignatureConfirmation, BitcoinSignPsbtConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; type Props = ThemeProps @@ -149,6 +149,13 @@ const Component = function ({ className }: Props) { type={confirmation.type} /> ); + case 'bitcoinSignPsbtRequest': + return ( + + ); case 'authorizeRequest': return ( diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx new file mode 100644 index 00000000000..bf14e6cc0b5 --- /dev/null +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx @@ -0,0 +1,88 @@ +// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { BitcoinSignPsbtRequest, ConfirmationsQueueItem } from '@subwallet/extension-base/background/KoniTypes'; +import { AccountItemWithName, ConfirmationGeneralInfo, MetaInfo, ViewDetailIcon } from '@subwallet/extension-koni-ui/components'; +import { useOpenDetailModal } from '@subwallet/extension-koni-ui/hooks'; +import { BitcoinSignArea } from '@subwallet/extension-koni-ui/Popup/Confirmations/parts'; +import { BitcoinSignatureSupportType, ThemeProps } from '@subwallet/extension-koni-ui/types'; +import { Button } from '@subwallet/react-ui'; +import CN from 'classnames'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; + +import { BaseDetailModal } from '../parts'; + +interface Props extends ThemeProps { + type: BitcoinSignatureSupportType + request: ConfirmationsQueueItem +} + +function Component ({ className, request, type }: Props) { + const { id, payload } = request; + const { t } = useTranslation(); + const { account } = payload; + + const onClickDetail = useOpenDetailModal(); + + return ( + <> +
+ +
+ {t('Signature required')} +
+
+ {t('You are approving a request with the following account')} +
+ +
+ +
+
+ + + + + + + + ); +} + +const BitcoinSignPsbtConfirmation = styled(Component)(({ theme: { token } }: ThemeProps) => ({ + '.account-list': { + '.__prop-label': { + marginRight: token.marginMD, + width: '50%', + float: 'left' + } + }, + + '.__label': { + textAlign: 'left' + } +})); + +export default BitcoinSignPsbtConfirmation; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx index 4cb70f36baf..0bd863a1e99 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignatureConfirmation.tsx @@ -42,6 +42,7 @@ function Component ({ className, request, type }: Props) { avatarSize={24} className='account-item' isSelected={true} + proxyId={account.proxyId} />
+ { + BITCOIN_CHAINS.includes(chainValue) && ( + + ) + } +
From ccea3ab6dd594055af1b38289605af31612d3a09 Mon Sep 17 00:00:00 2001 From: lw Date: Fri, 14 Jun 2024 19:03:25 +0700 Subject: [PATCH 13/43] Update broadcast logic for signPsbt --- .../src/background/KoniTypes.ts | 12 +++--- .../bitcoin/strategy/BlockStream/index.ts | 13 +++++++ .../handler/bitcoin/strategy/types.ts | 1 + .../handler/BitcoinRequestHandler.ts | 38 ++++++++++++++++--- .../src/services/request-service/index.ts | 2 +- 5 files changed, 52 insertions(+), 14 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 08c1b282f1c..578d1a248e5 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1337,11 +1337,6 @@ export interface BitcoinSignRequest { canSign: boolean; } -export interface BitcoinSignPsbtConfirmRequest { - accounts: AccountJson[]; - payload: BitcoinSignPsbtPayload -} - export interface BitcoinSignPsbtPayload { txInput: PsbtTxInput[]; signingIndexes: Record @@ -1377,7 +1372,10 @@ export type BitcoinSendTransactionRequest = BitcoinSignRequest export type EvmWatchTransactionRequest = EvmSendTransactionRequest; export type BitcoinWatchTransactionRequest = BitcoinSendTransactionRequest; -export type BitcoinSignPsbtRequest = Omit & BitcoinSignPsbtConfirmRequest; +export type BitcoinSignPsbtRequest = Omit & { + accounts: AccountJson[]; + payload: BitcoinSignPsbtPayload; +}; export interface ConfirmationsQueueItemOptions { requiredPassword?: boolean; @@ -1392,7 +1390,7 @@ export interface SignMessageBitcoinResult { export interface SignPsbtBitcoinResult { psbt: string; - txid: string + txid?: string } export interface ConfirmationsQueueItem extends ConfirmationsQueueItemOptions, ConfirmationRequestBase { diff --git a/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/BlockStream/index.ts b/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/BlockStream/index.ts index 6a2db98d87d..64f4abae8cc 100644 --- a/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/BlockStream/index.ts +++ b/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/BlockStream/index.ts @@ -199,6 +199,19 @@ export class BlockStreamRequestStrategy extends BaseApiRequestStrategy implement return eventEmitter; } + simpleSendRawTransaction (rawTransaction: string) { + return this.addRequest(async (): Promise => { + const _rs = await postRequest(this.getUrl('tx'), rawTransaction, this.headers, false); + const rs = await _rs.json() as OBResponse; + + if (rs.status_code !== 200) { + throw new SWError('BlockStreamRequestStrategy.simpleSendRawTransaction', rs.message); + } + + return rs.result; + }, 0); + } + async getRunes (address: string) { const runesFullList: RunesInfoByAddress[] = []; const pageSize = 60; diff --git a/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/types.ts b/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/types.ts index 4cfdb942c4b..e2b8e9d57b1 100644 --- a/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/types.ts +++ b/packages/extension-base/src/services/chain-service/handler/bitcoin/strategy/types.ts @@ -21,6 +21,7 @@ export interface BitcoinApiStrategy extends Omit; getTxHex (txHash: string): Promise; sendRawTransaction (rawTransaction: string): EventEmitter; + simpleSendRawTransaction (rawTransaction: string): Promise; } export interface BitcoinTransactionEventMap { diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index df9cb75a3ab..9973b75c908 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -4,8 +4,10 @@ import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError'; import { BitcoinProviderErrorType, ConfirmationDefinitionsBitcoin, ConfirmationsQueueBitcoin, ConfirmationsQueueItemOptions, ConfirmationTypeBitcoin, RequestConfirmationCompleteBitcoin, SignMessageBitcoinResult, SignPsbtBitcoinResult } from '@subwallet/extension-base/background/KoniTypes'; import { ConfirmationRequestBase, Resolver } from '@subwallet/extension-base/background/types'; +import { ChainService } from '@subwallet/extension-base/services/chain-service'; import RequestService from '@subwallet/extension-base/services/request-service'; import { isInternalRequest } from '@subwallet/extension-base/utils/request'; +import { getKeypairTypeByAddress } from '@subwallet/keyring'; import keyring from '@subwallet/ui-keyring'; import { Psbt } from 'bitcoinjs-lib'; import { t } from 'i18next'; @@ -16,6 +18,7 @@ import { Logger } from '@polkadot/util/types'; export default class BitcoinRequestHandler { readonly #requestService: RequestService; + readonly #chainService: ChainService; readonly #logger: Logger; private readonly confirmationsQueueSubjectBitcoin = new BehaviorSubject({ bitcoinSignatureRequest: {}, @@ -26,8 +29,9 @@ export default class BitcoinRequestHandler { private readonly confirmationsPromiseMap: Record, validator?: (rs: any) => Error | undefined }> = {}; - constructor (requestService: RequestService) { + constructor (requestService: RequestService, chainService: ChainService) { this.#requestService = requestService; + this.#chainService = chainService; this.#logger = createLogger('BitcoinRequestHandler'); } @@ -192,10 +196,12 @@ export default class BitcoinRequestHandler { return signedTransaction.extractTransaction().toHex(); } - private bitcoinSignPsbtconfirmation (request: ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]): SignPsbtBitcoinResult { + private async signPsbt (request: ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]): Promise { // Extract necessary information from the BitcoinSendTransactionRequest const { accounts, payload } = request.payload; - const { psbt, signingIndexes } = payload; + const { broadcast, psbt, signingIndexes } = payload; + + // todo: validate type of the account if (accounts.length === 0) { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, 'Please connect to Wallet to try this request'); @@ -216,11 +222,31 @@ export default class BitcoinRequestHandler { }); const psbtCombine = psbtList[0].combine(...psbtList); - const transactionObj = psbt.extractTransaction(); + + if (!broadcast) { + return { + psbt: psbtCombine.toHex() + }; + } + + const addressType = getKeypairTypeByAddress(accounts[0].address); + + // todo: this is hotfix, will update logic to get chain value later + const chain = (() => { + if (['bittest-86', 'bittest-84'].includes(addressType)) { + return 'bitcoinTestnet'; + } + + return 'bitcoin'; + })(); + + const txid = await this.#chainService.getBitcoinApi(chain).api.simpleSendRawTransaction(psbt.extractTransaction().toHex()); + + console.log('TXID', txid); return { psbt: psbtCombine.toHex(), - txid: transactionObj.toHex() + txid }; } @@ -231,7 +257,7 @@ export default class BitcoinRequestHandler { } else if (t === 'bitcoinSendTransactionRequest') { result.payload = this.signTransactionBitcoin(request as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][0]); } else if (t === 'bitcoinSignPsbtRequest') { - result.payload = this.bitcoinSignPsbtconfirmation(request as ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]); + result.payload = await this.signPsbt(request as ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]); } if (t === 'bitcoinSignatureRequest' || t === 'bitcoinSendTransactionRequest') { diff --git a/packages/extension-base/src/services/request-service/index.ts b/packages/extension-base/src/services/request-service/index.ts index 24b59a8bd1e..dc20bf453a2 100644 --- a/packages/extension-base/src/services/request-service/index.ts +++ b/packages/extension-base/src/services/request-service/index.ts @@ -40,7 +40,7 @@ export default class RequestService { this.#authRequestHandler = new AuthRequestHandler(this, this.#chainService, this.keyringService); this.#substrateRequestHandler = new SubstrateRequestHandler(this); this.#evmRequestHandler = new EvmRequestHandler(this); - this.#bitcoinRequestHandler = new BitcoinRequestHandler(this); + this.#bitcoinRequestHandler = new BitcoinRequestHandler(this, this.#chainService); this.#connectWCRequestHandler = new ConnectWCRequestHandler(this); this.#notSupportWCRequestHandler = new NotSupportWCRequestHandler(this); From 69ca415dd7bcb2a307407227b399c294512d40a9 Mon Sep 17 00:00:00 2001 From: lw Date: Tue, 18 Jun 2024 14:29:43 +0700 Subject: [PATCH 14/43] [UI] Update response type for getAddresses --- .../src/background/KoniTypes.ts | 9 --------- .../src/koni/background/handlers/Tabs.ts | 17 +++++++++++++---- packages/extension-base/src/types/dApp.ts | 19 +++++++++++++++++++ packages/extension-inject/src/types.ts | 1 + 4 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 packages/extension-base/src/types/dApp.ts diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 578d1a248e5..a50e00c51f1 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -76,15 +76,6 @@ export interface AuthRequestV2 extends Resolver { accountAuthType: AccountAuthType; } -export type AuthAddress = { - address: string; - publicKey?: string; - tweakedPublicKey?: string; - derivationPath?: string; - isTestnet?: boolean; - type: 'p2tr' | 'p2wpkh' | 'p2sh' | 'ethereum' | 'unknown'; -} - /// Manage Auth // Get Auth diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index 2b016a02cd6..fb5ebb66e52 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -9,7 +9,7 @@ import { EvmProviderError } from '@subwallet/extension-base/background/errors/Ev import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; import { createSubscription, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AddNetworkRequestExternal, AddTokenRequestExternal, AuthAddress, BitcoinProviderErrorType, BitcoinSignPsbtRawRequest, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; +import { AddNetworkRequestExternal, AddTokenRequestExternal, BitcoinProviderErrorType, BitcoinSignPsbtRawRequest, EvmAppState, EvmEventType, EvmProviderErrorType, EvmSendTransactionParams, PassPhishing, RequestAddPspToken, RequestEvmProviderSend, RequestSettingsType, ValidateNetworkResponse } from '@subwallet/extension-base/background/KoniTypes'; import RequestBytesSign from '@subwallet/extension-base/background/RequestBytesSign'; import RequestExtrinsicSign from '@subwallet/extension-base/background/RequestExtrinsicSign'; import { AccountAuthType, MessageTypes, RequestAccountList, RequestAccountSubscribe, RequestAccountUnsubscribe, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestTypes, ResponseRpcListProviders, ResponseSigning, ResponseTypes, SubscriptionMessageTypes } from '@subwallet/extension-base/background/types'; @@ -21,6 +21,7 @@ import { _NetworkUpsertParams } from '@subwallet/extension-base/services/chain-s import { _generateCustomProviderKey } from '@subwallet/extension-base/services/chain-service/utils'; import { AuthUrls } from '@subwallet/extension-base/services/request-service/types'; import { DEFAULT_CHAIN_PATROL_ENABLE } from '@subwallet/extension-base/services/setting-service/constants'; +import { AuthAddress, RequestAddressesResult, Response } from '@subwallet/extension-base/types/dApp'; import { canDerive, getEVMChainInfo, stripUrl } from '@subwallet/extension-base/utils'; import { InjectedMetadataKnown, MetadataDef, ProviderMeta } from '@subwallet/extension-inject/types'; import { getDerivePath, getKeypairTypeByAddress } from '@subwallet/keyring'; @@ -1119,7 +1120,7 @@ export default class KoniTabs { return await this.#koniState.addTokenConfirm(id, url, tokenInfo); } - async bitcoinGetAddresses (url: string, request: RequestArguments): Promise { + async bitcoinGetAddresses (url: string, request: RequestArguments): Promise> { try { await this.#koniState.authorizeUrlV2(url, { origin: '', @@ -1129,10 +1130,18 @@ export default class KoniTabs { const authInfo = await this.getAuthInfo(url); if (!authInfo) { - return []; + return { + result: { + addresses: [] + } + }; } - return getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])); + return { + result: { + addresses: getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])) + } + }; } catch (e) { throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST); } diff --git a/packages/extension-base/src/types/dApp.ts b/packages/extension-base/src/types/dApp.ts new file mode 100644 index 00000000000..3bfa0c1501b --- /dev/null +++ b/packages/extension-base/src/types/dApp.ts @@ -0,0 +1,19 @@ +// Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +export type Response = { + result: T +} + +export type AuthAddress = { + address: string; + publicKey?: string; + tweakedPublicKey?: string; + derivationPath?: string; + isTestnet?: boolean; + type: 'p2tr' | 'p2wpkh' | 'p2sh' | 'ethereum' | 'unknown'; +} + +export type RequestAddressesResult = { + addresses: AuthAddress[]; +}; diff --git a/packages/extension-inject/src/types.ts b/packages/extension-inject/src/types.ts index f6cc41f2b52..c252fce6e9d 100644 --- a/packages/extension-inject/src/types.ts +++ b/packages/extension-inject/src/types.ts @@ -117,6 +117,7 @@ export interface InjectedWindow extends This { ethereum: EvmProvider; OpenBit: EvmProvider; OpenBitProvider: OpenBitProviderType; + LeatherProvider: OpenBitProviderType; } export type InjectedExtension = InjectedExtensionInfo & Injected; From 40ad089157fb0311f5e2f5318deb0b8234647e40 Mon Sep 17 00:00:00 2001 From: lw Date: Sat, 22 Jun 2024 10:06:21 +0700 Subject: [PATCH 15/43] Update logic for showing auth accounts --- .../src/background/KoniTypes.ts | 5 + .../src/koni/background/handlers/Extension.ts | 78 ++++--- .../src/koni/background/handlers/Tabs.ts | 14 +- .../handler/AuthRequestHandler.ts | 14 +- .../variants/AuthorizeConfirmation.tsx | 201 +++++------------- .../Security/ManageWebsiteAccess/Detail.tsx | 53 ++--- .../Security/ManageWebsiteAccess/index.tsx | 31 +-- .../AccountProxy/AccountProxyItem.tsx | 29 ++- .../Layout/parts/ConnectWebsiteModal.tsx | 75 +++---- .../Layout/parts/SelectAccount/index.tsx | 27 ++- .../src/messaging/settings/auth.ts | 4 + 11 files changed, 237 insertions(+), 294 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 578d1a248e5..7dcb177b872 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -112,6 +112,10 @@ export interface RequestAuthorizationPerAccount extends RequestAuthorization { address: string; } +export interface RequestAuthorizationPerAccountProxy extends RequestAuthorization { + proxyId: string; +} + // Manage single site with multi account export interface RequestAuthorizationPerSite { @@ -2371,6 +2375,7 @@ export interface KoniRequestSignatures { 'pri(authorize.changeSiteAll)': [RequestAuthorizationAll, boolean, AuthUrls]; 'pri(authorize.changeSite)': [RequestAuthorization, boolean, AuthUrls]; 'pri(authorize.changeSitePerAccount)': [RequestAuthorizationPerAccount, boolean, AuthUrls]; + 'pri(authorize.changeSitePerAccountProxy)': [RequestAuthorizationPerAccountProxy, boolean, AuthUrls]; 'pri(authorize.changeSitePerSite)': [RequestAuthorizationPerSite, boolean]; 'pri(authorize.changeSiteBlock)': [RequestAuthorizationBlock, boolean]; 'pri(authorize.forgetSite)': [RequestForgetSite, boolean, AuthUrls]; diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index e45d033b82e..e5a75b27334 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -7,8 +7,8 @@ import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _MultiChainAsset } from import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { createSubscription } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AccountExternalError, AccountExternalErrorCode, AccountProxiesWithCurrentProxy, AccountsWithCurrentAddress, AddressBookInfo, AmountData, AmountDataWithId, AssetSetting, AssetSettingUpdateReq, BasicTxErrorType, BasicTxWarningCode, BondingOptionParams, BrowserConfirmationType, CampaignBanner, CampaignData, CampaignDataType, ChainType, CreateDeriveAccountInfo, CronReloadRequest, CrowdloanJson, CurrentAccountInfo, DeriveAccountInfo, ExternalRequestPromiseStatus, ExtrinsicType, KeyringState, MantaPayEnableMessage, MantaPayEnableParams, MantaPayEnableResponse, MantaPaySyncState, NftCollection, NftJson, NftTransactionRequest, NftTransactionResponse, OptionInputAddress, PriceJson, RequestAccountCreateExternalV2, RequestAccountCreateHardwareMultiple, RequestAccountCreateHardwareV2, RequestAccountCreateSuriV2, RequestAccountCreateWithSecretKey, RequestAccountExportPrivateKey, RequestAccountMeta, RequestAccountProxyCreateSuri, RequestAccountProxyEdit, RequestAddInjectedAccounts, RequestApproveConnectWalletSession, RequestApproveWalletConnectNotSupport, RequestAuthorization, RequestAuthorizationBlock, RequestAuthorizationPerAccount, RequestAuthorizationPerSite, RequestAuthorizeApproveV2, RequestBatchRestoreV2, RequestBondingSubmit, RequestCameraSettings, RequestCampaignBannerComplete, RequestChangeEnableChainPatrol, RequestChangeLanguage, RequestChangeMasterPassword, RequestChangeShowBalance, RequestChangeShowZeroBalance, RequestChangeTimeAutoLock, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestConnectWalletConnect, RequestCrossChainTransfer, RequestCrowdloanContributions, RequestDeleteContactAccount, RequestDeriveCreateMultiple, RequestDeriveCreateV2, RequestDeriveCreateV3, RequestDeriveValidateV2, RequestDisconnectWalletConnectSession, RequestEditContactAccount, RequestFindRawMetadata, RequestForgetSite, RequestFreeBalance, RequestGetDeriveAccounts, RequestGetTransaction, RequestJsonRestoreV2, RequestKeyringExportAccountProxyMnemonic, RequestKeyringExportMnemonic, RequestMaxTransferable, RequestMigratePassword, RequestParseEvmContractInput, RequestParseTransactionSubstrate, RequestPassPhishingPage, RequestQrParseRLP, RequestQrSignEvm, RequestQrSignSubstrate, RequestRejectConnectWalletSession, RequestRejectExternalRequest, RequestRejectWalletConnectNotSupport, RequestRemoveInjectedAccounts, RequestResetWallet, RequestResolveExternalRequest, RequestSaveRecentAccount, RequestSeedCreateV2, RequestSeedValidateV2, RequestSettingsType, RequestSigningApprovePasswordV2, RequestStakePoolingBonding, RequestStakePoolingUnbonding, RequestSubscribeHistory, RequestSubstrateNftSubmitTransaction, RequestTransferCheckReferenceCount, RequestTransferCheckSupporting, RequestTransferExistentialDeposit, RequestTuringCancelStakeCompound, RequestTuringStakeCompound, RequestUnbondingSubmit, RequestUnlockKeyring, RequestUnlockType, ResolveAddressToDomainRequest, ResolveDomainRequest, ResponseAccountCreateSuriV2, ResponseAccountCreateWithSecretKey, ResponseAccountExportPrivateKey, ResponseAccountMeta, ResponseChangeMasterPassword, ResponseCheckPublicAndSecretKey, ResponseDeriveValidateV2, ResponseFindRawMetadata, ResponseGetDeriveAccounts, ResponseKeyringExportMnemonic, ResponseMigratePassword, ResponseParseEvmContractInput, ResponseParseTransactionSubstrate, ResponsePrivateKeyValidateV2, ResponseQrParseRLP, ResponseQrSignEvm, ResponseQrSignSubstrate, ResponseRejectExternalRequest, ResponseResetWallet, ResponseResolveExternalRequest, ResponseSeedCreateV2, ResponseSeedValidateV2, ResponseSubscribeHistory, ResponseUnlockKeyring, StakingJson, StakingRewardJson, StakingTxErrorType, StakingType, SupportTransferResponse, ThemeNames, TransactionHistoryItem, TransactionResponse, TransferTxErrorType, ValidateNetworkRequest, ValidateNetworkResponse, ValidatorInfo } from '@subwallet/extension-base/background/KoniTypes'; -import { AccountAuthType, AccountJson, AccountProxy, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountProxy, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeCancel, RequestAuthorizeReject, RequestBatchRestore, RequestCurrentAccountAddress, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApproveSignature, RequestSigningCancel, RequestTypes, ResponseAccountExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseType, SigningRequest, WindowOpenParams } from '@subwallet/extension-base/background/types'; +import { AccountExternalError, AccountExternalErrorCode, AccountProxiesWithCurrentProxy, AccountsWithCurrentAddress, AddressBookInfo, AmountData, AmountDataWithId, AssetSetting, AssetSettingUpdateReq, BasicTxErrorType, BasicTxWarningCode, BondingOptionParams, BrowserConfirmationType, CampaignBanner, CampaignData, CampaignDataType, ChainType, CreateDeriveAccountInfo, CronReloadRequest, CrowdloanJson, CurrentAccountInfo, DeriveAccountInfo, ExternalRequestPromiseStatus, ExtrinsicType, KeyringState, MantaPayEnableMessage, MantaPayEnableParams, MantaPayEnableResponse, MantaPaySyncState, NftCollection, NftJson, NftTransactionRequest, NftTransactionResponse, OptionInputAddress, PriceJson, RequestAccountCreateExternalV2, RequestAccountCreateHardwareMultiple, RequestAccountCreateHardwareV2, RequestAccountCreateSuriV2, RequestAccountCreateWithSecretKey, RequestAccountExportPrivateKey, RequestAccountMeta, RequestAccountProxyCreateSuri, RequestAccountProxyEdit, RequestAddInjectedAccounts, RequestApproveConnectWalletSession, RequestApproveWalletConnectNotSupport, RequestAuthorization, RequestAuthorizationBlock, RequestAuthorizationPerAccount, RequestAuthorizationPerAccountProxy, RequestAuthorizationPerSite, RequestAuthorizeApproveV2, RequestBatchRestoreV2, RequestBondingSubmit, RequestCameraSettings, RequestCampaignBannerComplete, RequestChangeEnableChainPatrol, RequestChangeLanguage, RequestChangeMasterPassword, RequestChangeShowBalance, RequestChangeShowZeroBalance, RequestChangeTimeAutoLock, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestConnectWalletConnect, RequestCrossChainTransfer, RequestCrowdloanContributions, RequestDeleteContactAccount, RequestDeriveCreateMultiple, RequestDeriveCreateV2, RequestDeriveCreateV3, RequestDeriveValidateV2, RequestDisconnectWalletConnectSession, RequestEditContactAccount, RequestFindRawMetadata, RequestForgetSite, RequestFreeBalance, RequestGetDeriveAccounts, RequestGetTransaction, RequestJsonRestoreV2, RequestKeyringExportAccountProxyMnemonic, RequestKeyringExportMnemonic, RequestMaxTransferable, RequestMigratePassword, RequestParseEvmContractInput, RequestParseTransactionSubstrate, RequestPassPhishingPage, RequestQrParseRLP, RequestQrSignEvm, RequestQrSignSubstrate, RequestRejectConnectWalletSession, RequestRejectExternalRequest, RequestRejectWalletConnectNotSupport, RequestRemoveInjectedAccounts, RequestResetWallet, RequestResolveExternalRequest, RequestSaveRecentAccount, RequestSeedCreateV2, RequestSeedValidateV2, RequestSettingsType, RequestSigningApprovePasswordV2, RequestStakePoolingBonding, RequestStakePoolingUnbonding, RequestSubscribeHistory, RequestSubstrateNftSubmitTransaction, RequestTransferCheckReferenceCount, RequestTransferCheckSupporting, RequestTransferExistentialDeposit, RequestTuringCancelStakeCompound, RequestTuringStakeCompound, RequestUnbondingSubmit, RequestUnlockKeyring, RequestUnlockType, ResolveAddressToDomainRequest, ResolveDomainRequest, ResponseAccountCreateSuriV2, ResponseAccountCreateWithSecretKey, ResponseAccountExportPrivateKey, ResponseAccountMeta, ResponseChangeMasterPassword, ResponseCheckPublicAndSecretKey, ResponseDeriveValidateV2, ResponseFindRawMetadata, ResponseGetDeriveAccounts, ResponseKeyringExportMnemonic, ResponseMigratePassword, ResponseParseEvmContractInput, ResponseParseTransactionSubstrate, ResponsePrivateKeyValidateV2, ResponseQrParseRLP, ResponseQrSignEvm, ResponseQrSignSubstrate, ResponseRejectExternalRequest, ResponseResetWallet, ResponseResolveExternalRequest, ResponseSeedCreateV2, ResponseSeedValidateV2, ResponseSubscribeHistory, ResponseUnlockKeyring, StakingJson, StakingRewardJson, StakingTxErrorType, StakingType, SupportTransferResponse, ThemeNames, TransactionHistoryItem, TransactionResponse, TransferTxErrorType, ValidateNetworkRequest, ValidateNetworkResponse, ValidatorInfo } from '@subwallet/extension-base/background/KoniTypes'; +import { AccountJson, AccountProxy, AuthorizeRequest, MessageTypes, MetadataRequest, RequestAccountChangePassword, RequestAccountCreateExternal, RequestAccountCreateHardware, RequestAccountCreateSuri, RequestAccountEdit, RequestAccountExport, RequestAccountForget, RequestAccountProxy, RequestAccountShow, RequestAccountTie, RequestAccountValidate, RequestAuthorizeCancel, RequestAuthorizeReject, RequestBatchRestore, RequestCurrentAccountAddress, RequestDeriveCreate, RequestDeriveValidate, RequestJsonRestore, RequestMetadataApprove, RequestMetadataReject, RequestSeedCreate, RequestSeedValidate, RequestSigningApproveSignature, RequestSigningCancel, RequestTypes, ResponseAccountExport, ResponseAuthorizeList, ResponseDeriveValidate, ResponseJsonGetAccountInfo, ResponseSeedCreate, ResponseSeedValidate, ResponseType, SigningRequest, WindowOpenParams } from '@subwallet/extension-base/background/types'; import { TransactionWarning } from '@subwallet/extension-base/background/warnings/TransactionWarning'; import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH, BTC_DUST_AMOUNT, SUPPORT_KEYPAIR_TYPES, XCM_FEE_RATIO, XCM_MIN_AMOUNT_RATIO } from '@subwallet/extension-base/constants'; import { ALLOWED_PATH } from '@subwallet/extension-base/defaults'; @@ -107,6 +107,7 @@ function transformAccountProxies (accounts: SubjectInfo): AccountProxy[] { if (!proxyMap[proxyId].accounts.length) { proxyMap[proxyId].name = accountToAdd.name; proxyMap[proxyId].isMaster = accountToAdd.isMasterAccount; + proxyMap[proxyId].isReadOnly = accountToAdd.isReadOnly; } proxyMap[proxyId].accounts.push(accountToAdd); @@ -862,33 +863,22 @@ export default class KoniExtension { return true; } - private getAccounts (): string[] { - const storedAccounts = this.#koniState.keyringService.accounts; - const transformedAccounts = transformAccounts(storedAccounts); - - return transformedAccounts.map((a) => a.address); - } - - private filterAccountsByAccountAuthType (accounts: string[], accountAuthType?: AccountAuthType): string[] { - return accounts.filter((a) => isAddressValidWithAuthType(a, accountAuthType)); - } - private _changeAuthorizationAll (connectValue: boolean, callBack?: (value: AuthUrls) => void) { this.#koniState.getAuthorize((value) => { assert(value, 'The source is not known'); - const accounts = this.getAccounts(); - Object.keys(value).forEach((url) => { if (!value[url].isAllowed) { return; } - const targetAccounts = this.filterAccountsByAccountAuthType(accounts, value[url].accountAuthType); + for (const address in this.#koniState.keyringService.accounts) { + const singleAddress = this.#koniState.keyringService.accounts[address]; - targetAccounts.forEach((address) => { - value[url].isAllowedMap[address] = connectValue; - }); + if (!singleAddress.json.meta.isReadOnly) { + value[url].isAllowedMap[address] = connectValue; + } + } }); this.#koniState.setAuthorize(value, () => { callBack && callBack(value); @@ -914,12 +904,14 @@ export default class KoniExtension { this.#koniState.getAuthorize((value) => { assert(value[url], 'The source is not known'); - const accounts = this.getAccounts(); - const targetAccounts = this.filterAccountsByAccountAuthType(accounts, value[url].accountAuthType); + for (const address in this.#koniState.keyringService.accounts) { + const singleAddress = this.#koniState.keyringService.accounts[address]; + + if (!singleAddress.json.meta.isReadOnly) { + value[url].isAllowedMap[address] = connectValue; + } + } - targetAccounts.forEach((address) => { - value[url].isAllowedMap[address] = connectValue; - }); this.#koniState.setAuthorize(value, () => { callBack && callBack(value); }); @@ -954,6 +946,7 @@ export default class KoniExtension { return true; } + // deprecated private _changeAuthorizationPerAcc (address: string, connectValue: boolean, url: string, callBack?: (value: AuthUrls) => void) { this.#koniState.getAuthorize((value) => { assert(value, 'The source is not known'); @@ -970,6 +963,27 @@ export default class KoniExtension { }); } + private _changeAuthorizationPerAccountProxy (proxyId: string, connectValue: boolean, url: string, callBack?: (value: AuthUrls) => void) { + this.#koniState.getAuthorize((value) => { + assert(value, 'The source is not known'); + + this.#koniState.setAuthorize(value, () => { + const accounts = this.#koniState.keyringService.accounts; + + for (const address in accounts) { + const singleAddress = accounts[address]; + const _proxyId = (singleAddress.json.meta.proxyId || '') as string; + + if (_proxyId && _proxyId === proxyId && !singleAddress.json.meta.isReadOnly) { + value[url].isAllowedMap[singleAddress.json.address] = connectValue; + } + } + + callBack && callBack(value); + }); + }); + } + private _changeAuthorizationBlock (connectValue: boolean, id: string) { this.#koniState.getAuthorize((value) => { assert(value, 'The source is not known'); @@ -1004,6 +1018,20 @@ export default class KoniExtension { return true; } + private changeAuthorizationPerAccountProxy (data: RequestAuthorizationPerAccountProxy, id: string, port: chrome.runtime.Port): boolean { + const cb = createSubscription<'pri(authorize.changeSitePerAccountProxy)'>(id, port); + + this._changeAuthorizationPerAccountProxy(data.proxyId, data.connectValue, data.url, (items) => { + cb(items); + }); + + port.onDisconnect.addListener((): void => { + this.cancelSubscription(id); + }); + + return true; + } + private changeAuthorizationPerSite (data: RequestAuthorizationPerSite): boolean { this._changeAuthorizationPerSite(data.values, data.id); @@ -5098,6 +5126,8 @@ export default class KoniExtension { return this.changeAuthorization(request as RequestAuthorization, id, port); case 'pri(authorize.changeSitePerAccount)': return this.changeAuthorizationPerAcc(request as RequestAuthorizationPerAccount, id, port); + case 'pri(authorize.changeSitePerAccountProxy)': + return this.changeAuthorizationPerAccountProxy(request as RequestAuthorizationPerAccountProxy, id, port); case 'pri(authorize.changeSitePerSite)': return this.changeAuthorizationPerSite(request as RequestAuthorizationPerSite); case 'pri(authorize.changeSiteBlock)': diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index 2b016a02cd6..cf978db2296 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -93,6 +93,12 @@ const getAuthAddresses = (addresses: string[]) => { return; } + const pair = keyring.getPair(address); + + if (pair.meta.isReadOnly) { + return; + } + const item: AuthAddress = { address, type: (() => { @@ -116,14 +122,6 @@ const getAuthAddresses = (addresses: string[]) => { item.isTestnet = true; } - const pair = keyring.getPair(address); - - if (pair.meta.isReadOnly) { - result.push(item); - - return; - } - const deriFunc = getDerivePath(keypairType); const index = parseInt((pair.meta.suri as string)?.split('//')[1]) || 0; diff --git a/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts index b33e4158f59..53a7c6acd43 100644 --- a/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts @@ -214,7 +214,7 @@ export default class AuthRequestHandler { private authorizePromiseMap: Record> = {}; public async authorizeUrlV2 (url: string, request: RequestAuthorizeTab): Promise { let authList = await this.getAuthList(); - let accountAuthType = request.accountAuthType || 'substrate'; + let accountAuthType = request.accountAuthType || 'both'; request.accountAuthType = accountAuthType; @@ -287,13 +287,13 @@ export default class AuthRequestHandler { .map(([address, allowed]) => (allowed ? address : '')) .filter((item) => (item !== '')); - let allowedListByRequestType = [...request.allowedAccounts]; + const allowedListByRequestType = [...request.allowedAccounts]; - if (accountAuthType === 'evm') { - allowedListByRequestType = allowedListByRequestType.filter((a) => isEthereumAddress(a)); - } else if (accountAuthType === 'substrate') { - allowedListByRequestType = allowedListByRequestType.filter((a) => !isEthereumAddress(a)); - } + // if (accountAuthType === 'evm') { + // allowedListByRequestType = allowedListByRequestType.filter((a) => isEthereumAddress(a)); + // } else if (accountAuthType === 'substrate') { + // allowedListByRequestType = allowedListByRequestType.filter((a) => !isEthereumAddress(a)); + // } if (!confirmAnotherType && !request.reConfirm && allowedListByRequestType.length !== 0) { // Prevent appear confirmation popup diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx index 2689edfc78c..4e999a2bb9a 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/AuthorizeConfirmation.tsx @@ -1,23 +1,19 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { AccountAuthType, AccountJson, AuthorizeRequest } from '@subwallet/extension-base/background/types'; +import { AuthorizeRequest } from '@subwallet/extension-base/background/types'; import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants'; -import { AccountItemWithName, ConfirmationGeneralInfo } from '@subwallet/extension-koni-ui/components'; -import { DEFAULT_ACCOUNT_TYPES, EVM_ACCOUNT_TYPE, SUBSTRATE_ACCOUNT_TYPE } from '@subwallet/extension-koni-ui/constants'; -import { useSetSelectedAccountTypes } from '@subwallet/extension-koni-ui/hooks'; +import { AccountProxyAvatarGroup, AccountProxyItem, ConfirmationGeneralInfo } from '@subwallet/extension-koni-ui/components'; import { approveAuthRequestV2, cancelAuthRequestV2, rejectAuthRequestV2 } from '@subwallet/extension-koni-ui/messaging'; import { RootState } from '@subwallet/extension-koni-ui/stores'; import { ThemeProps } from '@subwallet/extension-koni-ui/types'; -import { isAccountAll, isNoAccount } from '@subwallet/extension-koni-ui/utils'; -import { KeypairType } from '@subwallet/keyring/types'; +import { isAccountAll } from '@subwallet/extension-koni-ui/utils'; import { Button, Icon } from '@subwallet/react-ui'; import CN from 'classnames'; -import { PlusCircle, ShieldSlash, XCircle } from 'phosphor-react'; +import { ShieldSlash } from 'phosphor-react'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; -import { useNavigate } from 'react-router-dom'; import styled from 'styled-components'; interface Props extends ThemeProps { @@ -36,68 +32,23 @@ async function handleBlock ({ id }: AuthorizeRequest) { return await rejectAuthRequestV2(id); } -export const filterAuthorizeAccounts = (accounts: AccountJson[], accountAuthType: AccountAuthType) => { - let rs = [...accounts]; - - // rs = rs.filter((acc) => acc.isReadOnly !== true); - - if (accountAuthType === 'evm') { - rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type === 'ethereum')); - } else if (accountAuthType === 'substrate') { - rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type && ['ed25519', 'sr25519', 'ecdsa'].includes(acc.type))); - } else if (accountAuthType === 'bitcoin') { - rs = rs.filter((acc) => (!isAccountAll(acc.address) && acc.type && ['bitcoin-86', 'bitcoin-84', 'bittest-84', 'bittest-86'].includes(acc.type))); - } else { - rs = rs.filter((acc) => !isAccountAll(acc.address)); - } - - if (isNoAccount(rs)) { - return []; - } - - return rs; -}; - function Component ({ className, request }: Props) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); - const { accountAuthType, allowedAccounts } = request.request; - const accounts = useSelector((state: RootState) => state.accountState.accounts); - const navigate = useNavigate(); - const setSelectedAccountTypes = useSetSelectedAccountTypes(true); + const { allowedAccounts } = request.request; + const accountProxies = useSelector((state: RootState) => state.accountState.accountProxies); - // List all of all accounts by auth type - const visibleAccounts = useMemo(() => (filterAuthorizeAccounts(accounts, accountAuthType || 'both')), - [accountAuthType, accounts]); + const visibleAccountProxies = useMemo(() => { + return accountProxies.filter((ap) => !isAccountAll(ap.proxyId) && !ap.isReadOnly); + }, + [accountProxies]); // Selected map with default values is map of all accounts const [selectedMap, setSelectedMap] = useState>({}); const isDisableConnect = useMemo(() => { - return !visibleAccounts.filter(({ address }) => !!selectedMap[address]).length; - }, [selectedMap, visibleAccounts]); - - const noAvailableTitle = useMemo(() => { - switch (accountAuthType) { - case 'substrate': - return t('No available Substrate account'); - case 'evm': - return t('No available EVM account'); - default: - return t('No available account'); - } - }, [accountAuthType, t]); - - const noAvailableDescription = useMemo(() => { - switch (accountAuthType) { - case 'substrate': - return t("You don't have any Substrate account to connect. Please create one or skip this step by hitting Cancel."); - case 'evm': - return t("You don't have any EVM account to connect. Please create one or skip this step by hitting Cancel."); - default: - return t("You don't have any account to connect. Please create one or skip this step by hitting Cancel."); - } - }, [accountAuthType, t]); + return !visibleAccountProxies.filter((ap) => selectedMap[ap.proxyId]).length; + }, [selectedMap, visibleAccountProxies]); // Handle buttons actions const onBlock = useCallback(() => { @@ -116,36 +67,28 @@ function Component ({ className, request }: Props) { const onConfirm = useCallback(() => { setLoading(true); - const selectedAccounts = Object.keys(selectedMap).filter((key) => selectedMap[key]); + const selectedAccountProxies = Object.keys(selectedMap).filter((key) => !isAccountAll(key) && selectedMap[key]); + + const selectedAccounts: string[] = []; + + visibleAccountProxies.forEach((ap) => { + if (selectedAccountProxies.includes(ap.proxyId)) { + ap.accounts.forEach((a) => { + selectedAccounts.push(a.address); + }); + } + }); handleConfirm(request, selectedAccounts).finally(() => { setLoading(false); }); - }, [request, selectedMap]); - - const onAddAccount = useCallback(() => { - let types: KeypairType[]; - - switch (accountAuthType) { - case 'substrate': - types = [SUBSTRATE_ACCOUNT_TYPE]; - break; - case 'evm': - types = [EVM_ACCOUNT_TYPE]; - break; - default: - types = DEFAULT_ACCOUNT_TYPES; - } - - setSelectedAccountTypes(types); - navigate('/accounts/new-seed-phrase', { state: { useGoBack: true } }); - }, [accountAuthType, setSelectedAccountTypes, navigate]); + }, [request, selectedMap, visibleAccountProxies]); const onAccountSelect = useCallback((address: string) => { const isAll = isAccountAll(address); return () => { - const visibleAddresses = visibleAccounts.map((item) => item.address); + const visibleAPs = visibleAccountProxies.map((item) => item.proxyId); setSelectedMap((map) => { const isChecked = !map[address]; @@ -153,14 +96,14 @@ function Component ({ className, request }: Props) { if (isAll) { // Select/deselect all accounts - visibleAddresses.forEach((key) => { + visibleAPs.forEach((key) => { newMap[key] = isChecked; }); newMap[ALL_ACCOUNT_KEY] = isChecked; } else { // Select/deselect single account and trigger all account newMap[address] = isChecked; - newMap[ALL_ACCOUNT_KEY] = visibleAddresses + newMap[ALL_ACCOUNT_KEY] = visibleAPs .filter((i) => !isAccountAll(i)) .every((item) => newMap[item]); } @@ -168,24 +111,24 @@ function Component ({ className, request }: Props) { return newMap; }); }; - }, [visibleAccounts]); + }, [visibleAccountProxies]); // Create selected map by default useEffect(() => { setSelectedMap((map) => { const existedKey = Object.keys(map); - accounts.forEach((item) => { - if (!existedKey.includes(item.address)) { - map[item.address] = (allowedAccounts || []).includes(item.address); + accountProxies.forEach((item) => { + if (!existedKey.includes(item.proxyId)) { + map[item.proxyId] = item.accounts.some((a) => allowedAccounts && allowedAccounts.includes(a.address)); } }); - map[ALL_ACCOUNT_KEY] = visibleAccounts.every((item) => map[item.address]); + map[ALL_ACCOUNT_KEY] = visibleAccountProxies.every((item) => map[item.proxyId]); return { ...map }; }); - }, [accounts, allowedAccounts, visibleAccounts]); + }, [accountProxies, allowedAccounts, visibleAccountProxies]); return ( <> @@ -195,43 +138,43 @@ function Component ({ className, request }: Props) { className={CN( 'title', { - 'sub-title': visibleAccounts.length > 0 + 'sub-title': visibleAccountProxies.length > 0 } )} > { - visibleAccounts.length === 0 - ? noAvailableTitle + visibleAccountProxies.length === 0 + ? t('No available account') : t('Choose the account(s) you’d like to connect') }
{ - !!visibleAccounts.length && ( + !!visibleAccountProxies.length && (
{ - visibleAccounts.length > 1 && + visibleAccountProxies.length > 1 && ( - + } onClick={onAccountSelect(ALL_ACCOUNT_KEY)} showUnselectIcon /> ) } - {visibleAccounts.map((item) => ( - ( + ))} @@ -240,15 +183,15 @@ function Component ({ className, request }: Props) { }
{ - visibleAccounts.length === 0 - ? noAvailableDescription + visibleAccountProxies.length === 0 + ? t("You don't have any account to connect. Please create one or skip this step by hitting Cancel.") : t('Make sure you trust this site before connecting') }
{ - visibleAccounts.length > 0 && + visibleAccountProxies.length > 0 && ( <> - - - ) - }
); diff --git a/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/Detail.tsx b/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/Detail.tsx index d5c2b909a20..2f0877d958b 100644 --- a/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/Detail.tsx +++ b/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/Detail.tsx @@ -2,16 +2,15 @@ // SPDX-License-Identifier: Apache-2.0 import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; -import { AccountAuthType, AccountJson } from '@subwallet/extension-base/background/types'; -import { AccountItemWithName, EmptyList, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components'; +import { AccountJson, AccountProxy } from '@subwallet/extension-base/background/types'; +import { AccountProxyItem, EmptyList, Layout, PageWrapper } from '@subwallet/extension-koni-ui/components'; import { ActionItemType, ActionModal } from '@subwallet/extension-koni-ui/components/Modal/ActionModal'; import useDefaultNavigate from '@subwallet/extension-koni-ui/hooks/router/useDefaultNavigate'; -import { changeAuthorization, changeAuthorizationPerAccount, forgetSite, toggleAuthorization } from '@subwallet/extension-koni-ui/messaging'; +import { changeAuthorization, changeAuthorizationPerAccountProxy, forgetSite, toggleAuthorization } from '@subwallet/extension-koni-ui/messaging'; import { RootState } from '@subwallet/extension-koni-ui/stores'; import { updateAuthUrls } from '@subwallet/extension-koni-ui/stores/utils'; import { Theme, ThemeProps } from '@subwallet/extension-koni-ui/types'; import { ManageWebsiteAccessDetailParam } from '@subwallet/extension-koni-ui/types/navigation'; -import { accountByAuthTypeFilter } from '@subwallet/extension-koni-ui/utils'; import { Icon, ModalContext, Switch, SwList } from '@subwallet/react-ui'; import { GearSix, MagnifyingGlass, Plugs, PlugsConnected, ShieldCheck, ShieldSlash, X } from 'phosphor-react'; import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; @@ -30,15 +29,16 @@ type WrapperProps = ThemeProps; const ActionModalId = 'actionModalId'; // const FilterModalId = 'filterModalId'; -function Component ({ accountAuthType, authInfo, className = '', goBack, origin, siteName }: Props): React.ReactElement { - const accounts = useSelector((state: RootState) => state.accountState.accounts); +function Component ({ authInfo, className = '', goBack, origin, siteName }: Props): React.ReactElement { + const accountProxies = useSelector((state: RootState) => state.accountState.accountProxies); const [pendingMap, setPendingMap] = useState>({}); const { activeModal, inactiveModal } = useContext(ModalContext); const { t } = useTranslation(); const { token } = useTheme() as Theme; - const accountItems = useMemo(() => { - return accounts.filter((opt) => opt.address !== 'ALL' && accountByAuthTypeFilter(opt.address, accountAuthType as AccountAuthType)); - }, [accountAuthType, accounts]); + + const accountProxyItems = useMemo(() => { + return accountProxies.filter((opt) => opt.proxyId !== 'ALL' && !opt.isReadOnly); + }, [accountProxies]); const onOpenActionModal = useCallback(() => { activeModal(ActionModalId); @@ -106,23 +106,23 @@ function Component ({ accountAuthType, authInfo, className = '', goBack, origin, return result; }, [authInfo.isAllowed, onCloseActionModal, origin, t, token]); - const renderItem = useCallback((item: AccountJson) => { - const isEnabled: boolean = authInfo.isAllowedMap[item.address]; + const renderItem = useCallback((item: AccountProxy) => { + const isEnabled: boolean = item.accounts.some((a) => authInfo.isAllowedMap[a.address]); const onClick = () => { setPendingMap((prevMap) => { return { ...prevMap, - [item.address]: !isEnabled + [item.proxyId]: !isEnabled }; }); - changeAuthorizationPerAccount(item.address, !isEnabled, origin, updateAuthUrls) + changeAuthorizationPerAccountProxy(item.proxyId, !isEnabled, origin, updateAuthUrls) .catch(console.log) .finally(() => { setPendingMap((prevMap) => { const newMap = { ...prevMap }; - delete newMap[item.address]; + delete newMap[item.proxyId]; return newMap; }); @@ -130,22 +130,21 @@ function Component ({ accountAuthType, authInfo, className = '', goBack, origin, }; return ( - )} /> ); - }, [authInfo.isAllowed, authInfo.isAllowedMap, origin, pendingMap, token.sizeLG]); + }, [authInfo.isAllowed, authInfo.isAllowedMap, origin, pendingMap]); const searchFunc = useCallback((item: AccountJson, searchText: string) => { const searchTextLowerCase = searchText.toLowerCase(); @@ -198,12 +197,10 @@ function Component ({ accountAuthType, authInfo, className = '', goBack, origin, title={siteName || authInfo.id} > ('Search account')} @@ -262,6 +259,10 @@ const ManageWebsiteAccessDetail = styled(WrapperComponent)(({ theme: { to paddingTop: token.paddingSM }, + '.account-proxy-item': { + marginBottom: token.marginXS + }, + '&.action-modal': { '.__action-item.block .ant-setting-item-name': { color: token.colorError diff --git a/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/index.tsx b/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/index.tsx index 23048344fcc..d201826f5fe 100644 --- a/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Settings/Security/ManageWebsiteAccess/index.tsx @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; +import { AccountProxy } from '@subwallet/extension-base/background/types'; +import { isAccountAll } from '@subwallet/extension-base/utils'; import { ActionItemType, ActionModal, EmptyList, FilterModal, PageWrapper, WebsiteAccessItem } from '@subwallet/extension-koni-ui/components'; import { useDefaultNavigate, useFilterModal } from '@subwallet/extension-koni-ui/hooks'; import { changeAuthorizationAll, forgetAllSite } from '@subwallet/extension-koni-ui/messaging'; @@ -16,26 +18,26 @@ import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import styled, { useTheme } from 'styled-components'; -import { isEthereumAddress } from '@polkadot/util-crypto'; - type Props = ThemeProps; function getWebsiteItems (authUrlMap: Record): AuthUrlInfo[] { return Object.values(authUrlMap); } -function getAccountCount (item: AuthUrlInfo): number { - const authType = item.accountAuthType; +function getAccountCount (item: AuthUrlInfo, accountProxies: AccountProxy[]): number { + let result = 0; - if (authType === 'evm') { - return item.isAllowedMap ? Object.entries(item.isAllowedMap).filter(([address, rs]) => rs && isEthereumAddress(address)).length : 0; - } + accountProxies.forEach((ap) => { + if (isAccountAll(ap.proxyId) || ap.isReadOnly) { + return; + } - if (authType === 'substrate') { - return item.isAllowedMap ? Object.entries(item.isAllowedMap).filter(([address, rs]) => rs && !isEthereumAddress(address)).length : 0; - } + if (ap.accounts.some((a) => item.isAllowedMap[a.address])) { + ++result; + } + }); - return Object.values(item.isAllowedMap).filter((i) => i).length; + return result; } const ACTION_MODAL_ID = 'actionModalId'; @@ -51,6 +53,7 @@ enum FilterValue { function Component ({ className = '' }: Props): React.ReactElement { const authUrlMap = useSelector((state: RootState) => state.settings.authUrls); + const accountProxies = useSelector((state: RootState) => state.accountState.accountProxies); const { activeModal, inactiveModal } = useContext(ModalContext); const { t } = useTranslation(); const navigate = useNavigate(); @@ -101,8 +104,6 @@ function Component ({ className = '' }: Props): React.ReactElement { }, [t]); const websiteAccessItems = useMemo(() => { - console.log('authUrlMap', authUrlMap); - return getWebsiteItems(authUrlMap); }, [authUrlMap]); @@ -163,7 +164,7 @@ function Component ({ className = '' }: Props): React.ReactElement { (item: AuthUrlInfo) => { return ( { /> ); }, - [onClickItem] + [accountProxies, onClickItem] ); const renderEmptyList = useCallback(() => { diff --git a/packages/extension-koni-ui/src/components/AccountProxy/AccountProxyItem.tsx b/packages/extension-koni-ui/src/components/AccountProxy/AccountProxyItem.tsx index e1b382abe6a..a0814835371 100644 --- a/packages/extension-koni-ui/src/components/AccountProxy/AccountProxyItem.tsx +++ b/packages/extension-koni-ui/src/components/AccountProxy/AccountProxyItem.tsx @@ -14,18 +14,22 @@ import styled, { ThemeContext } from 'styled-components'; type Props = ThemeProps & { accountProxy: AccountProxy; isSelected?: boolean; + showUnselectIcon?: boolean; renderRightPart?: (existNode: React.ReactNode) => React.ReactNode; + rightPartNode?: React.ReactNode; + leftPartNode?: React.ReactNode; onClick?: VoidFunction; + accountProxyName?: string; }; function Component (props: Props): React.ReactElement { - const { accountProxy, className, isSelected, onClick, renderRightPart } = props; + const { accountProxy, accountProxyName, className, isSelected, leftPartNode, onClick, renderRightPart, rightPartNode, showUnselectIcon } = props; const token = useContext(ThemeContext as Context).token; - const checkedIconNode = (isSelected && ( + const checkedIconNode = ((showUnselectIcon || isSelected) && (
{ onClick={onClick} >
- + { + leftPartNode || ( + + ) + }
- {accountProxy.name} + {accountProxyName || accountProxy.name}
- {renderRightPart ? renderRightPart(checkedIconNode) : checkedIconNode} + {rightPartNode || (renderRightPart ? renderRightPart(checkedIconNode) : checkedIconNode)}
); @@ -69,7 +77,8 @@ const AccountProxyItem = styled(Component)(({ theme }) => { gap: token.sizeSM, '.__item-middle-part': { - flex: 1 + flex: 1, + textAlign: 'left' }, '.__item-right-part': { diff --git a/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx b/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx index c5aa9266360..9bb461c888a 100644 --- a/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx +++ b/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx @@ -2,8 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { AuthUrlInfo } from '@subwallet/extension-base/background/handlers/State'; +import { AccountProxy } from '@subwallet/extension-base/background/types'; import { isAccountAll } from '@subwallet/extension-base/utils'; -import AccountItemWithName from '@subwallet/extension-koni-ui/components/Account/Item/AccountItemWithName'; +import { AccountProxyItem } from '@subwallet/extension-koni-ui/components'; import ConfirmationGeneralInfo from '@subwallet/extension-koni-ui/components/Confirmation/ConfirmationGeneralInfo'; import { changeAuthorizationBlock, changeAuthorizationPerSite } from '@subwallet/extension-koni-ui/messaging'; import { RootState } from '@subwallet/extension-koni-ui/stores'; @@ -35,19 +36,26 @@ function Component ({ authInfo, className = '', id, isBlocked = true, isNotConne const { t } = useTranslation(); const [allowedMap, setAllowedMap] = useState>(authInfo?.isAllowedMap || {}); - const accounts = useSelector((state: RootState) => state.accountState.accounts); - const currentAccount = useSelector((state: RootState) => state.accountState.currentAccount); + const accountProxies = useSelector((state: RootState) => state.accountState.accountProxies); + const currentAccountProxy = useSelector((state: RootState) => state.accountState.currentAccountProxy); // const [oldConnected, setOldConnected] = useState(0); const [isSubmit, setIsSubmit] = useState(false); const { token } = useTheme() as Theme; const _isNotConnected = isNotConnected || !authInfo; - const handlerUpdateMap = useCallback((address: string, oldValue: boolean) => { + const handlerUpdateMap = useCallback((item: AccountProxy, oldValue: boolean) => { return () => { - setAllowedMap((values) => ({ - ...values, - [address]: !oldValue - })); + setAllowedMap((values) => { + const result = { + ...values + }; + + item.accounts.forEach((a) => { + result[a.address] = !oldValue; + }); + + return result; + }); }; }, []); @@ -235,9 +243,9 @@ function Component ({ authInfo, className = '', id, isBlocked = true, isNotConne ); } - const list = Object.entries(allowedMap).map(([address, value]) => ({ address, value })); + const list = accountProxies.filter((ap) => !isAccountAll(ap.proxyId) && !ap.isReadOnly); - const current = list.find(({ address }) => address === currentAccount?.address); + const current = list.find(({ proxyId }) => proxyId === currentAccountProxy?.proxyId); if (current) { const idx = list.indexOf(current); @@ -254,27 +262,16 @@ function Component ({ authInfo, className = '', id, isBlocked = true, isNotConne
{ - list.map(({ address, value }) => { - const account = accounts.find((acc) => acc.address === address); - - if (!account || isAccountAll(account.address)) { - return null; - } - - const isCurrent = account.address === currentAccount?.address; + list.map((item) => { + const isSelected = item.accounts.some((a) => allowedMap[a.address]); return ( - ); @@ -342,25 +339,7 @@ export const ConnectWebsiteModal = styled(Component)(({ theme: { token } marginTop: token.margin }, - '.account-item-with-name': { - position: 'relative', - cursor: 'pointer', - - '&:before': { - content: '""', - position: 'absolute', - inset: 0, - display: 'block', - border: '2px solid transparent', - borderRadius: token.borderRadiusLG - }, - - '&:-is-current:before': { - borderColor: token.colorPrimary - } - }, - - '.account-item-with-name + .account-item-with-name': { + '.account-proxy-item + .account-proxy-item': { marginTop: token.marginSM }, diff --git a/packages/extension-koni-ui/src/components/Layout/parts/SelectAccount/index.tsx b/packages/extension-koni-ui/src/components/Layout/parts/SelectAccount/index.tsx index 8659badb551..cf3cc26bb94 100644 --- a/packages/extension-koni-ui/src/components/Layout/parts/SelectAccount/index.tsx +++ b/packages/extension-koni-ui/src/components/Layout/parts/SelectAccount/index.tsx @@ -9,7 +9,7 @@ import { saveCurrentAccountProxy } from '@subwallet/extension-koni-ui/messaging' import { RootState } from '@subwallet/extension-koni-ui/stores'; import { Theme } from '@subwallet/extension-koni-ui/themes'; import { ThemeProps } from '@subwallet/extension-koni-ui/types'; -import { accountByAuthTypeFilter, funcSortByProxyName, isAccountAll, searchAccountProxyFunction } from '@subwallet/extension-koni-ui/utils'; +import { funcSortByProxyName, isAccountAll, searchAccountProxyFunction } from '@subwallet/extension-koni-ui/utils'; import { BackgroundIcon, Icon, ModalContext, SelectModal, Tooltip } from '@subwallet/react-ui'; import CN from 'classnames'; import { CaretDown, Plug, Plugs, PlugsConnected } from 'phosphor-react'; @@ -191,7 +191,6 @@ function Component ({ className }: Props): React.ReactElement { setConnected(0); setConnectionState(ConnectionStatement.BLOCKED); } else { - const type = currentAuth.accountAuthType; const allowedMap = currentAuth.isAllowedMap; if (!isAllAccount) { @@ -200,6 +199,10 @@ function Component ({ className }: Props): React.ReactElement { return undefined; } + if (currentAccountProxy.isReadOnly) { + return false; + } + return currentAccountProxy.accounts.some((a) => allowedMap[a.address]); })(); @@ -212,23 +215,25 @@ function Component ({ className }: Props): React.ReactElement { setConnectionState(isAllowed ? ConnectionStatement.CONNECTED : ConnectionStatement.DISCONNECTED); } } else { - const numberAccounts = noAllAccountProxies.reduce((numAccount, currentValue) => { - currentValue.accounts.find(({ address }) => accountByAuthTypeFilter(address, type)) && numAccount++; + const numberAccountProxies = noAllAccountProxies.reduce((numAccountProxy, currentValue) => { + !currentValue.isReadOnly && numAccountProxy++; + + return numAccountProxy; + }, 0); + + const numberAllowedAccounts = noAllAccountProxies.reduce((numAccountProxy, currentValue) => { + !currentValue.isReadOnly && currentValue.accounts.some((a) => allowedMap[a.address]) && numAccountProxy++; - return numAccount; + return numAccountProxy; }, 0); - const numberAllowedAccounts = Object.entries(allowedMap) - .filter(([address]) => accountByAuthTypeFilter(address, type)) - .filter(([, value]) => value) - .length; setConnected(numberAllowedAccounts); - setCanConnect(numberAccounts); + setCanConnect(numberAccountProxies); if (numberAllowedAccounts === 0) { setConnectionState(ConnectionStatement.DISCONNECTED); } else { - if (numberAllowedAccounts > 0 && numberAllowedAccounts < numberAccounts) { + if (numberAllowedAccounts > 0 && numberAllowedAccounts < numberAccountProxies) { setConnectionState(ConnectionStatement.PARTIAL_CONNECTED); } else { setConnectionState(ConnectionStatement.CONNECTED); diff --git a/packages/extension-koni-ui/src/messaging/settings/auth.ts b/packages/extension-koni-ui/src/messaging/settings/auth.ts index 4c8d03e7c41..223b6816887 100644 --- a/packages/extension-koni-ui/src/messaging/settings/auth.ts +++ b/packages/extension-koni-ui/src/messaging/settings/auth.ts @@ -31,6 +31,10 @@ export async function changeAuthorizationPerAccount (address: string, connectVal return sendMessage('pri(authorize.changeSitePerAccount)', { address, url, connectValue }, callback); } +export async function changeAuthorizationPerAccountProxy (accountProxyId: string, connectValue: boolean, url: string, callback: (data: AuthUrls) => void): Promise { + return sendMessage('pri(authorize.changeSitePerAccountProxy)', { proxyId: accountProxyId, url, connectValue }, callback); +} + export async function changeAuthorizationPerSite (request: RequestAuthorizationPerSite): Promise { return sendMessage('pri(authorize.changeSitePerSite)', request); } From 7cce940a144a44241a8f474389b5f1d1b3716195 Mon Sep 17 00:00:00 2001 From: lw Date: Sat, 22 Jun 2024 10:16:04 +0700 Subject: [PATCH 16/43] Update keyring lib --- local-libs/keyring/cjs/pair/index.js | 25 ++++--- local-libs/keyring/cjs/types.js | 10 ++- local-libs/keyring/cjs/utils/derive-path.js | 14 ++-- local-libs/keyring/package.json | 1 + local-libs/keyring/pair/index.js | 27 ++++---- local-libs/keyring/types.d.ts | 5 +- local-libs/keyring/utils/derive-path.d.ts | 2 +- local-libs/keyring/utils/derive-path.js | 14 ++-- yarn.lock | 74 +++++++++++++++++++-- 9 files changed, 124 insertions(+), 48 deletions(-) diff --git a/local-libs/keyring/cjs/pair/index.js b/local-libs/keyring/cjs/pair/index.js index 3ea2a566a03..965b08a726f 100644 --- a/local-libs/keyring/cjs/pair/index.js +++ b/local-libs/keyring/cjs/pair/index.js @@ -5,8 +5,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.createPair = createPair; +var _bip322Js = require("bip322-js"); var bitcoin = _interopRequireWildcard(require("bitcoinjs-lib")); -var _bitcoinjsMessage = require("bitcoinjs-message"); var _ecpair = require("ecpair"); var _ethSimpleKeyring = _interopRequireDefault(require("eth-simple-keyring")); var ecc = _interopRequireWildcard(require("tiny-secp256k1")); @@ -108,13 +108,13 @@ function createPair(_ref, _ref2) { const encodeAddress = () => { const raw = _utils.TYPE_ADDRESS[type](publicKey); const bitNetwork = ['bitcoin-44', 'bitcoin-84', 'bitcoin-86'].includes(type) ? bitcoin.networks.bitcoin : ['bittest-44', 'bittest-84', 'bittest-86'].includes(type) ? bitcoin.networks.testnet : bitcoin.networks.regtest; + let dataKey; /** * With bitcoin accounts, some attached account have no public key (only address). * In this case, public key is the hash of result after decoded address. * Add `noPublicKey` in metadata for this case. */ - let dataKey; if (meta.noPublicKey) { dataKey = 'hash'; } else { @@ -316,21 +316,24 @@ function createPair(_ref, _ref2) { type }, derived, meta, null); }, - signMessage: (message, compressed, options) => { + signMessage: message => { if (isLocked(secretKey)) { throw new Error('Cannot encrypt with a locked key pair'); } - const _message = typeof message === 'string' ? message : Buffer.from(message); + const _message = typeof message === 'string' ? message : (0, _util.u8aToString)(message); + const address = encodeAddress(); + const _pair = ECPair.fromPrivateKey(Buffer.from(secretKey)); + const wif = _pair.toWIF(); // Sign the message - const signature = (0, _bitcoinjsMessage.sign)(_message, Buffer.from(secretKey), compressed, options); - return signature.toString('base64'); + const signature = _bip322Js.Signer.sign(wif, address, _message); + return typeof signature === 'string' ? signature : signature.toString('base64'); }, - signTransaction: (transaction, indexes) => { + signTransaction: (psbt, indexes, sighashTypes, tapLeafHashToSign) => { if (isLocked(secretKey)) { throw new Error('Cannot encrypt with a locked key pair'); } - if (!transaction) { + if (!psbt) { throw new Error('Not found sign method'); } const pair = ECPair.fromPrivateKey(Buffer.from(secretKey)); @@ -338,12 +341,12 @@ function createPair(_ref, _ref2) { for (const index of indexes) { if (isTaproot) { const tweakedSigner = pair.tweak(bitcoin.crypto.taggedHash('TapTweak', toXOnly(pair.publicKey))); - transaction.signTaprootInput(index, tweakedSigner); + psbt.signTaprootInput(index, tweakedSigner, tapLeafHashToSign, sighashTypes); } else { - transaction.signInput(index, pair); + psbt.signInput(index, pair, sighashTypes); } } - return transaction; + return psbt; }, get output() { return output || Buffer.from([]); diff --git a/local-libs/keyring/cjs/types.js b/local-libs/keyring/cjs/types.js index 765402d2755..f7de91985af 100644 --- a/local-libs/keyring/cjs/types.js +++ b/local-libs/keyring/cjs/types.js @@ -3,13 +3,21 @@ Object.defineProperty(exports, "__esModule", { value: true }); -exports.BitcoinAddressType = void 0; +exports.SubstrateKeypairTypes = exports.EthereumKeypairTypes = exports.BitcoinKeypairTypes = exports.BitcoinAddressType = void 0; // Copyright 2017-2022 @polkadot/keyring authors & contributors // SPDX-License-Identifier: Apache-2.0 + /** * * * */ + +const SubstrateKeypairTypes = ['sr25519', 'ed25519', 'ecdsa']; +exports.SubstrateKeypairTypes = SubstrateKeypairTypes; +const EthereumKeypairTypes = ['ethereum']; +exports.EthereumKeypairTypes = EthereumKeypairTypes; +const BitcoinKeypairTypes = ['bitcoin-44', 'bitcoin-84', 'bitcoin-86', 'bittest-44', 'bittest-84', 'bittest-86']; +exports.BitcoinKeypairTypes = BitcoinKeypairTypes; let BitcoinAddressType; exports.BitcoinAddressType = BitcoinAddressType; (function (BitcoinAddressType) { diff --git a/local-libs/keyring/cjs/utils/derive-path.js b/local-libs/keyring/cjs/utils/derive-path.js index 7412fefe285..14848b69952 100644 --- a/local-libs/keyring/cjs/utils/derive-path.js +++ b/local-libs/keyring/cjs/utils/derive-path.js @@ -15,7 +15,7 @@ const getEvmDerivePath = index => { return emvPath.replace('{index}', index.toString()); }; exports.getEvmDerivePath = getEvmDerivePath; -const getBitDerivePathFunction = (proposal, slip44) => { +const getBitDerivePathFunction = (slip44, proposal) => { const path = bitPath.replace('{proposal}', proposal.toString()).replace('{slip44}', slip44.toString()); return index => { return path.replace('{index}', index.toString()); @@ -27,17 +27,17 @@ const getDerivePath = type => { case 'ethereum': return getEvmDerivePath; case 'bitcoin-44': - return getBitDerivePathFunction(44, 0); + return getBitDerivePathFunction(0, 44); case 'bitcoin-84': - return getBitDerivePathFunction(84, 0); + return getBitDerivePathFunction(0, 84); case 'bitcoin-86': - return getBitDerivePathFunction(86, 0); + return getBitDerivePathFunction(0, 86); case 'bittest-44': - return getBitDerivePathFunction(44, 1); + return getBitDerivePathFunction(1, 44); case 'bittest-84': - return getBitDerivePathFunction(84, 1); + return getBitDerivePathFunction(1, 84); case 'bittest-86': - return getBitDerivePathFunction(86, 1); + return getBitDerivePathFunction(1, 86); default: return () => ''; } diff --git a/local-libs/keyring/package.json b/local-libs/keyring/package.json index 1767e9b1c39..1690caaf2b2 100644 --- a/local-libs/keyring/package.json +++ b/local-libs/keyring/package.json @@ -205,6 +205,7 @@ "@polkadot/util-crypto": "^12.2.1", "bcryptjs": "^2.4.3", "bignumber.js": "^9.1.2", + "bip322-js": "^2.0.0", "bitcoinjs-lib": "^6.1.5", "bitcoinjs-message": "^2.2.0", "ecpair": "^2.1.0", diff --git a/local-libs/keyring/pair/index.js b/local-libs/keyring/pair/index.js index e41a426a330..ccf880aae23 100644 --- a/local-libs/keyring/pair/index.js +++ b/local-libs/keyring/pair/index.js @@ -1,12 +1,12 @@ // Copyright 2017-2022 @polkadot/keyring authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { Signer } from 'bip322-js'; import * as bitcoin from 'bitcoinjs-lib'; -import { sign as bitcoinSignMessage } from 'bitcoinjs-message'; import { ECPairFactory } from 'ecpair'; import SimpleKeyring from 'eth-simple-keyring'; import * as ecc from 'tiny-secp256k1'; -import { hexAddPrefix, hexStripPrefix, objectSpread, u8aConcat, u8aEmpty, u8aEq, u8aToHex, u8aToU8a } from '@polkadot/util'; +import { hexAddPrefix, hexStripPrefix, objectSpread, u8aConcat, u8aEmpty, u8aEq, u8aToHex, u8aToString, u8aToU8a } from '@polkadot/util'; import { blake2AsU8a, ethereumEncode, hdEthereum, keyExtractPath, mnemonicToLegacySeed, secp256k1Compress, signatureVerify, sr25519VrfSign, sr25519VrfVerify } from '@polkadot/util-crypto'; import { entropyToMnemonic } from '@polkadot/util-crypto/mnemonic/bip39'; import { getDerivePath, keyFromPath, TYPE_ADDRESS, TYPE_FROM_SEED, TYPE_PREFIX, TYPE_SIGNATURE } from "../utils/index.js"; @@ -94,13 +94,13 @@ export function createPair({ const encodeAddress = () => { const raw = TYPE_ADDRESS[type](publicKey); const bitNetwork = ['bitcoin-44', 'bitcoin-84', 'bitcoin-86'].includes(type) ? bitcoin.networks.bitcoin : ['bittest-44', 'bittest-84', 'bittest-86'].includes(type) ? bitcoin.networks.testnet : bitcoin.networks.regtest; + let dataKey; /** * With bitcoin accounts, some attached account have no public key (only address). * In this case, public key is the hash of result after decoded address. * Add `noPublicKey` in metadata for this case. */ - let dataKey; if (meta.noPublicKey) { dataKey = 'hash'; } else { @@ -301,21 +301,24 @@ export function createPair({ type }, derived, meta, null); }, - signMessage: (message, compressed, options) => { + signMessage: message => { if (isLocked(secretKey)) { throw new Error('Cannot encrypt with a locked key pair'); } - const _message = typeof message === 'string' ? message : Buffer.from(message); + const _message = typeof message === 'string' ? message : u8aToString(message); + const address = encodeAddress(); + const _pair = ECPair.fromPrivateKey(Buffer.from(secretKey)); + const wif = _pair.toWIF(); // Sign the message - const signature = bitcoinSignMessage(_message, Buffer.from(secretKey), compressed, options); - return signature.toString('base64'); + const signature = Signer.sign(wif, address, _message); + return typeof signature === 'string' ? signature : signature.toString('base64'); }, - signTransaction: (transaction, indexes) => { + signTransaction: (psbt, indexes, sighashTypes, tapLeafHashToSign) => { if (isLocked(secretKey)) { throw new Error('Cannot encrypt with a locked key pair'); } - if (!transaction) { + if (!psbt) { throw new Error('Not found sign method'); } const pair = ECPair.fromPrivateKey(Buffer.from(secretKey)); @@ -323,12 +326,12 @@ export function createPair({ for (const index of indexes) { if (isTaproot) { const tweakedSigner = pair.tweak(bitcoin.crypto.taggedHash('TapTweak', toXOnly(pair.publicKey))); - transaction.signTaprootInput(index, tweakedSigner); + psbt.signTaprootInput(index, tweakedSigner, tapLeafHashToSign, sighashTypes); } else { - transaction.signInput(index, pair); + psbt.signInput(index, pair, sighashTypes); } } - return transaction; + return psbt; }, get output() { return output || Buffer.from([]); diff --git a/local-libs/keyring/types.d.ts b/local-libs/keyring/types.d.ts index 7797ae55742..53d1b857cf1 100644 --- a/local-libs/keyring/types.d.ts +++ b/local-libs/keyring/types.d.ts @@ -4,7 +4,6 @@ import type { HexString } from '@polkadot/util/types'; import type { EncryptedJson, Keypair, Prefix } from '@polkadot/util-crypto/types'; import { TypedTransaction } from '@ethereumjs/tx'; import { Psbt as BitcoinTransaction } from 'bitcoinjs-lib'; -import { SignatureOptions } from 'bitcoinjs-message'; /** * * @@ -58,8 +57,8 @@ export interface SubstrateSigner { } export interface BitcoinSigner { derive: (index: number, meta?: KeyringPair$Meta) => KeyringPair; - signMessage: (message: HexString | string | Uint8Array, compressed?: boolean, options?: SignatureOptions) => string; - signTransaction: (transaction: BitcoinTransaction, indexes: number[]) => BitcoinTransaction; + signMessage: (message: HexString | string | Uint8Array) => string; + signTransaction: (transaction: BitcoinTransaction, indexes: number[], sighashTypes?: number[], tapLeafHashToSign?: Buffer | undefined) => BitcoinTransaction; output: Buffer; internalPubkey: Buffer; } diff --git a/local-libs/keyring/utils/derive-path.d.ts b/local-libs/keyring/utils/derive-path.d.ts index 06c925adc30..341cf531a0f 100644 --- a/local-libs/keyring/utils/derive-path.d.ts +++ b/local-libs/keyring/utils/derive-path.d.ts @@ -2,5 +2,5 @@ import { KeypairType } from '../types'; export declare const emvPath = "m/44'/60'/0'/0/{index}"; export declare const bitPath = "m/{proposal}'/{slip44}'/{index}'/0/0"; export declare const getEvmDerivePath: (index: number) => string; -export declare const getBitDerivePathFunction: (proposal: number, slip44: number) => (index: number) => string; +export declare const getBitDerivePathFunction: (slip44: number, proposal: number) => (index: number) => string; export declare const getDerivePath: (type: KeypairType) => (index: number) => string; diff --git a/local-libs/keyring/utils/derive-path.js b/local-libs/keyring/utils/derive-path.js index d24cb9e7c18..722c90fa1aa 100644 --- a/local-libs/keyring/utils/derive-path.js +++ b/local-libs/keyring/utils/derive-path.js @@ -6,7 +6,7 @@ export const bitPath = "m/{proposal}'/{slip44}'/{index}'/0/0"; export const getEvmDerivePath = index => { return emvPath.replace('{index}', index.toString()); }; -export const getBitDerivePathFunction = (proposal, slip44) => { +export const getBitDerivePathFunction = (slip44, proposal) => { const path = bitPath.replace('{proposal}', proposal.toString()).replace('{slip44}', slip44.toString()); return index => { return path.replace('{index}', index.toString()); @@ -17,17 +17,17 @@ export const getDerivePath = type => { case 'ethereum': return getEvmDerivePath; case 'bitcoin-44': - return getBitDerivePathFunction(44, 0); + return getBitDerivePathFunction(0, 44); case 'bitcoin-84': - return getBitDerivePathFunction(84, 0); + return getBitDerivePathFunction(0, 84); case 'bitcoin-86': - return getBitDerivePathFunction(86, 0); + return getBitDerivePathFunction(0, 86); case 'bittest-44': - return getBitDerivePathFunction(44, 1); + return getBitDerivePathFunction(1, 44); case 'bittest-84': - return getBitDerivePathFunction(84, 1); + return getBitDerivePathFunction(1, 84); case 'bittest-86': - return getBitDerivePathFunction(86, 1); + return getBitDerivePathFunction(1, 86); default: return () => ''; } diff --git a/yarn.lock b/yarn.lock index 875649e4dce..850031df8ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1839,6 +1839,16 @@ __metadata: languageName: node linkType: hard +"@bitcoinerlab/secp256k1@npm:^1.1.1": + version: 1.1.1 + resolution: "@bitcoinerlab/secp256k1@npm:1.1.1" + dependencies: + "@noble/hashes": ^1.1.5 + "@noble/secp256k1": ^1.7.1 + checksum: 01f23cb05553ceb3783513b9c79b3303289bf55cd694150967b15ce60a35821cad68794ae958088cbe6b8a2b02c9294ebff78fd6b53d6fd78662eeda7bc742f1 + languageName: node + linkType: hard + "@btckit/types@npm:^0.0.19": version: 0.0.19 resolution: "@btckit/types@npm:0.0.19" @@ -3811,7 +3821,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:^1.1.2": +"@noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.1.5": version: 1.4.0 resolution: "@noble/hashes@npm:1.4.0" checksum: 8ba816ae26c90764b8c42493eea383716396096c5f7ba6bea559993194f49d80a73c081f315f4c367e51bd2d5891700bcdfa816b421d24ab45b41cb03e4f3342 @@ -3825,7 +3835,7 @@ __metadata: languageName: node linkType: hard -"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.6.3": +"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:^1.6.3, @noble/secp256k1@npm:^1.7.1": version: 1.7.1 resolution: "@noble/secp256k1@npm:1.7.1" checksum: d2301f1f7690368d8409a3152450458f27e54df47e3f917292de3de82c298770890c2de7c967d237eff9c95b70af485389a9695f73eb05a43e2bd562d18b18cb @@ -6777,13 +6787,14 @@ __metadata: "@subwallet/keyring@file:./local-libs/keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=03559c&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=d3bca5&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@ethereumjs/tx": ^5.0.0 "@polkadot/util": ^12.2.1 "@polkadot/util-crypto": ^12.2.1 bcryptjs: ^2.4.3 bignumber.js: ^9.1.2 + bip322-js: ^2.0.0 bitcoinjs-lib: ^6.1.5 bitcoinjs-message: ^2.2.0 ecpair: ^2.1.0 @@ -6792,7 +6803,7 @@ __metadata: rxjs: ^7.5.6 tiny-secp256k1: ^2.2.3 tslib: ^2.6.2 - checksum: b11f239a161cd67dea77d9af5d757d3fc55825d9b5572530368de58cae58860c79dfba0582258f1c5fc218ee0843f14b713075fdf5578c41ea0741c3a9ae0918 + checksum: 6b932a1451831b3775333f1a588151b8dfb3809b80a9e13fb00b7f6cde1d12ffcb271b02079976ba30124dbe45b505b70db9156ba0cbdaccb47e7c9d4a3225bc languageName: node linkType: hard @@ -6877,7 +6888,7 @@ __metadata: "@subwallet/ui-keyring@file:./local-libs/ui-keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=c7a0de&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=4dea1f&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@babel/runtime": ^7.20.1 "@polkadot/ui-settings": 2.9.14 @@ -6887,7 +6898,7 @@ __metadata: mkdirp: ^1.0.4 rxjs: ^7.5.7 store: ^2.0.12 - checksum: af099907afaac66bdce29812c1d15ed1181bba04167714352f4f3bd8f68a214c8d923f24b7f5e2e5612ad8db97b5eb5c6881a83fd291dd7d3f5f5e5c8a12a1a3 + checksum: ce2e1d12833856411902c2e270538e81d6f43735ba8f0f1a3f11d69043aba6bc9c47a497d03b5436dabd32593f4d9cdcf66f768c291724e706085a20f9090cbf languageName: node linkType: hard @@ -10631,6 +10642,21 @@ __metadata: languageName: node linkType: hard +"bip322-js@npm:^2.0.0": + version: 2.0.0 + resolution: "bip322-js@npm:2.0.0" + dependencies: + "@bitcoinerlab/secp256k1": ^1.1.1 + bitcoinjs-lib: ^6.1.5 + bitcoinjs-message: ^2.2.0 + ecpair: ^2.1.0 + elliptic: ^6.5.5 + fast-sha256: ^1.3.0 + secp256k1: ^5.0.0 + checksum: faaaa457251724513ec0510398bb5620775a29f8accb6bfb316bcfe9feee20f76e48cea945836d8b630dbc423d36fee8dd81399575f3f6ab46541e66c7b9c228 + languageName: node + linkType: hard + "bip32@npm:^4.0.0": version: 4.0.0 resolution: "bip32@npm:4.0.0" @@ -13144,6 +13170,21 @@ __metadata: languageName: node linkType: hard +"elliptic@npm:^6.5.5": + version: 6.5.5 + resolution: "elliptic@npm:6.5.5" + 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 + checksum: ec9105e4469eb3b32b0ee2579756c888ddf3f99d259aa0d65fccb906ee877768aaf8880caae73e3e669c9a4adeb3eb1945703aa974ec5000d2d33a239f4567eb + languageName: node + linkType: hard + "email-addresses@npm:^3.0.1": version: 3.1.0 resolution: "email-addresses@npm:3.1.0" @@ -20746,6 +20787,15 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: latest + checksum: 2508bd2d2981945406243a7bd31362fc7af8b70b8b4d65f869c61731800058fb818cc2fd36c8eac714ddd0e568cc85becf5e165cebbdf7b5024d5151bbc75ea1 + languageName: node + linkType: hard + "node-addon-api@npm:^6.0.0": version: 6.1.0 resolution: "node-addon-api@npm:6.1.0" @@ -24648,6 +24698,18 @@ __metadata: languageName: node linkType: hard +"secp256k1@npm:^5.0.0": + version: 5.0.0 + resolution: "secp256k1@npm:5.0.0" + dependencies: + elliptic: ^6.5.4 + node-addon-api: ^5.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + checksum: a0719dff4687c38d385b5e0b7e811c51a4ea24893128be9d097aee99f879eb0ea52582590deb15a49da627a3db23c6b028ad5c9c6ac1fca92ce760153b8cf21c + languageName: node + linkType: hard + "select-hose@npm:^2.0.0": version: 2.0.0 resolution: "select-hose@npm:2.0.0" From 0a1b897ef561334bf63ecc98772db85247aa4ae6 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Sat, 22 Jun 2024 11:04:18 +0700 Subject: [PATCH 17/43] update: Bip-322 standard sign message for bitcoin provider --- packages/extension-base/src/background/KoniTypes.ts | 1 + .../extension-base/src/koni/background/handlers/Tabs.ts | 5 +++-- .../request-service/handler/BitcoinRequestHandler.ts | 6 ++++-- yarn.lock | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 10ff1a8518a..56262837c95 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1380,6 +1380,7 @@ export interface ConfirmationsQueueItemOptions { export interface SignMessageBitcoinResult { signature: string; + message: string; address: string; } diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index eb87913e360..dfb454a9b72 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -39,6 +39,7 @@ import { checkIfDenied } from '@polkadot/phishing'; import { JsonRpcResponse } from '@polkadot/rpc-provider/types'; import { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; import { assert, hexStripPrefix, isNumber, u8aToHex } from '@polkadot/util'; +import { isEthereumAddress } from '@polkadot/util-crypto'; interface AccountSub { subscription: Subscription; @@ -74,7 +75,7 @@ function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: .filter(({ json: { meta: { isHidden } } }) => !isHidden) .filter(({ type }) => anyType ? true : canDerive(type)) .filter(authTypeFilter) - .filter(({ json: { address } }) => accountSelected.includes(address)) + .filter(({ json: { address } }) => accountSelected.includes(address) && (accountAuthType !== 'bitcoin' || !isEthereumAddress(address))) .sort((a, b) => (a.json.meta.whenCreated || 0) - (b.json.meta.whenCreated || 0)) .map(({ json: { address, meta: { genesisHash, name } }, type }): InjectedAccount => ({ address, @@ -1137,7 +1138,7 @@ export default class KoniTabs { return { result: { - addresses: getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])) + addresses: getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])).filter(({ address }) => !isEthereumAddress(address)) } }; } catch (e) { diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index 9973b75c908..939aec36f89 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -153,7 +153,8 @@ export default class BitcoinRequestHandler { if (typeof payload === 'string') { // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface return { - signature: pair.bitcoin.signMessage(payload, false), + signature: pair.bitcoin.signMessage(payload), + message: payload, address }; // Assuming compressed = false } else if (payload instanceof Uint8Array) { // Check if payload is a byte array (Uint8Array) @@ -162,7 +163,8 @@ export default class BitcoinRequestHandler { // Assume BitcoinSigner is an instance that implements the BitcoinSigner interface return { - signature: pair.bitcoin.signMessage(payloadString, false), + signature: pair.bitcoin.signMessage(payloadString), + message: payload.toString(), address }; // Assuming compressed = false } else { diff --git a/yarn.lock b/yarn.lock index 850031df8ad..9f7a174cf62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6787,7 +6787,7 @@ __metadata: "@subwallet/keyring@file:./local-libs/keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=d3bca5&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=97ee3e&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@ethereumjs/tx": ^5.0.0 "@polkadot/util": ^12.2.1 @@ -6803,7 +6803,7 @@ __metadata: rxjs: ^7.5.6 tiny-secp256k1: ^2.2.3 tslib: ^2.6.2 - checksum: 6b932a1451831b3775333f1a588151b8dfb3809b80a9e13fb00b7f6cde1d12ffcb271b02079976ba30124dbe45b505b70db9156ba0cbdaccb47e7c9d4a3225bc + checksum: 82d12c105f7a1901723e569440f522de6d5340606178c0bbb73b7e8b51a22fca676a225cae0fac37f3317c816c1fb31abb4249a6f50672bb8e204d9d78691e22 languageName: node linkType: hard From cef32ccef3b1b6d5097fb7dc3eac2189f13e3995 Mon Sep 17 00:00:00 2001 From: lw Date: Sat, 22 Jun 2024 12:37:36 +0700 Subject: [PATCH 18/43] Highlight current account proxy on connect dApp modal --- .../Layout/parts/ConnectWebsiteModal.tsx | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx b/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx index 9bb461c888a..272731ce573 100644 --- a/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx +++ b/packages/extension-koni-ui/src/components/Layout/parts/ConnectWebsiteModal.tsx @@ -264,11 +264,14 @@ function Component ({ authInfo, className = '', id, isBlocked = true, isNotConne { list.map((item) => { const isSelected = item.accounts.some((a) => allowedMap[a.address]); + const isCurrent = item.proxyId === currentAccountProxy?.proxyId; return ( (({ theme: { token } marginTop: token.margin }, + '.account-proxy-item': { + position: 'relative', + cursor: 'pointer', + + '&:before': { + content: '""', + position: 'absolute', + inset: 0, + display: 'block', + border: '2px solid transparent', + borderRadius: token.borderRadiusLG + }, + + '&.-is-current:before': { + borderColor: token.colorPrimary + } + }, + '.account-proxy-item + .account-proxy-item': { marginTop: token.marginSM }, From df8ce1cd3f1f6247b6c9a1f260420a7ff8c96aee Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Sat, 22 Jun 2024 12:40:52 +0700 Subject: [PATCH 19/43] update: sign Psbt for bitcoin provider --- .../src/background/KoniTypes.ts | 22 +++++--- .../src/koni/background/handlers/State.ts | 56 +++++++++++-------- .../src/koni/background/handlers/Tabs.ts | 2 +- .../handler/BitcoinRequestHandler.ts | 50 +++++++---------- .../src/Popup/Confirmations/index.tsx | 2 +- .../Confirmations/parts/Sign/Bitcoin.tsx | 6 +- .../variants/BitcoinSignPsbtConfirmation.tsx | 21 ++++--- 7 files changed, 83 insertions(+), 76 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index 56262837c95..05e75021cdb 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1332,18 +1332,27 @@ export interface BitcoinSignRequest { canSign: boolean; } -export interface BitcoinSignPsbtPayload { +export interface BitcoinSignPsbtPayload extends Omit{ txInput: PsbtTxInput[]; - signingIndexes: Record txOutput: PsbtTxOutput[]; - broadcast: boolean, psbt: Psbt } +enum SignatureHash { + DEFAULT = 0, + ALL = 1, + NONE = 2, + SINGLE = 3, + ANYONECANPAY = 128 +} + export interface BitcoinSignPsbtRawRequest { psbt: string; - signInputs: Record; - broadcast: boolean; + allowedSighash ?: SignatureHash[]; + signAtIndex?: number | number[]; + broadcast?: boolean; + network: 'mainnet' | 'testnet'; + account: string; } export interface EvmSignatureRequest extends EvmSignRequest { @@ -1367,8 +1376,7 @@ export type BitcoinSendTransactionRequest = BitcoinSignRequest export type EvmWatchTransactionRequest = EvmSendTransactionRequest; export type BitcoinWatchTransactionRequest = BitcoinSendTransactionRequest; -export type BitcoinSignPsbtRequest = Omit & { - accounts: AccountJson[]; +export type BitcoinSignPsbtRequest = BitcoinSendTransactionRequest & { payload: BitcoinSignPsbtPayload; }; diff --git a/packages/extension-base/src/koni/background/handlers/State.ts b/packages/extension-base/src/koni/background/handlers/State.ts index feb14080449..35cbb5112a6 100644 --- a/packages/extension-base/src/koni/background/handlers/State.ts +++ b/packages/extension-base/src/koni/background/handlers/State.ts @@ -57,7 +57,7 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { TransactionConfig } from 'web3-core'; import { JsonRpcResponse, ProviderInterface, ProviderInterfaceCallback } from '@polkadot/rpc-provider/types'; -import { assert, hexStripPrefix, hexToU8a, isHex, logger as createLogger, u8aToHex } from '@polkadot/util'; +import { assert, hexStripPrefix, hexToU8a, isArray, isHex, logger as createLogger, u8aToHex } from '@polkadot/util'; import { Logger } from '@polkadot/util/types'; import { base64Decode, isEthereumAddress, keyExtractSuri } from '@polkadot/util-crypto'; @@ -1223,9 +1223,9 @@ export default class KoniState { } public async bitcoinSignPspt (id: string, url: string, method: string, params: BitcoinSignPsbtRawRequest, allowedAccounts: string[]): Promise { - const { broadcast, psbt, signInputs } = params; + const { account: address, allowedSighash, broadcast, network, psbt, signAtIndex } = params; - if (!psbt || !signInputs) { + if (!psbt || !address) { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found payload to sign')); } @@ -1233,30 +1233,36 @@ export default class KoniState { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Psbt to be signed must be hex-encoded')); } - let canSign = true; + if (!(network === 'mainnet' || network === 'testnet')) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Network to try this request is must be mainnet or testnet')); + } - const accountListJson = Object.keys(signInputs).reduce((accountList, address) => { - if (!isBitcoinAddress(address)) { - return accountList; - } + if (!isBitcoinAddress(address)) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found address')); + } - // Check sign abiblity - if (!allowedAccounts.find((acc) => (acc.toLowerCase() === address.toLowerCase()))) { - return accountList; - } + // Check sign abiblity + if (!allowedAccounts.find((acc) => (acc.toLowerCase() === address.toLowerCase()))) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('You have rescinded allowance for this account in wallet')); + } - const pair = keyring.getPair(address); + const pair = keyring.getPair(address); - if (!pair) { - return accountList; - } + if (!pair) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Unable to find account')); + } - if (pair.meta && pair.meta.isExternal) { - canSign = false; + if (network === 'mainnet') { + if (!['bitcoin-86', 'bitcoin-84'].includes(pair.type)) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Your address is not on the mainnet network')); } + } else if (network === 'testnet') { + if (!['bittest-86', 'bittest-84'].includes(pair.type)) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Your address is not on the testnet network')); + } + } - return [...accountList, { address: pair.address, ...pair.meta }]; - }, [] as AccountJson[]); + const account: AccountJson = { address: pair.address, ...pair.meta }; const psbtGenerate = bitcoin.Psbt.fromHex(psbt); const psbtTxInputs = psbtGenerate.txInputs; @@ -1264,15 +1270,19 @@ export default class KoniState { const payload: BitcoinSignPsbtPayload = { psbt: psbtGenerate, - broadcast, - signingIndexes: signInputs, + broadcast: !!broadcast, + network, + signAtIndex: isArray(signAtIndex) && signAtIndex.length === 0 ? undefined : signAtIndex, + account: account.address, + allowedSighash, txInput: psbtTxInputs, txOutput: psbtTxOutputs }; const hashPayload = ''; + const canSign = !account.isExternal; const signPayload: BitcoinSignPsbtRequest = { - accounts: accountListJson, + account, payload, hashPayload, canSign diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index dfb454a9b72..b7b31c2748b 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -75,7 +75,7 @@ function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: .filter(({ json: { meta: { isHidden } } }) => !isHidden) .filter(({ type }) => anyType ? true : canDerive(type)) .filter(authTypeFilter) - .filter(({ json: { address } }) => accountSelected.includes(address) && (accountAuthType !== 'bitcoin' || !isEthereumAddress(address))) + .filter(({ json: { address } }) => accountSelected.includes(address)) .sort((a, b) => (a.json.meta.whenCreated || 0) - (b.json.meta.whenCreated || 0)) .map(({ json: { address, meta: { genesisHash, name } }, type }): InjectedAccount => ({ address, diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index 939aec36f89..36a6a568057 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -7,13 +7,12 @@ import { ConfirmationRequestBase, Resolver } from '@subwallet/extension-base/bac import { ChainService } from '@subwallet/extension-base/services/chain-service'; import RequestService from '@subwallet/extension-base/services/request-service'; import { isInternalRequest } from '@subwallet/extension-base/utils/request'; -import { getKeypairTypeByAddress } from '@subwallet/keyring'; import keyring from '@subwallet/ui-keyring'; import { Psbt } from 'bitcoinjs-lib'; import { t } from 'i18next'; import { BehaviorSubject } from 'rxjs'; -import { logger as createLogger } from '@polkadot/util'; +import { isArray, logger as createLogger } from '@polkadot/util'; import { Logger } from '@polkadot/util/types'; export default class BitcoinRequestHandler { @@ -200,54 +199,47 @@ export default class BitcoinRequestHandler { private async signPsbt (request: ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]): Promise { // Extract necessary information from the BitcoinSendTransactionRequest - const { accounts, payload } = request.payload; - const { broadcast, psbt, signingIndexes } = payload; + const { account, payload } = request.payload; + const { allowedSighash, broadcast, network, psbt, signAtIndex } = payload; // todo: validate type of the account - if (accounts.length === 0) { + if (Object.keys(account).length === 0) { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, 'Please connect to Wallet to try this request'); } - const psbtList = accounts.map(({ address }) => { - const pair = keyring.getPair(address); + const pair = keyring.getPair(account.address); - // Unlock the pair if it is locked - if (pair.isLocked) { - keyring.unlockPair(pair.address); - } - - // Sign the Psbt using the pair's bitcoin object - const signedTransaction = pair.bitcoin.signTransaction(psbt, signingIndexes[address]); + // Unlock the pair if it is locked + if (pair.isLocked) { + keyring.unlockPair(pair.address); + } - return signedTransaction.finalizeAllInputs(); - }); + const signAtIndexGenerate = signAtIndex ? (isArray(signAtIndex) ? signAtIndex : [signAtIndex]) : [psbt.inputCount]; - const psbtCombine = psbtList[0].combine(...psbtList); + // Sign the Psbt using the pair's bitcoin object + const psptSignedTransaction = pair.bitcoin.signTransaction(psbt, signAtIndexGenerate, allowedSighash); if (!broadcast) { + for (const index of signAtIndexGenerate) { + psptSignedTransaction.finalizeInput(index); + } + return { - psbt: psbtCombine.toHex() + psbt: psptSignedTransaction.toHex() }; } - const addressType = getKeypairTypeByAddress(accounts[0].address); - - // todo: this is hotfix, will update logic to get chain value later - const chain = (() => { - if (['bittest-86', 'bittest-84'].includes(addressType)) { - return 'bitcoinTestnet'; - } + psptSignedTransaction.finalizeAllInputs(); - return 'bitcoin'; - })(); + const chain = network === 'mainnet' ? 'bitcoin' : 'bitcoinTestnet'; - const txid = await this.#chainService.getBitcoinApi(chain).api.simpleSendRawTransaction(psbt.extractTransaction().toHex()); + const txid = await this.#chainService.getBitcoinApi(chain).api.simpleSendRawTransaction(psptSignedTransaction.extractTransaction().toHex()); console.log('TXID', txid); return { - psbt: psbtCombine.toHex(), + psbt: psptSignedTransaction.toHex(), txid }; } diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index 736a630d0c3..a1263be240e 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -94,7 +94,7 @@ const Component = function ({ className }: Props) { account = request.payload.account; canSign = request.payload.canSign; isMessage = confirmation.type === 'evmSignatureRequest'; - } else if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest'].includes(confirmation.type)) { + } else if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest', 'bitcoinSignPsbtRequest'].includes(confirmation.type)) { const request = confirmation.item as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest' | 'bitcoinSendTransactionRequest' | 'bitcoinWatchTransactionRequest'][0]; account = request.payload.account; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx index 9cd664be620..c5b4cc6fa50 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-koni-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { BitcoinSignatureRequest, BitcoinSignPsbtRequest, ConfirmationDefinitionsBitcoin, ConfirmationResult, EvmSendTransactionRequest, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; +import { BitcoinSignatureRequest, ConfirmationDefinitionsBitcoin, ConfirmationResult, EvmSendTransactionRequest, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; import { CONFIRMATION_QR_MODAL } from '@subwallet/extension-koni-ui/constants'; import { useGetChainInfoByChainId, useLedger, useNotification, useUnlockChecker } from '@subwallet/extension-koni-ui/hooks'; import { completeConfirmationBitcoin } from '@subwallet/extension-koni-ui/messaging'; @@ -52,8 +52,6 @@ const Component: React.FC = (props: Props) => { const { className, extrinsicType, id, payload, type } = props; const { payload: { canSign, hashPayload } } = payload; const account = (payload.payload as BitcoinSignatureRequest).account; - const accounts = (payload.payload as BitcoinSignPsbtRequest).accounts; - // const isModeAllAccount = accounts && accounts.length > 0 && !account; const chainId = (payload.payload as EvmSendTransactionRequest)?.chainId || 1; const { t } = useTranslation(); @@ -64,7 +62,7 @@ const Component: React.FC = (props: Props) => { const chain = useGetChainInfoByChainId(chainId); const checkUnlock = useUnlockChecker(); - const signMode = useMemo(() => getSignMode(account || accounts[0]), [account, accounts]); + const signMode = useMemo(() => getSignMode(account), [account]); const isLedger = useMemo(() => signMode === AccountSignMode.LEDGER, [signMode]); const isMessage = isBitcoinMessage(payload); diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx index 65cba51ee0d..070fe49b7b5 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSignPsbtConfirmation.tsx @@ -22,7 +22,7 @@ interface Props extends ThemeProps { function Component ({ className, request, type }: Props) { const { id, payload } = request; const { t } = useTranslation(); - const { accounts } = payload; + const { account } = payload; const onClickDetail = useOpenDetailModal(); @@ -36,16 +36,15 @@ function Component ({ className, request, type }: Props) {
{t('You are approving a request with the following account')}
- {accounts.map((account) => - )} +
+
+
+ + + {/* */} + + + ); +} + +const BitcoinTransactionConfirmation = styled(Component)(({ theme: { token } }: ThemeProps) => ({ + '.account-list': { + '.__prop-label': { + marginRight: token.marginMD, + width: '50%', + float: 'left' + } + }, + + '.network-box': { + marginTop: token.margin + }, + + '.to-account': { + marginTop: token.margin - 2 + }, + + '.__label': { + textAlign: 'left' + } +})); + +export default BitcoinTransactionConfirmation; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts index cedca5f8ff1..fb09094f997 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts @@ -14,3 +14,4 @@ export { default as TransactionConfirmation } from './Transaction'; export { default as NotSupportWCConfirmation } from './NotSupportWCConfirmation'; export { default as BitcoinSignatureConfirmation } from './BitcoinSignatureConfirmation'; export { default as BitcoinSignPsbtConfirmation } from './BitcoinSignPsbtConfirmation'; +export { default as BitcoinTransactionConfirmation } from './BitcoinTransactionConfirmation'; From 5ba0ecbf07f91f43a6b0164d8d6cb4091ee92438 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Mon, 24 Jun 2024 18:36:08 +0700 Subject: [PATCH 27/43] update: send Transfer for bitcoin provider #7 --- .../src/koni/background/handlers/Tabs.ts | 22 ++++++++++++++++++- .../handler/BitcoinRequestHandler.ts | 1 - 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index e23eb530d1f..d3d93bd797e 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -1169,9 +1169,29 @@ export default class KoniTabs { }; } + const { proxyId: currentAccountProxy } = this.#koniState.keyringService.currentAccountProxy; + + const addressesAllowed = + getAuthAddresses(Object.keys(authInfo.isAllowedMap) + .filter((k) => authInfo.isAllowedMap[k])) + .filter(({ address }) => !isEthereumAddress(address)) + .reduce((listSorted, account) => { + const pair = keyring.getPair(account.address); + + console.log(pair.meta.proxyId, currentAccountProxy, account.address); + + if (pair.meta.proxyId === currentAccountProxy) { + listSorted.unshift(account); + } else { + listSorted.push(account); + } + + return listSorted; + }, [] as AuthAddress[]); + return { result: { - addresses: getAuthAddresses(Object.keys(authInfo.isAllowedMap).filter((k) => authInfo.isAllowedMap[k])).filter(({ address }) => !isEthereumAddress(address)) + addresses: addressesAllowed } }; } catch (e) { diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index 100c5900946..b8e1c4277ef 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -217,7 +217,6 @@ export default class BitcoinRequestHandler { const chainInfo = this.#chainService.getChainInfoByKey(chain); const bitcoinApi = this.#chainService.getBitcoinApi(chain); - console.log('bitcoinApi', bitcoinApi, transactionObj); const network = chainInfo.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; const getChainFee: GetFeeFunction = (id, chain, type) => { From 4817d1cab87210020285c83b4f88ac4c59153bb2 Mon Sep 17 00:00:00 2001 From: lw Date: Wed, 26 Jun 2024 09:11:00 +0700 Subject: [PATCH 28/43] Update sendTransfer confirmation --- .../src/background/KoniTypes.ts | 2 +- .../src/koni/background/handlers/State.ts | 2 +- .../src/koni/background/handlers/Tabs.ts | 28 ++ .../src/Popup/Confirmations/index.tsx | 4 +- .../Confirmations/parts/Sign/Bitcoin.tsx | 7 +- ...coinSendTransactionRequestConfirmation.tsx | 289 ++++++++++++++++++ .../BitcoinTransactionConfirmation.tsx | 147 --------- .../src/Popup/Confirmations/variants/index.ts | 2 +- .../BitcoinFeeSelector/index.tsx | 119 +++++--- 9 files changed, 401 insertions(+), 199 deletions(-) create mode 100644 packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSendTransactionRequestConfirmation.tsx delete mode 100644 packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinTransactionConfirmation.tsx diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index cb229069c7b..ced27a36753 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -7,6 +7,7 @@ import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _FundStatus, _MultiChai import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; import { AuthUrls, Resolver } from '@subwallet/extension-base/background/handlers/State'; import { AccountAuthType, AccountJson, AccountProxy, AddressJson, AuthorizeRequest, ConfirmationRequestBase, RequestAccountList, RequestAccountProxy, RequestAccountSubscribe, RequestAccountUnsubscribe, RequestAuthorizeCancel, RequestAuthorizeReject, RequestAuthorizeSubscribe, RequestAuthorizeTab, RequestCurrentAccountAddress, ResponseAuthorizeList, ResponseJsonGetAccountInfo, SeedLengths } from '@subwallet/extension-base/background/types'; +import { BitcoinApiStrategy } from '@subwallet/extension-base/services/chain-service/handler/bitcoin/strategy/types'; import { _CHAIN_VALIDATION_ERROR } from '@subwallet/extension-base/services/chain-service/handler/types'; import { _BitcoinApi, _ChainState, _EvmApi, _NetworkUpsertParams, _SubstrateApi, _ValidateCustomAssetRequest, _ValidateCustomAssetResponse, _ValidateCustomBrc20Request, _ValidateCustomBrc20Response, _ValidateCustomRuneRequest, _ValidateCustomRuneResponse, EnableChainParams, EnableMultiChainParams } from '@subwallet/extension-base/services/chain-service/types'; import { CrowdloanContributionsResponse } from '@subwallet/extension-base/services/subscan-service/types'; @@ -28,7 +29,6 @@ import { JsonRpcPayload, JsonRpcResponse } from 'web3-core-helpers'; import { SignerResult } from '@polkadot/types/types/extrinsic'; import { HexString } from '@polkadot/util/types'; -import { BitcoinApiStrategy } from '../../build/services/chain-service/handler/bitcoin/strategy/types'; import { TransactionWarning } from './warnings/TransactionWarning'; export enum RuntimeEnvironment { diff --git a/packages/extension-base/src/koni/background/handlers/State.ts b/packages/extension-base/src/koni/background/handlers/State.ts index de8d60bcf2d..9bf811f60ea 100644 --- a/packages/extension-base/src/koni/background/handlers/State.ts +++ b/packages/extension-base/src/koni/background/handlers/State.ts @@ -1343,7 +1343,7 @@ export default class KoniState { from: transactionParams.account, to: transactionParams.recipients[0].address, value: autoFormatNumber(transactionParams.recipients[0].amount), - networkKey: transactionParams.network + networkKey: transactionParams.network === 'testnet' ? 'bitcoinTestnet' : 'bitcoin' }; // Address is validated in before step diff --git a/packages/extension-base/src/koni/background/handlers/Tabs.ts b/packages/extension-base/src/koni/background/handlers/Tabs.ts index d3d93bd797e..5c11087758e 100644 --- a/packages/extension-base/src/koni/background/handlers/Tabs.ts +++ b/packages/extension-base/src/koni/background/handlers/Tabs.ts @@ -1264,6 +1264,34 @@ export default class KoniTabs { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Network unavailable. Please switch network or manually add network to wallet')); } + const senderAccountType = getKeypairTypeByAddress(transactionParams.account); + + if ((transactionParams.network === 'mainnet' && senderAccountType !== 'bitcoin-84') || (transactionParams.network === 'testnet' && senderAccountType !== 'bittest-84')) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('The account or the network is incorrect')); + } + + if (!networkKey) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Network unavailable. Please switch network or manually add network to wallet')); + } + + if (!transactionParams.recipients?.length) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Please provide the recipient and the amount')); + } + + if (transactionParams.recipients?.length > 1) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t("We don't support multiple recipients yet. Please provide only one for now.")); + } + + if (transactionParams.account === transactionParams.recipients[0].address) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t("The recipient address cannot be the same as the sender's")); + } + + const recipientAccountType = getKeypairTypeByAddress(transactionParams.recipients[0].address); + + if (senderAccountType !== recipientAccountType) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t("The recipient address type must be the same as the sender's")); + } + const allowedAccounts = await this.getBitcoinCurrentAccount(url); const transactionHash = await this.#koniState.bitcoinSendTransaction(id, url, networkKey, allowedAccounts, transactionParams); diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index 5210fed73ab..dd65830e722 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -18,7 +18,7 @@ import styled from 'styled-components'; import { SignerPayloadJSON } from '@polkadot/types/types'; import { ConfirmationHeader } from './parts'; -import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, BitcoinSignatureConfirmation, BitcoinSignPsbtConfirmation, BitcoinTransactionConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; +import { AddNetworkConfirmation, AddTokenConfirmation, AuthorizeConfirmation, BitcoinSendTransactionRequestConfirmation, BitcoinSignatureConfirmation, BitcoinSignPsbtConfirmation, ConnectWalletConnectConfirmation, EvmSignatureConfirmation, EvmTransactionConfirmation, MetadataConfirmation, NotSupportConfirmation, NotSupportWCConfirmation, SignConfirmation, TransactionConfirmation } from './variants'; type Props = ThemeProps @@ -159,7 +159,7 @@ const Component = function ({ className }: Props) { ); case 'bitcoinSendTransactionRequest': return ( - diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx index 076d90ed1cc..99600a4c8c2 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx @@ -25,6 +25,7 @@ interface Props extends ThemeProps { payload: ConfirmationDefinitionsBitcoin[BitcoinSignatureSupportType][0]; extrinsicType?: ExtrinsicType; editedPayload?: SWTransactionResult; + canSign?: boolean; } const handleConfirm = async (type: BitcoinSignatureSupportType, id: string, payload: string) => { @@ -51,8 +52,8 @@ const handleSignature = async (type: BitcoinSignatureSupportType, id: string, si }; const Component: React.FC = (props: Props) => { - const { className, editedPayload, extrinsicType, id, payload, type } = props; - const { payload: { canSign, hashPayload } } = payload; + const { canSign, className, editedPayload, extrinsicType, id, payload, type } = props; + const { payload: { hashPayload } } = payload; const account = (payload.payload as BitcoinSignatureRequest).account; const chainId = (payload.payload as EvmSendTransactionRequest)?.chainId || 1; @@ -252,7 +253,7 @@ const Component: React.FC = (props: Props) => { {t('Cancel')} - - - - - {/* */} - - - ); -} - -const BitcoinTransactionConfirmation = styled(Component)(({ theme: { token } }: ThemeProps) => ({ - '.account-list': { - '.__prop-label': { - marginRight: token.marginMD, - width: '50%', - float: 'left' - } - }, - - '.network-box': { - marginTop: token.margin - }, - - '.to-account': { - marginTop: token.margin - 2 - }, - - '.__label': { - textAlign: 'left' - } -})); - -export default BitcoinTransactionConfirmation; diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts index fb09094f997..0e87a4aa777 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/index.ts @@ -14,4 +14,4 @@ export { default as TransactionConfirmation } from './Transaction'; export { default as NotSupportWCConfirmation } from './NotSupportWCConfirmation'; export { default as BitcoinSignatureConfirmation } from './BitcoinSignatureConfirmation'; export { default as BitcoinSignPsbtConfirmation } from './BitcoinSignPsbtConfirmation'; -export { default as BitcoinTransactionConfirmation } from './BitcoinTransactionConfirmation'; +export { default as BitcoinSendTransactionRequestConfirmation } from './BitcoinSendTransactionRequestConfirmation'; diff --git a/packages/extension-koni-ui/src/components/Field/TransactionFee/BitcoinFeeSelector/index.tsx b/packages/extension-koni-ui/src/components/Field/TransactionFee/BitcoinFeeSelector/index.tsx index 5f22dcb5448..8443b85cf11 100644 --- a/packages/extension-koni-ui/src/components/Field/TransactionFee/BitcoinFeeSelector/index.tsx +++ b/packages/extension-koni-ui/src/components/Field/TransactionFee/BitcoinFeeSelector/index.tsx @@ -16,18 +16,31 @@ import styled from 'styled-components'; import { BitcoinFeeEditorModal } from './BitcoinFeeEditorModal'; +export type RenderFieldNodeParams = { + isLoading: boolean; + feeInfo: { + decimals: number, + symbol: string, + value: BigN, + convertedValue: BigN + }, + disableEdit: boolean, + onClickEdit: VoidFunction +} + type Props = ThemeProps & { feeDetail: BitcoinFeeDetail | undefined; onSelect: (transactionFee: TransactionFee) => void; isLoading?: boolean; tokenSlug: string; resetTrigger?: unknown; + renderFieldNode?: (params: RenderFieldNodeParams) => React.ReactNode; }; // todo: will update dynamic later const modalId = 'BitcoinFeeSelectorId'; -const Component = ({ className, feeDetail, isLoading, onSelect, resetTrigger, tokenSlug }: Props): React.ReactElement => { +const Component = ({ className, feeDetail, isLoading, onSelect, renderFieldNode, resetTrigger, tokenSlug }: Props): React.ReactElement => { const { t } = useTranslation(); const { activeModal } = useContext(ModalContext); const assetRegistry = useSelector((root) => root.assetRegistry.assetRegistry); @@ -79,7 +92,7 @@ const Component = ({ className, feeDetail, isLoading, onSelect, resetTrigger, to const feeValue = useMemo(() => { if (!selectedOption || !feeDetail) { - return '0'; + return BN_ZERO; } if (selectedOption.option === 'custom') { @@ -106,52 +119,70 @@ const Component = ({ className, feeDetail, isLoading, onSelect, resetTrigger, to return ( <> - - {isLoading || !feeDetail || !selectedOption - ? ( - - ) - : ( + { + renderFieldNode?.({ + isLoading: isLoading || !feeDetail || !selectedOption, + feeInfo: { + decimals, + symbol, + value: feeValue, + convertedValue: tokenFeePriceValue + }, + disableEdit: isLoading || !feeDetail || !selectedOption, + onClickEdit + }) + } + + { + !renderFieldNode && ( + + {isLoading || !feeDetail || !selectedOption + ? ( + + ) + : ( + + )} + + )} + label={'Transaction fee'} + placeholder={t('Network name')} + suffix={( +
- )} -
- )} - label={'Transaction fee'} - placeholder={t('Network name')} - suffix={( -
- -
- )} - tooltipPlacement='topLeft' - /> + + )} + tooltipPlacement='topLeft' + /> + ) + } { feeDetail && selectedOption && ( From 62aaf974ef669a7b8f8d27b84770ea9385b14392 Mon Sep 17 00:00:00 2001 From: nguyentiendung Date: Wed, 26 Jun 2024 10:19:08 +0700 Subject: [PATCH 29/43] Update yarn.lock --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index c24cbdee688..dd93dd6cb5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6787,7 +6787,7 @@ __metadata: "@subwallet/keyring@file:./local-libs/keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=2b0c43&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=41584c&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@ethereumjs/tx": ^5.0.0 "@polkadot/util": ^12.2.1 @@ -6803,7 +6803,7 @@ __metadata: rxjs: ^7.5.6 tiny-secp256k1: ^2.2.3 tslib: ^2.6.2 - checksum: 1fc7dcc1ff1dc5415a5bdb703678e73368c3912979b8cbd558a150b0e2a6385c8baccd03189f045c3b1affd0e9ca3f08711e9478d7fc824cb922a712f0d8c3cb + checksum: 2a08cc156184ed60a2b2c3da9e181075520f35fd9bb30810ad573f5be6957919b6d8103dd60b64b556f73958e1128d2855ddde1499be719d1b590f6f312841e9 languageName: node linkType: hard @@ -6966,7 +6966,7 @@ __metadata: "@subwallet/ui-keyring@file:./local-libs/ui-keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=4dea1f&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=c7a0de&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@babel/runtime": ^7.20.1 "@polkadot/ui-settings": 2.9.14 @@ -6976,7 +6976,7 @@ __metadata: mkdirp: ^1.0.4 rxjs: ^7.5.7 store: ^2.0.12 - checksum: ce2e1d12833856411902c2e270538e81d6f43735ba8f0f1a3f11d69043aba6bc9c47a497d03b5436dabd32593f4d9cdcf66f768c291724e706085a20f9090cbf + checksum: af099907afaac66bdce29812c1d15ed1181bba04167714352f4f3bd8f68a214c8d923f24b7f5e2e5612ad8db97b5eb5c6881a83fd291dd7d3f5f5e5c8a12a1a3 languageName: node linkType: hard From d16cce0deca8149aeca1b016438844d1f6d2ead9 Mon Sep 17 00:00:00 2001 From: lw Date: Wed, 26 Jun 2024 10:26:05 +0700 Subject: [PATCH 30/43] Fix eslint issues --- .../extension-base/src/page/bitcoin/OpenBitProvider.ts | 6 +----- packages/extension-base/src/page/substrate/Injected.ts | 3 ++- packages/extension-base/src/page/substrate/Metadata.ts | 2 +- .../src/page/substrate/PostMessageProvider.ts | 2 +- packages/extension-base/src/page/substrate/Signer.ts | 3 ++- .../request-service/handler/AuthRequestHandler.ts | 2 +- packages/extension-inject/src/bundle.ts | 10 +++------- packages/extension-inject/src/types.ts | 7 +++++-- packages/extension-koni/src/page.ts | 4 ++-- 9 files changed, 18 insertions(+), 21 deletions(-) diff --git a/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts b/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts index 5ffffa64fa9..76c30068358 100644 --- a/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts +++ b/packages/extension-base/src/page/bitcoin/OpenBitProvider.ts @@ -1,16 +1,12 @@ // Copyright 2019-2022 @subwallet/extension authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { StacksProvider } from '@stacks/connect'; import { BitcoinProviderError } from '@subwallet/extension-base/background/errors/BitcoinProviderError'; import { version } from '@subwallet/extension-base/page/params'; +import { OpenBitProviderType } from '@subwallet/extension-inject/types'; import { sendMessage } from '../message'; -export interface OpenBitProviderType extends StacksProvider { - isOpenBit: boolean; -} - export const OpenBitProvider: OpenBitProviderType = { isOpenBit: true, getURL: () => { diff --git a/packages/extension-base/src/page/substrate/Injected.ts b/packages/extension-base/src/page/substrate/Injected.ts index 0779db4addc..e8bb8615567 100644 --- a/packages/extension-base/src/page/substrate/Injected.ts +++ b/packages/extension-base/src/page/substrate/Injected.ts @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import type { Injected } from '@subwallet/extension-inject/types'; -import type { SendRequest } from './types'; + +import { SendRequest } from '@subwallet/extension-base/page/types'; import Accounts from './Accounts'; import Metadata from './Metadata'; diff --git a/packages/extension-base/src/page/substrate/Metadata.ts b/packages/extension-base/src/page/substrate/Metadata.ts index 8cf0ac5e66a..52af76d37ee 100644 --- a/packages/extension-base/src/page/substrate/Metadata.ts +++ b/packages/extension-base/src/page/substrate/Metadata.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import type { InjectedMetadata, InjectedMetadataKnown, MetadataDef } from '@subwallet/extension-inject/types'; -import type { SendRequest } from './types'; import { RequestAddPspToken } from '@subwallet/extension-base/background/KoniTypes'; +import { SendRequest } from '@subwallet/extension-base/page/types'; // External to class, this.# is not private enough (yet) let sendRequest: SendRequest; diff --git a/packages/extension-base/src/page/substrate/PostMessageProvider.ts b/packages/extension-base/src/page/substrate/PostMessageProvider.ts index 23a0f1287cd..a112309f91a 100644 --- a/packages/extension-base/src/page/substrate/PostMessageProvider.ts +++ b/packages/extension-base/src/page/substrate/PostMessageProvider.ts @@ -4,8 +4,8 @@ import type { InjectedProvider, ProviderList, ProviderMeta } from '@subwallet/extension-inject/types'; import type { ProviderInterfaceEmitCb, ProviderInterfaceEmitted } from '@polkadot/rpc-provider/types'; import type { AnyFunction } from '@polkadot/types/types'; -import type { SendRequest } from './types'; +import { SendRequest } from '@subwallet/extension-base/page/types'; import EventEmitter from 'eventemitter3'; import { isUndefined, logger } from '@polkadot/util'; diff --git a/packages/extension-base/src/page/substrate/Signer.ts b/packages/extension-base/src/page/substrate/Signer.ts index 92ce69b5654..4aadb3c9c43 100644 --- a/packages/extension-base/src/page/substrate/Signer.ts +++ b/packages/extension-base/src/page/substrate/Signer.ts @@ -3,7 +3,8 @@ import type { Signer as SignerInterface, SignerResult } from '@polkadot/api/types'; import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; -import type { SendRequest } from './types'; + +import { SendRequest } from '@subwallet/extension-base/page/types'; // External to class, this.# is not private enough (yet) let sendRequest: SendRequest; diff --git a/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts index 8f9e2692826..e395bd6d28f 100644 --- a/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/AuthRequestHandler.ts @@ -5,7 +5,7 @@ import { _ChainInfo } from '@subwallet/chain-list/types'; import { AuthRequestV2, ResultResolver } from '@subwallet/extension-base/background/KoniTypes'; import { AccountAuthType, AuthorizeRequest, RequestAuthorizeTab, Resolver } from '@subwallet/extension-base/background/types'; import { ChainService } from '@subwallet/extension-base/services/chain-service'; -import {_isChainBitcoinCompatible, _isChainEvmCompatible} from '@subwallet/extension-base/services/chain-service/utils'; +import { _isChainBitcoinCompatible, _isChainEvmCompatible } from '@subwallet/extension-base/services/chain-service/utils'; import { KeyringService } from '@subwallet/extension-base/services/keyring-service'; import RequestService from '@subwallet/extension-base/services/request-service'; import { PREDEFINED_CHAIN_DAPP_CHAIN_MAP, WEB_APP_URL } from '@subwallet/extension-base/services/request-service/constants'; diff --git a/packages/extension-inject/src/bundle.ts b/packages/extension-inject/src/bundle.ts index f38b4534895..aa8ca412c05 100644 --- a/packages/extension-inject/src/bundle.ts +++ b/packages/extension-inject/src/bundle.ts @@ -1,11 +1,7 @@ // Copyright 2019-2022 @polkadot/extension-inject authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Injected, InjectedWindow, InjectOptions } from './types'; - -import { OpenBitProvider } from '@subwallet/extension-base/page'; - -import { EIP6963ProviderDetail, EIP6963ProviderInfo, EvmProvider } from './types'; +import { EIP6963ProviderDetail, EIP6963ProviderInfo, EvmProvider, Injected, InjectedWindow, InjectOptions, OpenBitProviderType } from './types'; export { packageInfo } from './packageInfo'; @@ -94,8 +90,8 @@ export const inject6963EIP = (provider: EvmProvider) => { announceProvider(); }; -export function injectBitcoinProvider () { +export function injectBitcoinProvider (openBitProvider: OpenBitProviderType) { const windowInject = window as Window & InjectedWindow; - windowInject.OpenBitProvider = OpenBitProvider; + windowInject.OpenBitProvider = openBitProvider; } diff --git a/packages/extension-inject/src/types.ts b/packages/extension-inject/src/types.ts index c252fce6e9d..82c2e253c70 100644 --- a/packages/extension-inject/src/types.ts +++ b/packages/extension-inject/src/types.ts @@ -5,7 +5,7 @@ import type { Signer as InjectedSigner } from '@polkadot/api/types'; import type { ProviderInterface } from '@polkadot/rpc-provider/types'; import type { ExtDef } from '@polkadot/types/extrinsic/signedExtensions/types'; -import { OpenBitProviderType } from '@subwallet/extension-base/page'; +import { StacksProvider } from '@stacks/connect'; import { KeypairType } from '@subwallet/keyring/types'; // eslint-disable-next-line no-undef @@ -107,6 +107,10 @@ export interface EvmProvider { isConnected(): boolean, } +export interface OpenBitProviderType extends StacksProvider { + isOpenBit: boolean; +} + export interface InjectedWindowProvider { enable: (origin: string) => Promise; version: string; @@ -117,7 +121,6 @@ export interface InjectedWindow extends This { ethereum: EvmProvider; OpenBit: EvmProvider; OpenBitProvider: OpenBitProviderType; - LeatherProvider: OpenBitProviderType; } export type InjectedExtension = InjectedExtensionInfo & Injected; diff --git a/packages/extension-koni/src/page.ts b/packages/extension-koni/src/page.ts index c90a38c269d..889e967a158 100644 --- a/packages/extension-koni/src/page.ts +++ b/packages/extension-koni/src/page.ts @@ -5,7 +5,7 @@ import type { RequestSignatures, TransportRequestMessage } from '@subwallet/exte import type { Message } from '@subwallet/extension-base/types'; import { MESSAGE_ORIGIN_CONTENT } from '@subwallet/extension-base/defaults'; -import { handleResponse, initEvmProvider } from '@subwallet/extension-base/page'; +import { handleResponse, initEvmProvider, OpenBitProvider } from '@subwallet/extension-base/page'; import { injectBitcoinProvider, injectEvmExtension } from '@subwallet/extension-inject'; function inject () { @@ -14,7 +14,7 @@ function inject () { // version: version // }); injectEvmExtension(initEvmProvider()); - injectBitcoinProvider(); + injectBitcoinProvider(OpenBitProvider); } // setup a response listener (events created by the loader for extension responses) From 5ba66056d8a4f76784a44f8039e0b17d43ed3859 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Wed, 26 Jun 2024 11:52:36 +0700 Subject: [PATCH 31/43] update: send Transfer for bitcoin provider #8 --- .../src/stores/base/RequestState.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/extension-koni-ui/src/stores/base/RequestState.ts b/packages/extension-koni-ui/src/stores/base/RequestState.ts index 4aeed8de6c5..3364a430fea 100644 --- a/packages/extension-koni-ui/src/stores/base/RequestState.ts +++ b/packages/extension-koni-ui/src/stores/base/RequestState.ts @@ -103,27 +103,27 @@ const requestStateSlice = createSlice({ updateAuthorizeRequests (state, { payload }: PayloadAction>) { state.authorizeRequest = payload; readyMap.updateAuthorizeRequests = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateMetadataRequests (state, { payload }: PayloadAction>) { state.metadataRequest = payload; readyMap.updateMetadataRequests = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateSigningRequests (state, { payload }: PayloadAction>) { state.signingRequest = payload; readyMap.updateSigningRequests = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateConfirmationRequests (state, action: PayloadAction>) { Object.assign(state, action.payload); readyMap.updateConfirmationRequests = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateBitcoinConfirmationRequests (state, action: PayloadAction>) { Object.assign(state, action.payload); readyMap.updateConfirmationRequests = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateTransactionRequests (state, { payload }: PayloadAction>) { state.transactionRequest = payload; @@ -131,13 +131,13 @@ const requestStateSlice = createSlice({ updateConnectWCRequests (state, { payload }: PayloadAction>) { state.connectWCRequest = payload; readyMap.updateConnectWalletConnect = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); }, updateWCNotSupportRequests (state, { payload }: PayloadAction>) { state.notSupportWCRequest = payload; readyMap.updateNotSupportWalletConnect = true; - computeStateSummary(state); + computeStateSummary(state as RequestState); } } }); From 2972e76e8abfdacd12621e417d68a9f611b9b6b6 Mon Sep 17 00:00:00 2001 From: lw Date: Wed, 26 Jun 2024 12:09:06 +0700 Subject: [PATCH 32/43] Fix eslint issues --- packages/extension-base/package.json | 1 - packages/extension-base/src/background/KoniTypes.ts | 3 +-- packages/extension-inject/package.json | 1 + packages/extension-web-ui/src/types/wallet.ts | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/extension-base/package.json b/packages/extension-base/package.json index b8ebb0c4ce7..f90a933c78a 100644 --- a/packages/extension-base/package.json +++ b/packages/extension-base/package.json @@ -48,7 +48,6 @@ "@polkadot/x-global": "^12.6.1", "@reduxjs/toolkit": "^1.9.1", "@sora-substrate/type-definitions": "^1.17.7", - "@stacks/connect": "^7.7.1", "@substrate/connect": "^0.7.26", "@subwallet/chain-list": "0.2.43", "@subwallet/extension-base": "^1.1.44-0", diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ced27a36753..8fb3ea03004 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -1,7 +1,7 @@ // Copyright 2019-2022 @polkadot/extension-koni authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { Psbt, PsbtTxOutput } from 'bitcoinjs-lib'; +import type { Psbt, PsbtTxInput, PsbtTxOutput } from 'bitcoinjs-lib'; import { _AssetRef, _AssetType, _ChainAsset, _ChainInfo, _FundStatus, _MultiChainAsset } from '@subwallet/chain-list/types'; import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError'; @@ -19,7 +19,6 @@ import { KeypairType, KeyringPair$Json, KeyringPair$Meta } from '@subwallet/keyr import { KeyringOptions } from '@subwallet/ui-keyring/options/types'; import { KeyringAddress, KeyringPairs$Json } from '@subwallet/ui-keyring/types'; import { SessionTypes } from '@walletconnect/types/dist/types/sign-client/session'; -import { PsbtTxInput } from 'bitcoinjs-lib/src/psbt'; import BN from 'bn.js'; import { DexieExportJsonStructure } from 'dexie-export-import'; import Web3 from 'web3'; diff --git a/packages/extension-inject/package.json b/packages/extension-inject/package.json index 9f5fbff69b5..8cd9be84ba0 100644 --- a/packages/extension-inject/package.json +++ b/packages/extension-inject/package.json @@ -23,6 +23,7 @@ "@polkadot/util": "^12.6.2", "@polkadot/util-crypto": "^12.6.2", "@polkadot/x-global": "^12.2.1", + "@stacks/connect": "^7.7.1", "@subwallet/keyring": "0.1.5-beta.0", "web3-core": "^1.10.0" }, diff --git a/packages/extension-web-ui/src/types/wallet.ts b/packages/extension-web-ui/src/types/wallet.ts index a182084a0e5..edc90bb0f6e 100644 --- a/packages/extension-web-ui/src/types/wallet.ts +++ b/packages/extension-web-ui/src/types/wallet.ts @@ -1,7 +1,7 @@ // Copyright 2019-2022 @subwallet/extension-web-ui authors & contributors // SPDX-License-Identifier: Apache-2.0 -import { OpenBitEvmProvider } from '@subwallet/extension-base/page/SubWalleEvmProvider'; +import { OpenBitEvmProvider } from '@subwallet/extension-base/page/evm/OpenBitEvmProvider'; import { EvmProvider, InjectedWindowProvider } from '@subwallet/extension-inject/types'; export interface WalletInfo { From 80f425f032d36fb06dc257fdec23a85a536c8e73 Mon Sep 17 00:00:00 2001 From: nguyentiendung Date: Wed, 26 Jun 2024 12:43:55 +0700 Subject: [PATCH 33/43] Update yarn.lock --- yarn.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index dd93dd6cb5d..f9b040b7f9c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6444,7 +6444,6 @@ __metadata: "@polkadot/x-global": ^12.6.1 "@reduxjs/toolkit": ^1.9.1 "@sora-substrate/type-definitions": ^1.17.7 - "@stacks/connect": ^7.7.1 "@substrate/connect": ^0.7.26 "@subwallet/chain-list": 0.2.43 "@subwallet/extension-base": ^1.1.44-0 @@ -6547,6 +6546,7 @@ __metadata: "@polkadot/util": ^12.6.2 "@polkadot/util-crypto": ^12.6.2 "@polkadot/x-global": ^12.2.1 + "@stacks/connect": ^7.7.1 "@subwallet/keyring": 0.1.5-beta.0 "@types/chrome": ^0.0.254 "@types/firefox-webext-browser": ^120.0.0 From 28596592cdc7bffc6c6876de11b7018389617896 Mon Sep 17 00:00:00 2001 From: Thiendekaco Date: Wed, 26 Jun 2024 14:14:53 +0700 Subject: [PATCH 34/43] update: completed send Transfer for bitcoin provider --- .../src/background/KoniTypes.ts | 16 ++- .../src/koni/background/handlers/Extension.ts | 119 +++++++++++++++++- .../src/koni/background/handlers/State.ts | 82 +++++------- .../src/page/substrate/Injected.ts | 3 +- .../src/page/substrate/PostMessageProvider.ts | 2 +- .../src/page/substrate/Signer.ts | 3 +- .../handler/BitcoinRequestHandler.ts | 64 +++++----- .../src/services/request-service/index.ts | 5 +- .../src/services/transaction-service/index.ts | 78 ++++++++++++ .../src/services/transaction-service/types.ts | 6 +- .../src/types/balance/transfer.ts | 4 + .../src/utils/bitcoin/utxo-management.ts | 2 - .../src/Popup/Confirmations/index.tsx | 12 +- .../Confirmations/parts/Sign/Bitcoin.tsx | 20 +-- ...coinSendTransactionRequestConfirmation.tsx | 63 +++++----- .../Popup/Transaction/variants/SendFund.tsx | 24 ++-- .../src/messaging/transaction/transfer.ts | 6 +- .../src/stores/base/RequestState.ts | 2 + .../src/types/confirmation.ts | 2 +- yarn.lock | 8 +- 20 files changed, 368 insertions(+), 153 deletions(-) diff --git a/packages/extension-base/src/background/KoniTypes.ts b/packages/extension-base/src/background/KoniTypes.ts index ced27a36753..1f983cb7eff 100644 --- a/packages/extension-base/src/background/KoniTypes.ts +++ b/packages/extension-base/src/background/KoniTypes.ts @@ -13,7 +13,7 @@ import { _BitcoinApi, _ChainState, _EvmApi, _NetworkUpsertParams, _SubstrateApi, import { CrowdloanContributionsResponse } from '@subwallet/extension-base/services/subscan-service/types'; import { BitcoinTransactionData, SWTransactionResponse, SWTransactionResult } from '@subwallet/extension-base/services/transaction-service/types'; import { WalletConnectNotSupportRequest, WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types'; -import { BalanceJson, BitcoinFeeDetail, BuyServiceInfo, BuyTokenInfo, EarningRewardHistoryItem, EarningRewardJson, EarningStatus, HandleYieldStepParams, LeavePoolAdditionalData, NominationPoolInfo, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestGetYieldPoolTargets, RequestStakeCancelWithdrawal, RequestStakeClaimReward, RequestSubmitTransfer, RequestSubscribeTransfer, RequestUnlockDotCheckCanMint, RequestUnlockDotSubscribeMintedData, RequestYieldLeave, RequestYieldStepSubmit, RequestYieldWithdrawal, ResponseEarlyValidateYield, ResponseGetYieldPoolTargets, ResponseSubscribeTransfer, SubmitYieldStepData, TokenApproveData, UnlockDotTransactionNft, UnstakingStatus, ValidateYieldProcessParams, YieldPoolInfo, YieldPositionInfo, YieldValidationStatus } from '@subwallet/extension-base/types'; +import { BalanceJson, BitcoinFeeDetail, BuyServiceInfo, BuyTokenInfo, EarningRewardHistoryItem, EarningRewardJson, EarningStatus, HandleYieldStepParams, LeavePoolAdditionalData, NominationPoolInfo, OptimalYieldPath, OptimalYieldPathParams, RequestEarlyValidateYield, RequestGetYieldPoolTargets, RequestStakeCancelWithdrawal, RequestStakeClaimReward, RequestSubmitTransfer, RequestSubmitTransferWithId, RequestSubscribeTransfer, RequestUnlockDotCheckCanMint, RequestUnlockDotSubscribeMintedData, RequestYieldLeave, RequestYieldStepSubmit, RequestYieldWithdrawal, ResponseEarlyValidateYield, ResponseGetYieldPoolTargets, ResponseSubscribeTransfer, SubmitYieldStepData, TokenApproveData, UnlockDotTransactionNft, UnstakingStatus, UtxoResponseItem, ValidateYieldProcessParams, YieldPoolInfo, YieldPositionInfo, YieldValidationStatus } from '@subwallet/extension-base/types'; import { InjectedAccount, InjectedAccountWithMeta, MetadataDefBase } from '@subwallet/extension-inject/types'; import { KeypairType, KeyringPair$Json, KeyringPair$Meta } from '@subwallet/keyring/types'; import { KeyringOptions } from '@subwallet/ui-keyring/options/types'; @@ -1392,7 +1392,10 @@ export interface EvmSendTransactionRequest extends TransactionConfig, EvmSignReq isToContract: boolean; } -export interface BitcoinSendTransactionRequest extends BitcoinSignRequest, BitcoinTransactionConfig {} +export interface BitcoinSendTransactionRequest extends BitcoinSignRequest, BitcoinTransactionConfig { + outputs?: BitcoinOutputUtox[], + inputs?: UtxoResponseItem[], +} export type EvmWatchTransactionRequest = EvmSendTransactionRequest; export type BitcoinWatchTransactionRequest = BitcoinSendTransactionRequest; @@ -1406,11 +1409,18 @@ export interface ConfirmationsQueueItemOptions { networkKey?: string; } +export interface BitcoinOutputUtox { + address: string; + value: number; +} + export interface BitcoinTransactionConfig{ + id?: string, from?: string | number; to?: string; value?: number | string | BN; networkKey?: string; + tokenSlug?: string; fee?: BitcoinFeeDetail; } @@ -1491,6 +1501,7 @@ export interface ConfirmationDefinitions { export interface ConfirmationDefinitionsBitcoin { bitcoinSignatureRequest: [ConfirmationsQueueItem, ConfirmationResult], bitcoinSendTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult], + bitcoinSendTransactionRequestAfterConfirmation: [ConfirmationsQueueItem, ConfirmationResult], bitcoinWatchTransactionRequest: [ConfirmationsQueueItem, ConfirmationResult], bitcoinSignPsbtRequest: [ConfirmationsQueueItem, ConfirmationResult], } @@ -2560,6 +2571,7 @@ export interface KoniRequestSignatures { // Transfer 'pri(accounts.checkTransfer)': [RequestCheckTransfer, ValidateTransactionResponse]; 'pri(accounts.transfer)': [RequestSubmitTransfer, SWTransactionResponse]; + 'pri(accounts.transfer.after.confirmation)': [RequestSubmitTransferWithId, SWTransactionResponse]; 'pri(accounts.getBitcoinTransactionData)': [RequestSubmitTransfer, BitcoinTransactionData]; 'pri(accounts.checkCrossChainTransfer)': [RequestCheckCrossChainTransfer, ValidateTransactionResponse]; diff --git a/packages/extension-base/src/koni/background/handlers/Extension.ts b/packages/extension-base/src/koni/background/handlers/Extension.ts index 67fba33026a..25fbb6eb6b7 100644 --- a/packages/extension-base/src/koni/background/handlers/Extension.ts +++ b/packages/extension-base/src/koni/background/handlers/Extension.ts @@ -39,7 +39,40 @@ import { WALLET_CONNECT_EIP155_NAMESPACE } from '@subwallet/extension-base/servi import { isProposalExpired, isSupportWalletConnectChain, isSupportWalletConnectNamespace } from '@subwallet/extension-base/services/wallet-connect-service/helpers'; import { ResultApproveWalletConnectSession, WalletConnectNotSupportRequest, WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types'; import { AccountsStore } from '@subwallet/extension-base/stores'; -import { BalanceJson, BitcoinFeeInfo, BitcoinFeeRate, BuyServiceInfo, BuyTokenInfo, DetermineUtxosForSpendArgs, EarningRewardJson, EvmEIP1995FeeOption, EvmFeeInfo, FeeChainType, FeeDetail, FeeInfo, GetFeeFunction, NominationPoolInfo, OptimalYieldPathParams, RequestEarlyValidateYield, RequestGetYieldPoolTargets, RequestStakeCancelWithdrawal, RequestStakeClaimReward, RequestSubmitTransfer, RequestSubscribeTransfer, RequestUnlockDotCheckCanMint, RequestUnlockDotSubscribeMintedData, RequestYieldLeave, RequestYieldStepSubmit, RequestYieldWithdrawal, ResponseGetYieldPoolTargets, ResponseSubscribeTransfer, SubstrateFeeInfo, ValidateYieldProcessParams, YieldPoolType } from '@subwallet/extension-base/types'; +import { + BalanceJson, + BitcoinFeeInfo, + BitcoinFeeRate, + BuyServiceInfo, + BuyTokenInfo, + DetermineUtxosForSpendArgs, + EarningRewardJson, + EvmEIP1995FeeOption, + EvmFeeInfo, + FeeChainType, + FeeDetail, + FeeInfo, + GetFeeFunction, + NominationPoolInfo, + OptimalYieldPathParams, + RequestEarlyValidateYield, + RequestGetYieldPoolTargets, + RequestStakeCancelWithdrawal, + RequestStakeClaimReward, + RequestSubmitTransfer, RequestSubmitTransferWithId, + RequestSubscribeTransfer, + RequestUnlockDotCheckCanMint, + RequestUnlockDotSubscribeMintedData, + RequestYieldLeave, + RequestYieldStepSubmit, + RequestYieldWithdrawal, + ResponseGetYieldPoolTargets, + ResponseSubmitTransferWithId, + ResponseSubscribeTransfer, + SubstrateFeeInfo, + ValidateYieldProcessParams, + YieldPoolType +} from '@subwallet/extension-base/types'; import { combineBitcoinFee, combineEthFee, convertSubjectInfoToAddresses, createTransactionFromRLP, determineUtxosForSpend, determineUtxosForSpendAll, filterUneconomicalUtxos, generateAccountProxyId, getSizeInfo, isAddressValidWithAuthType, isSameAddress, keyringGetAccounts, reformatAddress, signatureToHex, Transaction as QrTransaction, uniqueStringArray } from '@subwallet/extension-base/utils'; import { parseContractInput, parseEvmRlp } from '@subwallet/extension-base/utils/eth/parseTransaction'; import { balanceFormatter, BN_ZERO, formatNumber } from '@subwallet/extension-base/utils/number'; @@ -1949,8 +1982,6 @@ export default class KoniExtension { to, network }); - - console.log('getBitcoinTransactionObject', transaction); } else { const substrateApi = this.#koniState.getSubstrateApi(chain); @@ -2040,6 +2071,86 @@ export default class KoniExtension { }); } + private async makeTransferAfterConfirmation (inputData: RequestSubmitTransferWithId): Promise { + const { chain, feeCustom, feeOption, from, to, tokenSlug, transferAll, value, id } = inputData; + const [errors, , , tokenInfo] = this.validateTransfer(tokenSlug, from, to, value, transferAll); + + const warnings: TransactionWarning[] = []; + + const chainInfo = this.#koniState.getChainInfo(chain); + const nativeTokenInfo = this.#koniState.getNativeTokenInfo(chain); + const nativeTokenSlug: string = nativeTokenInfo.slug; + const isTransferNativeToken = nativeTokenSlug === tokenSlug; + let chainType = ChainType.SUBSTRATE; + + const tokenBaseAmount: AmountData = { value: '0', symbol: tokenInfo.symbol, decimals: tokenInfo.decimals || 0 }; + const transferAmount: AmountData = { ...tokenBaseAmount }; + + let transaction: ValidateTransactionResponseInput['transaction']; + + // Get native token amount + const freeBalance = await this.getAddressFreeBalance({ address: from, networkKey: chain, token: tokenSlug }); + + const getChainFee: GetFeeFunction = (id, chain, type) => { + return this.#koniState.feeService.subscribeChainFee(id, chain, type); + }; + + const txVal: string = transferAll ? freeBalance.value : (value || '0'); + + try { + if (_isChainBitcoinCompatible(chainInfo)) { + chainType = ChainType.BITCOIN; + + const bitcoinApi = this.#koniState.getBitcoinApi(chain); // Get Bitcoin API map + const network = chainInfo.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; + + [ + transaction, + transferAmount.value + ] = await getBitcoinTransactionObject({ + bitcoinApi, + from, + getChainFee, + chain: chain, + feeCustom, + feeOption, + transferAll, + value: txVal, + to, + network + }); + } + } catch (e) { + const error = e as Error; + + if (error.message.includes('transfer amount exceeds balance')) { + error.message = t('Insufficient balance'); + } + + throw error; + } + + const transferNativeAmount = isTransferNativeToken ? transferAmount.value : '0'; + + return this.#koniState.transactionService.handleTransactionAfterConfirmation({ + id, + errors, + warnings, + address: from, + chain: chain, + feeCustom, + feeOption, + chainType, + transferNativeAmount, + transaction, + data: inputData, + extrinsicType: isTransferNativeToken ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.TRANSFER_TOKEN, + ignoreWarnings: transferAll, + isTransferAll: isTransferNativeToken ? transferAll : false, + edAsWarning: isTransferNativeToken + }); + } + private async getBitcoinTransactionData (inputData: RequestSubmitTransfer): Promise { const { chain, feeCustom, feeOption, from, to, transferAll, value } = inputData; @@ -5435,6 +5546,8 @@ export default class KoniExtension { /// Transfer case 'pri(accounts.transfer)': return await this.makeTransfer(request as RequestSubmitTransfer); + case 'pri(accounts.transfer.after.confirmation)': + return await this.makeTransferAfterConfirmation(request as RequestSubmitTransfer); case 'pri(accounts.crossChainTransfer)': return await this.makeCrossChainTransfer(request as RequestCrossChainTransfer); case 'pri(accounts.getBitcoinTransactionData)': diff --git a/packages/extension-base/src/koni/background/handlers/State.ts b/packages/extension-base/src/koni/background/handlers/State.ts index 9bf811f60ea..98a1515b402 100644 --- a/packages/extension-base/src/koni/background/handlers/State.ts +++ b/packages/extension-base/src/koni/background/handlers/State.ts @@ -6,7 +6,7 @@ import { BitcoinProviderError } from '@subwallet/extension-base/background/error import { EvmProviderError } from '@subwallet/extension-base/background/errors/EvmProviderError'; import { withErrorLog } from '@subwallet/extension-base/background/handlers/helpers'; import { isSubscriptionRunning, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions'; -import { AccountRefMap, AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, BasicTxErrorType, BitcoinProviderErrorType, BitcoinSendTransactionParams, BitcoinSendTransactionRequest, BitcoinSignatureRequest, BitcoinSignPsbtPayload, BitcoinSignPsbtRawRequest, BitcoinSignPsbtRequest, BitcoinTransactionConfig, ChainStakingMetadata, ChainType, ConfirmationsQueue, CrowdloanItem, CrowdloanJson, CurrentAccountInfo, CurrentAccountProxyInfo, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCheckPublicAndSecretKey, ServiceInfo, SignMessageBitcoinResult, SignPsbtBitcoinResult, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes'; +import { AccountRefMap, AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, BasicTxErrorType, BitcoinOutputUtox, BitcoinProviderErrorType, BitcoinSendTransactionParams, BitcoinSendTransactionRequest, BitcoinSignatureRequest, BitcoinSignPsbtPayload, BitcoinSignPsbtRawRequest, BitcoinSignPsbtRequest, BitcoinTransactionConfig, ChainStakingMetadata, ChainType, ConfirmationsQueue, CrowdloanItem, CrowdloanJson, CurrentAccountInfo, CurrentAccountProxyInfo, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestCheckPublicAndSecretKey, RequestConfirmationComplete, RequestConfirmationCompleteBitcoin, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ResponseCheckPublicAndSecretKey, ServiceInfo, SignMessageBitcoinResult, SignPsbtBitcoinResult, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes'; import { AccountJson, RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning } from '@subwallet/extension-base/background/types'; import { ALL_ACCOUNT_KEY, ALL_GENESIS_HASH, MANTA_PAY_BALANCE_INTERVAL } from '@subwallet/extension-base/constants'; import { NftService } from '@subwallet/extension-base/koni/api/nft'; @@ -40,7 +40,7 @@ import { TransactionEventResponse } from '@subwallet/extension-base/services/tra import WalletConnectService from '@subwallet/extension-base/services/wallet-connect-service'; import { SWStorage } from '@subwallet/extension-base/storage'; import AccountRefStore from '@subwallet/extension-base/stores/AccountRef'; -import { BalanceItem, BalanceMap, DetermineUtxosForSpendArgs, EvmFeeInfo } from '@subwallet/extension-base/types'; +import { BalanceItem, BalanceMap, DetermineUtxosForSpendArgs, EvmFeeInfo, UtxoResponseItem } from '@subwallet/extension-base/types'; import { determineUtxosForSpend, filterUneconomicalUtxos, getSizeInfo, isAccountAll, keyringGetAccounts, stripUrl, targetIsWeb } from '@subwallet/extension-base/utils'; import { isContractAddress, parseContractInput } from '@subwallet/extension-base/utils/eth/parseTransaction'; import { createPromiseHandler } from '@subwallet/extension-base/utils/promise'; @@ -160,17 +160,18 @@ export default class KoniState { this.subscanService = SubscanService.getInstance(); this.settingService = new SettingService(); this.feeService = new FeeService(this); - this.requestService = new RequestService(this.chainService, this.settingService, this.keyringService, this.feeService); + this.priceService = new PriceService(this.dbService, this.eventService, this.chainService); this.balanceService = new BalanceService(this); this.historyService = new HistoryService(this.dbService, this.chainService, this.eventService, this.keyringService); this.mintCampaignService = new MintCampaignService(this); - this.walletConnectService = new WalletConnectService(this, this.requestService); this.migrationService = new MigrationService(this, this.eventService); this.campaignService = new CampaignService(this); this.buyService = new BuyService(this); this.transactionService = new TransactionService(this); + this.requestService = new RequestService(this.chainService, this.settingService, this.keyringService, this.feeService, this.transactionService); + this.walletConnectService = new WalletConnectService(this, this.requestService); this.earningService = new EarningService(this); this.feeService = new FeeService(this); this.nftService = new NftService(); @@ -1339,10 +1340,14 @@ export default class KoniState { throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Receiving address must be different from sending address')); } + const tokenInfo = this.getNativeTokenInfo(networkKey); + const transaction: BitcoinTransactionConfig = { + id, from: transactionParams.account, to: transactionParams.recipients[0].address, value: autoFormatNumber(transactionParams.recipients[0].amount), + tokenSlug: tokenInfo.slug, networkKey: transactionParams.network === 'testnet' ? 'bitcoinTestnet' : 'bitcoin' }; @@ -1426,9 +1431,11 @@ export default class KoniState { let maxTransferable = new BigN('0'); let estimatedFee = '0'; + let inputs: UtxoResponseItem[] = []; + let outputs: BitcoinOutputUtox[] = []; try { - const { fee: _estimatedFee, inputs } = determineUtxosForSpend(determineUtxosArgs); + const { fee: _estimatedFee, inputs: inputsRs, outputs: outputRs } = determineUtxosForSpend(determineUtxosArgs); const { txVBytes: vSize } = getSizeInfo({ inputLength: inputs.length, @@ -1436,6 +1443,8 @@ export default class KoniState { recipients: [transaction.to] }); + inputs = [...inputsRs]; + outputs = [...outputRs]; estimatedFee = new BigN(_estimatedFee).toFixed(0); feeOptions = { ...feeOptions_, @@ -1467,58 +1476,27 @@ export default class KoniState { ...transaction, hashPayload: JSON.stringify(transaction), fee: feeOptions, - account: account, - canSign: true + inputs, + canSign: true, + outputs, + account: account }; - const eType = transaction.value ? ExtrinsicType.TRANSFER_BALANCE : ExtrinsicType.EVM_EXECUTE; - - const transactionData = { ...transaction }; - const token = this.chainService.getNativeTokenInfo(networkKey); - - if (eType === ExtrinsicType.TRANSFER_BALANCE) { - // @ts-ignore - transactionData.tokenSlug = token.slug; - } - // Custom handle this instead of general handler transaction - const transactionEmitter = await this.transactionService.addTransaction({ - transaction: requestPayload, - address: requestPayload.from as string, - chain: networkKey, - url, - data: transactionData, - extrinsicType: eType, - chainType: ChainType.BITCOIN, - estimateFee: { - value: estimatedFee, - symbol: token.symbol, - decimals: token.decimals || 18 - }, - id - }); - - // Wait extrinsic hash - return new Promise((resolve, reject) => { - transactionEmitter.on('extrinsicHash', (rs: TransactionEventResponse) => { - resolve(rs.extrinsicHash); - }); - - // Mapping error for evmProvider - transactionEmitter.on('error', (rs: TransactionEventResponse) => { - let evmProviderError = new EvmProviderError(EvmProviderErrorType.INTERNAL_ERROR); - - const errorType = (rs.errors[0]?.errorType || BasicTxErrorType.INTERNAL_ERROR); - - if (errorType === BasicTxErrorType.USER_REJECT_REQUEST || errorType === BasicTxErrorType.UNABLE_TO_SIGN) { - evmProviderError = new EvmProviderError(EvmProviderErrorType.USER_REJECTED_REQUEST); - } else if (errorType === BasicTxErrorType.UNABLE_TO_SEND) { - evmProviderError = new EvmProviderError(EvmProviderErrorType.INTERNAL_ERROR, rs.errors[0]?.message); + return this.requestService.addConfirmationBitcoin(id, url, 'bitcoinSendTransactionRequestAfterConfirmation', requestPayload, { + requiredPassword: false + }) + .then(({ isApproved, payload }) => { + if (isApproved) { + if (payload) { + return payload; + } else { + throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS, t('Not found signature')); + } + } else { + throw new BitcoinProviderError(BitcoinProviderErrorType.USER_REJECTED_REQUEST); } - - reject(evmProviderError); }); - }); } public refreshSubstrateApi (key: string) { diff --git a/packages/extension-base/src/page/substrate/Injected.ts b/packages/extension-base/src/page/substrate/Injected.ts index e8bb8615567..fa738e3f53e 100644 --- a/packages/extension-base/src/page/substrate/Injected.ts +++ b/packages/extension-base/src/page/substrate/Injected.ts @@ -1,10 +1,9 @@ // Copyright 2019-2022 @polkadot/extension authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SendRequest } from '@subwallet/extension-base/page/types'; import type { Injected } from '@subwallet/extension-inject/types'; -import { SendRequest } from '@subwallet/extension-base/page/types'; - import Accounts from './Accounts'; import Metadata from './Metadata'; import PostMessageProvider from './PostMessageProvider'; diff --git a/packages/extension-base/src/page/substrate/PostMessageProvider.ts b/packages/extension-base/src/page/substrate/PostMessageProvider.ts index a112309f91a..936b69286ea 100644 --- a/packages/extension-base/src/page/substrate/PostMessageProvider.ts +++ b/packages/extension-base/src/page/substrate/PostMessageProvider.ts @@ -1,11 +1,11 @@ // Copyright 2019-2022 @polkadot/extension-base authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SendRequest } from '@subwallet/extension-base/page/types'; import type { InjectedProvider, ProviderList, ProviderMeta } from '@subwallet/extension-inject/types'; import type { ProviderInterfaceEmitCb, ProviderInterfaceEmitted } from '@polkadot/rpc-provider/types'; import type { AnyFunction } from '@polkadot/types/types'; -import { SendRequest } from '@subwallet/extension-base/page/types'; import EventEmitter from 'eventemitter3'; import { isUndefined, logger } from '@polkadot/util'; diff --git a/packages/extension-base/src/page/substrate/Signer.ts b/packages/extension-base/src/page/substrate/Signer.ts index 4aadb3c9c43..2dcbc5ad918 100644 --- a/packages/extension-base/src/page/substrate/Signer.ts +++ b/packages/extension-base/src/page/substrate/Signer.ts @@ -1,11 +1,10 @@ // Copyright 2019-2022 @polkadot/extension-base authors & contributors // SPDX-License-Identifier: Apache-2.0 +import type { SendRequest } from '@subwallet/extension-base/page/types'; import type { Signer as SignerInterface, SignerResult } from '@polkadot/api/types'; import type { SignerPayloadJSON, SignerPayloadRaw } from '@polkadot/types/types'; -import { SendRequest } from '@subwallet/extension-base/page/types'; - // External to class, this.# is not private enough (yet) let sendRequest: SendRequest; let nextId = 0; diff --git a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts index b8e1c4277ef..968d3ba8f50 100644 --- a/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts +++ b/packages/extension-base/src/services/request-service/handler/BitcoinRequestHandler.ts @@ -8,7 +8,8 @@ import { getBitcoinTransactionObject } from '@subwallet/extension-base/services/ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import FeeService from '@subwallet/extension-base/services/fee-service/service'; import RequestService from '@subwallet/extension-base/services/request-service'; -import { SWTransactionResult } from '@subwallet/extension-base/services/transaction-service/types'; +import TransactionService from '@subwallet/extension-base/services/transaction-service'; +import { TransactionEventResponse } from '@subwallet/extension-base/services/transaction-service/types'; import { GetFeeFunction } from '@subwallet/extension-base/types'; import { isInternalRequest } from '@subwallet/extension-base/utils/request'; import keyring from '@subwallet/ui-keyring'; @@ -23,21 +24,24 @@ import { Logger } from '@polkadot/util/types'; export default class BitcoinRequestHandler { readonly #requestService: RequestService; readonly #chainService: ChainService; + readonly #transactionService: TransactionService; readonly #feeService: FeeService; readonly #logger: Logger; private readonly confirmationsQueueSubjectBitcoin = new BehaviorSubject({ bitcoinSignatureRequest: {}, bitcoinSendTransactionRequest: {}, bitcoinWatchTransactionRequest: {}, + bitcoinSendTransactionRequestAfterConfirmation: {}, bitcoinSignPsbtRequest: {} }); private readonly confirmationsPromiseMap: Record, validator?: (rs: any) => Error | undefined }> = {}; - constructor (requestService: RequestService, chainService: ChainService, feeService: FeeService) { + constructor (requestService: RequestService, chainService: ChainService, feeService: FeeService, transactionService: TransactionService) { this.#requestService = requestService; this.#chainService = chainService; this.#feeService = feeService; + this.#transactionService = transactionService; this.#logger = createLogger('BitcoinRequestHandler'); } @@ -68,7 +72,7 @@ export default class BitcoinRequestHandler { const payloadJson = JSON.stringify(payload); const isInternal = isInternalRequest(url); - if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest'].includes(type)) { + if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinSendTransactionRequestAfterConfirmation'].includes(type)) { const isAlwaysRequired = await this.#requestService.settingService.isAlwaysRequired; if (isAlwaysRequired) { @@ -102,6 +106,7 @@ export default class BitcoinRequestHandler { }; }); + console.log(confirmations, 'bg'); this.confirmationsQueueSubjectBitcoin.next(confirmations); if (!isInternal) { @@ -204,18 +209,23 @@ export default class BitcoinRequestHandler { return signedTransaction.extractTransaction().toHex(); } - private async signTransactionBitcoinWithPayload (request: ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][0], { payload }: ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][1]): Promise { - if (!payload) { - throw new BitcoinProviderError(BitcoinProviderErrorType.INVALID_PARAMS); - } - - const transactionObj = JSON.parse(payload) as SWTransactionResult; + private async signTransactionBitcoinWithPayload (request: ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequestAfterConfirmation'][0]): Promise { + const transaction = this.#transactionService.getTransaction(request.id); + const { chain, emitterTransaction, feeCustom, feeOption, id } = transaction; + const { from, to, value } = transaction.data as ExtrinsicDataTypeMap['transfer.balance']; - const { chain, feeCustom, feeOption } = transactionObj; - const { from, to, value } = transactionObj.data as ExtrinsicDataTypeMap['transfer.balance']; + if (!emitterTransaction) { + throw new BitcoinProviderError(BitcoinProviderErrorType.INTERNAL_ERROR); + } const chainInfo = this.#chainService.getChainInfoByKey(chain); const bitcoinApi = this.#chainService.getBitcoinApi(chain); + const eventData: TransactionEventResponse = { + id, + errors: [], + warnings: [], + extrinsicHash: id + }; const network = chainInfo.isTestnet ? bitcoin.networks.testnet : bitcoin.networks.bitcoin; @@ -250,7 +260,11 @@ export default class BitcoinRequestHandler { signedTransaction.finalizeAllInputs(); - return signedTransaction.extractTransaction().toHex(); + const signature = signedTransaction.extractTransaction().toHex(); + + this.#transactionService.emitterEventTransaction(emitterTransaction, eventData, chainInfo.slug, signature); + + return signature; } private async signPsbt (request: ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]): Promise { @@ -301,21 +315,17 @@ export default class BitcoinRequestHandler { } private async decorateResultBitcoin (t: T, request: ConfirmationDefinitionsBitcoin[T][0], result: ConfirmationDefinitionsBitcoin[T][1]) { - if (!result.payload) { - if (t === 'bitcoinSignatureRequest') { - result.payload = this.signMessageBitcoin(request as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest'][0]); - } else if (t === 'bitcoinSendTransactionRequest') { - result.payload = this.signTransactionBitcoin(request as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][0]); - } else if (t === 'bitcoinSignPsbtRequest') { - result.payload = await this.signPsbt(request as ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]); - } - } else { - if (t === 'bitcoinSendTransactionRequest') { - result.payload = await this.signTransactionBitcoinWithPayload(request as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][0], result as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][1]); - } + if (t === 'bitcoinSignatureRequest') { + result.payload = this.signMessageBitcoin(request as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest'][0]); + } else if (t === 'bitcoinSendTransactionRequest') { + result.payload = this.signTransactionBitcoin(request as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequest'][0]); + } else if (t === 'bitcoinSignPsbtRequest') { + result.payload = await this.signPsbt(request as ConfirmationDefinitionsBitcoin['bitcoinSignPsbtRequest'][0]); + } else if (t === 'bitcoinSendTransactionRequestAfterConfirmation') { + result.payload = await this.signTransactionBitcoinWithPayload(request as ConfirmationDefinitionsBitcoin['bitcoinSendTransactionRequestAfterConfirmation'][0]); } - if (t === 'bitcoinSignatureRequest' || t === 'bitcoinSendTransactionRequest') { + if (t === 'bitcoinSignatureRequest' || t === 'bitcoinSendTransactionRequest' || t === 'bitcoinSignPsbtRequest' || t === 'bitcoinSendTransactionRequestAfterConfirmation') { const isAlwaysRequired = await this.#requestService.settingService.isAlwaysRequired; if (isAlwaysRequired) { @@ -327,19 +337,15 @@ export default class BitcoinRequestHandler { public async completeConfirmationBitcoin (request: RequestConfirmationCompleteBitcoin): Promise { const confirmations = this.confirmationsQueueSubjectBitcoin.getValue(); - console.log(confirmations); - for (const ct in request) { const type = ct as ConfirmationTypeBitcoin; const result = request[type] as ConfirmationDefinitionsBitcoin[typeof type][1]; - console.log(request, 'errr'); const { id, isApproved } = result; const { resolver, validator } = this.confirmationsPromiseMap[id]; const confirmation = confirmations[type][id]; if (!resolver || !confirmation) { - console.log('error'); this.#logger.error(t('Unable to proceed. Please try again'), type, id); throw new Error('Unable to proceed. Please try again'); } diff --git a/packages/extension-base/src/services/request-service/index.ts b/packages/extension-base/src/services/request-service/index.ts index 2750303aa8c..47a88e70ad6 100644 --- a/packages/extension-base/src/services/request-service/index.ts +++ b/packages/extension-base/src/services/request-service/index.ts @@ -7,6 +7,7 @@ import { ChainService } from '@subwallet/extension-base/services/chain-service'; import FeeService from '@subwallet/extension-base/services/fee-service/service'; import { KeyringService } from '@subwallet/extension-base/services/keyring-service'; import SettingService from '@subwallet/extension-base/services/setting-service/SettingService'; +import TransactionService from '@subwallet/extension-base/services/transaction-service'; import { WalletConnectNotSupportRequest, WalletConnectSessionRequest } from '@subwallet/extension-base/services/wallet-connect-service/types'; import { MetadataDef } from '@subwallet/extension-inject/types'; import { BehaviorSubject } from 'rxjs'; @@ -32,7 +33,7 @@ export default class RequestService { readonly #notSupportWCRequestHandler: NotSupportWCRequestHandler; // Common - constructor (chainService: ChainService, settingService: SettingService, keyringService: KeyringService, feeService: FeeService) { + constructor (chainService: ChainService, settingService: SettingService, keyringService: KeyringService, feeService: FeeService, transactionService: TransactionService) { this.#chainService = chainService; this.settingService = settingService; this.keyringService = keyringService; @@ -41,7 +42,7 @@ export default class RequestService { this.#authRequestHandler = new AuthRequestHandler(this, this.#chainService, this.keyringService); this.#substrateRequestHandler = new SubstrateRequestHandler(this); this.#evmRequestHandler = new EvmRequestHandler(this); - this.#bitcoinRequestHandler = new BitcoinRequestHandler(this, this.#chainService, feeService); + this.#bitcoinRequestHandler = new BitcoinRequestHandler(this, this.#chainService, feeService, transactionService); this.#connectWCRequestHandler = new ConnectWCRequestHandler(this); this.#notSupportWCRequestHandler = new NotSupportWCRequestHandler(this); diff --git a/packages/extension-base/src/services/transaction-service/index.ts b/packages/extension-base/src/services/transaction-service/index.ts index 3ceedf488ed..a237ef2a380 100644 --- a/packages/extension-base/src/services/transaction-service/index.ts +++ b/packages/extension-base/src/services/transaction-service/index.ts @@ -368,6 +368,56 @@ export default class TransactionService { return validatedTransaction; } + public async handleTransactionAfterConfirmation (transaction: SWTransactionInput): Promise { + const validatedTransaction = await this.generalValidate(transaction); + const stopByErrors = validatedTransaction.errors.length > 0; + const stopByWarnings = validatedTransaction.warnings.length > 0 && !validatedTransaction.ignoreWarnings; + + if (stopByErrors || stopByWarnings) { + // @ts-ignore + 'transaction' in validatedTransaction && delete validatedTransaction.transaction; + 'additionalValidator' in validatedTransaction && delete validatedTransaction.additionalValidator; + 'eventsHandler' in validatedTransaction && delete validatedTransaction.eventsHandler; + + return validatedTransaction; + } + + validatedTransaction.warnings = []; + + const transactionsSubject = this.transactions; + const emitter = new EventEmitter(); + + // Fill transaction default info + const transactionUpdated = this.fillTransactionDefaultInfo(transaction); + + // Add Transaction + transactionsSubject[transactionUpdated.id] = { ...transactionUpdated, emitterTransaction: emitter }; + this.transactionSubject.next({ ...transactionsSubject }); + + emitter.on('success', (data: TransactionEventResponse) => { + validatedTransaction.id = data.id; + validatedTransaction.extrinsicHash = data.extrinsicHash; + }); + + emitter.on('signed', (data: TransactionEventResponse) => { + validatedTransaction.id = data.id; + validatedTransaction.extrinsicHash = data.extrinsicHash; + }); + + emitter.on('error', (data: TransactionEventResponse) => { + if (data.errors.length > 0) { + validatedTransaction.errors.push(...data.errors); + } + }); + + // @ts-ignore + 'transaction' in validatedTransaction && delete validatedTransaction.transaction; + 'additionalValidator' in validatedTransaction && delete validatedTransaction.additionalValidator; + 'eventsHandler' in validatedTransaction && delete validatedTransaction.eventsHandler; + + return { ...validatedTransaction }; + } + private async sendTransaction (transaction: SWTransaction): Promise { let emitter: TransactionEmitter; @@ -1074,6 +1124,34 @@ export default class TransactionService { return emitter; } + public emitterEventTransaction = (emitter: TransactionEmitter, eventData: TransactionEventResponse, chain: string, payload: string) => { + // Emit signed event + emitter.emit('signed', eventData); + // Add start info + emitter.emit('send', eventData); + + console.log(chain, payload); + const event = this.chainService.getBitcoinApi(chain).api.sendRawTransaction(payload); + + event.on('extrinsicHash', (txHash) => { + eventData.extrinsicHash = txHash; + emitter.emit('extrinsicHash', eventData); + }); + + event.on('success', (transactionStatus) => { + console.log(transactionStatus); + eventData.blockHash = transactionStatus.block_hash || undefined; + eventData.blockNumber = transactionStatus.block_height || undefined; + eventData.blockTime = transactionStatus.block_time ? (transactionStatus.block_time * 1000) : undefined; + emitter.emit('success', eventData); + }); + + event.on('error', (error) => { + eventData.errors.push(new TransactionError(BasicTxErrorType.UNABLE_TO_SEND, error)); + emitter.emit('error', eventData); + }); + }; + private async signAndSendEvmTransaction ({ address, chain, id, diff --git a/packages/extension-base/src/services/transaction-service/types.ts b/packages/extension-base/src/services/transaction-service/types.ts index 07ced89ee4f..efb664b335f 100644 --- a/packages/extension-base/src/services/transaction-service/types.ts +++ b/packages/extension-base/src/services/transaction-service/types.ts @@ -10,7 +10,7 @@ import { TransactionConfig } from 'web3-core'; import { SubmittableExtrinsic } from '@polkadot/api/promise/types'; import { EventRecord } from '@polkadot/types/interfaces'; -export interface SWTransaction extends ValidateTransactionResponse, Partial>, TransactionFee { +export interface SWTransaction extends ValidateTransactionResponse, Partial>, TransactionFee, SWTransactionEmitter { id: string; url?: string; isInternal: boolean, @@ -31,6 +31,10 @@ export interface SWTransaction extends ValidateTransactionResponse, Partial +export interface SWTransactionEmitter { + emitterTransaction?: TransactionEmitter +} + type SwInputBase = Pick & Partial>; diff --git a/packages/extension-base/src/types/balance/transfer.ts b/packages/extension-base/src/types/balance/transfer.ts index 0f457953a86..b3b96fad746 100644 --- a/packages/extension-base/src/types/balance/transfer.ts +++ b/packages/extension-base/src/types/balance/transfer.ts @@ -31,3 +31,7 @@ export interface RequestSubmitTransfer extends BaseRequestSign, TransactionFee { transferAll: boolean; value: string; } + +export interface RequestSubmitTransferWithId extends RequestSubmitTransfer{ + id?: string; +} diff --git a/packages/extension-base/src/utils/bitcoin/utxo-management.ts b/packages/extension-base/src/utils/bitcoin/utxo-management.ts index d0a24595156..ae33c50619d 100644 --- a/packages/extension-base/src/utils/bitcoin/utxo-management.ts +++ b/packages/extension-base/src/utils/bitcoin/utxo-management.ts @@ -33,8 +33,6 @@ export function filterUneconomicalUtxos ({ feeRate, return filteredAndSortUtxos.reduce((utxos, utxo, currentIndex) => { const utxosWithout = utxos.filter((u) => u.txid !== utxo.txid); - console.log(currentIndex); - const { fee: feeWithout, spendableAmount: spendableAmountWithout } = getSpendableAmount({ utxos: utxosWithout, feeRate, diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx index dd65830e722..8f0740e51d7 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/index.tsx @@ -31,6 +31,7 @@ const titleMap: Record = { evmWatchTransactionRequest: detectTranslate('Transaction request'), bitcoinSignatureRequest: detectTranslate('Signature request'), bitcoinSendTransactionRequest: detectTranslate('Transaction request'), + bitcoinSendTransactionRequestAfterConfirmation: detectTranslate('Transaction request'), bitcoinWatchTransactionRequest: detectTranslate('Transaction request'), bitcoinSignPsbtRequest: detectTranslate('Sign PSBT request'), metadataRequest: detectTranslate('Update metadata'), @@ -46,6 +47,9 @@ const Component = function ({ className }: Props) { const { confirmationQueue, numberOfConfirmations } = useConfirmationsInfo(); const [index, setIndex] = useState(0); const confirmation = confirmationQueue[index] || null; + + console.log(confirmation, 'confirmation'); + const { t } = useTranslation(); const { alertProps, closeAlert, openAlert } = useAlert(alertModalId); @@ -94,8 +98,8 @@ const Component = function ({ className }: Props) { account = request.payload.account; canSign = request.payload.canSign; isMessage = confirmation.type === 'evmSignatureRequest'; - } else if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest', 'bitcoinSignPsbtRequest'].includes(confirmation.type)) { - const request = confirmation.item as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest' | 'bitcoinSendTransactionRequest' | 'bitcoinWatchTransactionRequest'][0]; + } else if (['bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest', 'bitcoinSignPsbtRequest', 'bitcoinSendTransactionRequestAfterConfirmation'].includes(confirmation.type)) { + const request = confirmation.item as ConfirmationDefinitionsBitcoin['bitcoinSignatureRequest' | 'bitcoinSendTransactionRequest' | 'bitcoinWatchTransactionRequest' | 'bitcoinSendTransactionRequestAfterConfirmation'][0]; account = request.payload.account; canSign = request.payload.canSign; @@ -157,10 +161,10 @@ const Component = function ({ className }: Props) { type={confirmation.type} /> ); - case 'bitcoinSendTransactionRequest': + case 'bitcoinSendTransactionRequestAfterConfirmation': return ( ); diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx index 99600a4c8c2..b34a1784c96 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx @@ -2,10 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { BitcoinSignatureRequest, ConfirmationDefinitionsBitcoin, ConfirmationResult, EvmSendTransactionRequest, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; -import { SWTransactionResult } from '@subwallet/extension-base/services/transaction-service/types'; +import { RequestSubmitTransferWithId } from '@subwallet/extension-base/types'; +import { wait } from '@subwallet/extension-base/utils'; import { CONFIRMATION_QR_MODAL } from '@subwallet/extension-koni-ui/constants'; import { useGetChainInfoByChainId, useLedger, useNotification, useUnlockChecker } from '@subwallet/extension-koni-ui/hooks'; -import { completeConfirmationBitcoin } from '@subwallet/extension-koni-ui/messaging'; +import { completeConfirmationBitcoin, makeTransferAfterConfirmation } from '@subwallet/extension-koni-ui/messaging'; import { AccountSignMode, BitcoinSignatureSupportType, PhosphorIcon, SigData, ThemeProps } from '@subwallet/extension-koni-ui/types'; import { getSignMode, isBitcoinMessage, removeTransactionPersist } from '@subwallet/extension-koni-ui/utils'; import { Button, Icon, ModalContext } from '@subwallet/react-ui'; @@ -24,7 +25,7 @@ interface Props extends ThemeProps { type: BitcoinSignatureSupportType; payload: ConfirmationDefinitionsBitcoin[BitcoinSignatureSupportType][0]; extrinsicType?: ExtrinsicType; - editedPayload?: SWTransactionResult; + editedPayload?: RequestSubmitTransferWithId; canSign?: boolean; } @@ -109,11 +110,14 @@ const Component: React.FC = (props: Props) => { const onApprovePassword = useCallback(() => { setLoading(true); - setTimeout(() => { - handleConfirm(type, id, editedPayload ? JSON.stringify(editedPayload) : '').finally(() => { - setLoading(false); - }); - }, 1000); + (type === 'bitcoinSendTransactionRequestAfterConfirmation' && editedPayload ? makeTransferAfterConfirmation(editedPayload) : wait(1000)) + .then(() => { + console.log('complete', type, id); + handleConfirm(type, id, '').finally(() => { + setLoading(false); + }); + }) + .catch(console.error); }, [editedPayload, id, type]); const onApproveSignature = useCallback((signature: SigData) => { diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSendTransactionRequestConfirmation.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSendTransactionRequestConfirmation.tsx index 9e323e6238a..883c35bd95a 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSendTransactionRequestConfirmation.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/variants/BitcoinSendTransactionRequestConfirmation.tsx @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { _ChainAsset } from '@subwallet/chain-list/types'; -import { BitcoinSendTransactionRequest, ConfirmationsQueueItem, ExtrinsicDataTypeMap, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes'; -import { SWTransactionResult } from '@subwallet/extension-base/services/transaction-service/types'; -import { BitcoinFeeDetail, ResponseSubscribeTransfer, TransactionFee } from '@subwallet/extension-base/types'; +import { BitcoinSendTransactionRequest, ConfirmationsQueueItem } from '@subwallet/extension-base/background/KoniTypes'; +import { BitcoinFeeDetail, RequestSubmitTransferWithId, ResponseSubscribeTransfer, TransactionFee } from '@subwallet/extension-base/types'; import { BN_ZERO, getDomainFromUrl } from '@subwallet/extension-base/utils'; -import { AlertBox, BitcoinFeeSelector, MetaInfo } from '@subwallet/extension-koni-ui/components'; +import { BitcoinFeeSelector, MetaInfo } from '@subwallet/extension-koni-ui/components'; import { RenderFieldNodeParams } from '@subwallet/extension-koni-ui/components/Field/TransactionFee/BitcoinFeeSelector'; import { useGetAccountByAddress } from '@subwallet/extension-koni-ui/hooks'; import { cancelSubscription, subscribeMaxTransfer } from '@subwallet/extension-koni-ui/messaging'; @@ -36,39 +35,46 @@ const convertToBigN = (num: BitcoinSendTransactionRequest['value']): string | nu }; function Component ({ className, request, type }: Props) { - const { id, payload: { account } } = request; + const { id, payload: { account, fee, inputs, networkKey, outputs, to, tokenSlug, value } } = request; const { t } = useTranslation(); - const { transactionRequest } = useSelector((state: RootState) => state.requestState); - - // request.payload.fee - const transaction = useMemo(() => transactionRequest[id], [transactionRequest, id]); - const [transactionInfo, setTransactionInfo] = useState(transaction); + const [transactionInfo, setTransactionInfo] = useState({ + id, + chain: networkKey as string, + from: account.address, + to: to as string, + tokenSlug: tokenSlug as string, + transferAll: false, + value: value?.toString() || '0' + }); const [isFetchingInfo, setIsFetchingInfo] = useState(false); const [isTransferAll, setIsTransferAll] = useState(false); const [transferInfo, setTransferInfo] = useState(); - const [transactionFeeInfo, setTransactionFeeInfo] = useState(undefined); - - const data = transaction.data as ExtrinsicDataTypeMap[ExtrinsicType.TRANSFER_BALANCE]; + const [transactionFeeInfo, setTransactionFeeInfo] = useState({ + feeOption: fee?.options.default + }); const chainInfoMap = useSelector((root: RootState) => root.chainStore.chainInfoMap); const assetRegistry = useSelector((root: RootState) => root.assetRegistry.assetRegistry); - const transferAmountValue = data.value; - const fromValue = data.from; - const toValue = data.to; - const chainValue = data.networkKey; - const assetValue = data.tokenSlug; + const transferAmountValue = value?.toString() as string; + const fromValue = account.address; + const toValue = to as string; + const chainValue = networkKey as string; + const assetValue = tokenSlug as string; + + console.log(request); const chainInfo = useMemo( - () => chainInfoMap[transaction.chain], - [chainInfoMap, transaction.chain] + () => chainInfoMap[networkKey as string], + [chainInfoMap, networkKey] ); const assetInfo: _ChainAsset | undefined = useMemo(() => { return assetRegistry[assetValue]; }, [assetRegistry, assetValue]); + console.log(assetInfo); const recipient = useGetAccountByAddress(toValue); // console.log(transactionRequest); @@ -219,16 +225,17 @@ function Component ({ className, request, type }: Props) { /> - {!!transaction.estimateFee?.tooHigh && ( - - )} + {/* {!!transaction.estimateFee?.tooHigh && ( */} + {/* */} + {/* )} */} => { setLoading(true); const { asset, chain, destChain, from: _from, to, value } = values; + console.log(value, 'value'); + let sendPromise: Promise; const account = findAccountByAddress(accounts, _from); @@ -428,17 +430,17 @@ const _SendFund = ({ className = '' }: Props): React.ReactElement => { }); } - setTimeout(() => { - // Handle transfer action - sendPromise - .then(onSuccess) - .catch(onError) - .finally(() => { - setLoading(false); - }) - ; - }, 300); - }, [accounts, chainInfoMap, assetRegistry, notification, t, isTransferAll, onSuccess, onError, transactionFeeInfo]); + // setTimeout(() => { + // // Handle transfer action + // sendPromise + // .then(onSuccess) + // .catch(onError) + // .finally(() => { + // setLoading(false); + // }) + // ; + // }, 300); + }, [accounts, chainInfoMap, assetRegistry, notification, t, isTransferAll, transactionFeeInfo]); const onSetMaxTransferable = useCallback((value: boolean) => { const bnMaxTransfer = new BigN(transferInfo?.maxTransferable || '0'); diff --git a/packages/extension-koni-ui/src/messaging/transaction/transfer.ts b/packages/extension-koni-ui/src/messaging/transaction/transfer.ts index b6abfe289de..d261551d101 100644 --- a/packages/extension-koni-ui/src/messaging/transaction/transfer.ts +++ b/packages/extension-koni-ui/src/messaging/transaction/transfer.ts @@ -3,7 +3,7 @@ import { AmountData, RequestCrossChainTransfer, RequestMaxTransferable, RequestTransferCheckReferenceCount, RequestTransferCheckSupporting, RequestTransferExistentialDeposit, SupportTransferResponse } from '@subwallet/extension-base/background/KoniTypes'; import { BitcoinTransactionData, SWTransactionResponse } from '@subwallet/extension-base/services/transaction-service/types'; -import { RequestSubmitTransfer, RequestSubscribeTransfer, ResponseSubscribeTransfer } from '@subwallet/extension-base/types'; +import { RequestSubmitTransfer, RequestSubmitTransferWithId, RequestSubscribeTransfer, ResponseSubscribeTransfer } from '@subwallet/extension-base/types'; import { sendMessage } from '../base'; @@ -11,6 +11,10 @@ export async function makeTransfer (request: RequestSubmitTransfer): Promise { + return sendMessage('pri(accounts.transfer.after.confirmation)', request); +} + export async function makeCrossChainTransfer (request: RequestCrossChainTransfer): Promise { return sendMessage('pri(accounts.crossChainTransfer)', request); } diff --git a/packages/extension-koni-ui/src/stores/base/RequestState.ts b/packages/extension-koni-ui/src/stores/base/RequestState.ts index 3364a430fea..179babd063a 100644 --- a/packages/extension-koni-ui/src/stores/base/RequestState.ts +++ b/packages/extension-koni-ui/src/stores/base/RequestState.ts @@ -30,6 +30,7 @@ const initialState: RequestState = { bitcoinSignatureRequest: {}, bitcoinSendTransactionRequest: {}, bitcoinWatchTransactionRequest: {}, + bitcoinSendTransactionRequestAfterConfirmation: {}, bitcoinSignPsbtRequest: {}, // Summary Info @@ -52,6 +53,7 @@ export const CONFIRMATIONS_FIELDS: Array = [ 'bitcoinSignatureRequest', 'bitcoinSendTransactionRequest', 'bitcoinWatchTransactionRequest', + 'bitcoinSendTransactionRequestAfterConfirmation', 'bitcoinSignPsbtRequest', 'connectWCRequest', 'notSupportWCRequest' diff --git a/packages/extension-koni-ui/src/types/confirmation.ts b/packages/extension-koni-ui/src/types/confirmation.ts index 38eb485f926..0acf3529da3 100644 --- a/packages/extension-koni-ui/src/types/confirmation.ts +++ b/packages/extension-koni-ui/src/types/confirmation.ts @@ -4,4 +4,4 @@ import { ConfirmationDefinitions, ConfirmationDefinitionsBitcoin } from '@subwallet/extension-base/background/KoniTypes'; export type EvmSignatureSupportType = keyof Pick; -export type BitcoinSignatureSupportType = keyof Pick; +export type BitcoinSignatureSupportType = keyof Pick; diff --git a/yarn.lock b/yarn.lock index dd93dd6cb5d..8fbd576a4b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6787,7 +6787,7 @@ __metadata: "@subwallet/keyring@file:./local-libs/keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=41584c&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/keyring@file:./local-libs/keyring#./local-libs/keyring::hash=97ee3e&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@ethereumjs/tx": ^5.0.0 "@polkadot/util": ^12.2.1 @@ -6803,7 +6803,7 @@ __metadata: rxjs: ^7.5.6 tiny-secp256k1: ^2.2.3 tslib: ^2.6.2 - checksum: 2a08cc156184ed60a2b2c3da9e181075520f35fd9bb30810ad573f5be6957919b6d8103dd60b64b556f73958e1128d2855ddde1499be719d1b590f6f312841e9 + checksum: 82d12c105f7a1901723e569440f522de6d5340606178c0bbb73b7e8b51a22fca676a225cae0fac37f3317c816c1fb31abb4249a6f50672bb8e204d9d78691e22 languageName: node linkType: hard @@ -6966,7 +6966,7 @@ __metadata: "@subwallet/ui-keyring@file:./local-libs/ui-keyring::locator=root-workspace-0b6124%40workspace%3A.": version: 0.1.4 - resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=c7a0de&locator=root-workspace-0b6124%40workspace%3A." + resolution: "@subwallet/ui-keyring@file:./local-libs/ui-keyring#./local-libs/ui-keyring::hash=4dea1f&locator=root-workspace-0b6124%40workspace%3A." dependencies: "@babel/runtime": ^7.20.1 "@polkadot/ui-settings": 2.9.14 @@ -6976,7 +6976,7 @@ __metadata: mkdirp: ^1.0.4 rxjs: ^7.5.7 store: ^2.0.12 - checksum: af099907afaac66bdce29812c1d15ed1181bba04167714352f4f3bd8f68a214c8d923f24b7f5e2e5612ad8db97b5eb5c6881a83fd291dd7d3f5f5e5c8a12a1a3 + checksum: ce2e1d12833856411902c2e270538e81d6f43735ba8f0f1a3f11d69043aba6bc9c47a497d03b5436dabd32593f4d9cdcf66f768c291724e706085a20f9090cbf languageName: node linkType: hard From 358d874a13a71ca67a753972d59b225dce5fdf7d Mon Sep 17 00:00:00 2001 From: lw Date: Thu, 27 Jun 2024 10:53:49 +0700 Subject: [PATCH 35/43] Fix sign issue --- .../src/Popup/Confirmations/parts/Sign/Bitcoin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx index 99600a4c8c2..da5d3ad16d9 100644 --- a/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx +++ b/packages/extension-koni-ui/src/Popup/Confirmations/parts/Sign/Bitcoin.tsx @@ -253,7 +253,7 @@ const Component: React.FC = (props: Props) => { {t('Cancel')}