From 3907c31667a62c23580e118cfe18f8c656487a6d Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 9 Apr 2024 10:07:57 +0700 Subject: [PATCH 01/16] feat: dynex config --- packages/engine/src/managers/impl.ts | 5 +++ packages/engine/src/proxy.ts | 13 ++++-- packages/shared/src/config/presetNetworks.ts | 42 ++++++++++++++++++++ packages/shared/src/engine/engineConsts.ts | 7 ++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/packages/engine/src/managers/impl.ts b/packages/engine/src/managers/impl.ts index 3a7c52898b1..75fd7d822fb 100644 --- a/packages/engine/src/managers/impl.ts +++ b/packages/engine/src/managers/impl.ts @@ -10,6 +10,7 @@ import { COINTYPE_COSMOS, COINTYPE_DOGE, COINTYPE_DOT, + COINTYPE_DYNEX, COINTYPE_ETC, COINTYPE_ETH, COINTYPE_FIL, @@ -38,6 +39,7 @@ import { IMPL_COSMOS, IMPL_DOGE, IMPL_DOT, + IMPL_DYNEX, IMPL_EVM, IMPL_FIL, IMPL_KASPA, @@ -99,6 +101,7 @@ const implToCoinTypes: Partial> = { [IMPL_NOSTR]: COINTYPE_NOSTR, [IMPL_NERVOS]: COINTYPE_NERVOS, [IMPL_NEURAI]: COINTYPE_NEURAI, + [IMPL_DYNEX]: COINTYPE_DYNEX, }; const coinTypeToImpl: Record = Object.fromEntries( @@ -140,6 +143,7 @@ const implToAccountType: Record = { [IMPL_NOSTR]: AccountType.VARIANT, [IMPL_NERVOS]: AccountType.SIMPLE, [IMPL_NEURAI]: AccountType.UTXO, + [IMPL_DYNEX]: AccountType.UTXO, }; function isCoinTypeCompatibleWithImpl(coinType: string, impl: string): boolean { @@ -176,6 +180,7 @@ const defaultCurveMap: Record = { [IMPL_NOSTR]: Curve.SECP256K1, [IMPL_NERVOS]: Curve.SECP256K1, [IMPL_NEURAI]: Curve.SECP256K1, + [IMPL_DYNEX]: Curve.ED25519, }; function getCurveByImpl(impl: string): string { diff --git a/packages/engine/src/proxy.ts b/packages/engine/src/proxy.ts index 01e3108fa58..7b876ecf351 100644 --- a/packages/engine/src/proxy.ts +++ b/packages/engine/src/proxy.ts @@ -32,6 +32,7 @@ import { IMPL_BCH, IMPL_BTC, IMPL_DOGE, + IMPL_DYNEX, IMPL_LTC, IMPL_NEURAI, IMPL_TBTC, @@ -77,9 +78,15 @@ function fromDBNetworkToChainInfo(dbNetwork: DBNetwork): ChainInfo { let code = dbNetwork.id; if ( - [IMPL_BTC, IMPL_DOGE, IMPL_LTC, IMPL_BCH, IMPL_TBTC, IMPL_NEURAI].includes( - dbNetwork.impl, - ) + [ + IMPL_BTC, + IMPL_DOGE, + IMPL_LTC, + IMPL_BCH, + IMPL_TBTC, + IMPL_NEURAI, + IMPL_DYNEX, + ].includes(dbNetwork.impl) ) { code = dbNetwork.impl; } diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index 356bccd447a..6403e030f28 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4476,6 +4476,48 @@ const serverPresetNetworks = [ 'createdAt': '2024-03-10T00:00:00.001Z', 'updatedAt': '2024-03-10T00:00:00.001Z', }, + { + 'balance2FeeDecimals': 0, + 'chainId': '0', + 'code': 'dynex', + 'decimals': 8, + 'id': 'dynex--0', + 'impl': 'dynex', + 'isTestnet': false, + 'logoURI': 'https://onekey-asset.com/assets/dynex/dynex.png', + 'name': 'Dynex', + 'rpcURLs': [ + { + 'url': 'http://node.dynexcoin.org:18333/', + }, + ], + 'shortcode': 'dnx', + 'shortname': 'DNX', + 'symbol': 'DNX', + 'feeMeta': { + 'code': 'dnx', + 'decimals': 8, + 'symbol': 'DNX', + }, + 'defaultEnabled': true, + 'priceConfigs': [ + { + 'channel': 'coingecko', + 'native': 'dynex', + }, + ], + 'explorers': [ + { + 'address': 'https://blockexplorer.dynexcoin.org/?wallet={wallet}', + 'block': 'https://blockexplorer.dynexcoin.org/?block={block}', + 'name': 'https://blockexplorer.dynexcoin.org', + 'transaction': 'https://blockexplorer.dynexcoin.org/?tx={transaction}', + }, + ], + 'status': 'LISTED', + 'createdAt': '2024-04-09T00:00:00.001Z', + 'updatedAt': '2024-04-09T00:00:00.001Z', + }, ] as unknown as IServerNetwork[]; serverPresetNetworks.unshift(FAKE_ALL_NETWORK); diff --git a/packages/shared/src/engine/engineConsts.ts b/packages/shared/src/engine/engineConsts.ts index 6465a9ba27c..3c6b8c30231 100644 --- a/packages/shared/src/engine/engineConsts.ts +++ b/packages/shared/src/engine/engineConsts.ts @@ -89,6 +89,9 @@ const COINTYPE_NERVOS = '309'; const IMPL_NEURAI = 'neurai'; const COINTYPE_NEURAI = '1900'; +const IMPL_DYNEX = 'dynex'; +const COINTYPE_DYNEX = '42514524'; + const IMPL_ALLNETWORKS = 'all'; const COINTYPE_ALLNETWORKS = '0000'; @@ -127,6 +130,7 @@ const SUPPORTED_IMPLS = new Set([ IMPL_LIGHTNING_TESTNET, IMPL_NERVOS, IMPL_NEURAI, + IMPL_DYNEX, IMPL_ALLNETWORKS, ]); @@ -157,6 +161,7 @@ const PRODUCTION_IMPLS = new Set([ IMPL_NEXA, IMPL_NERVOS, IMPL_NEURAI, + IMPL_DYNEX, IMPL_ALLNETWORKS, ]); @@ -226,6 +231,7 @@ export { COINTYPE_NOSTR, COINTYPE_NERVOS, COINTYPE_NEURAI, + COINTYPE_DYNEX, IMPL_ADA, IMPL_ALGO, IMPL_ALLNETWORKS, @@ -254,6 +260,7 @@ export { IMPL_NOSTR, IMPL_NERVOS, IMPL_NEURAI, + IMPL_DYNEX, INDEX_PLACEHOLDER, SEPERATOR, getSupportedImpls, From 8bcb608ae631f53da82a8c40db0d38c8b76178ef Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 9 Apr 2024 15:59:05 +0700 Subject: [PATCH 02/16] feat: init dynex files --- .../src/vaults/factory.createVaultSettings.ts | 6 + packages/engine/src/vaults/factory.ts | 9 ++ .../src/vaults/impl/dynex/KeyringHardware.ts | 42 ++++++ .../engine/src/vaults/impl/dynex/KeyringHd.ts | 4 + .../src/vaults/impl/dynex/KeyringImported.ts | 4 + .../src/vaults/impl/dynex/KeyringWatching.ts | 4 + .../engine/src/vaults/impl/dynex/Vault.ts | 131 ++++++++++++++++++ .../src/vaults/impl/dynex/VaultHelper.ts | 8 ++ .../vaults/impl/dynex/helper/ClientDynex.ts | 90 ++++++++++++ .../engine/src/vaults/impl/dynex/settings.ts | 20 +++ packages/engine/src/vaults/types.ts | 2 + .../SendEditFee/SendEditFeeStandardForm.tsx | 2 + packages/shared/src/config/presetNetworks.ts | 2 +- 13 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 packages/engine/src/vaults/impl/dynex/KeyringHardware.ts create mode 100644 packages/engine/src/vaults/impl/dynex/KeyringHd.ts create mode 100644 packages/engine/src/vaults/impl/dynex/KeyringImported.ts create mode 100644 packages/engine/src/vaults/impl/dynex/KeyringWatching.ts create mode 100644 packages/engine/src/vaults/impl/dynex/Vault.ts create mode 100644 packages/engine/src/vaults/impl/dynex/VaultHelper.ts create mode 100644 packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts create mode 100644 packages/engine/src/vaults/impl/dynex/settings.ts diff --git a/packages/engine/src/vaults/factory.createVaultSettings.ts b/packages/engine/src/vaults/factory.createVaultSettings.ts index 3cbd1173db0..fb91da1bc63 100644 --- a/packages/engine/src/vaults/factory.createVaultSettings.ts +++ b/packages/engine/src/vaults/factory.createVaultSettings.ts @@ -10,6 +10,7 @@ import { IMPL_COSMOS, IMPL_DOGE, IMPL_DOT, + IMPL_DYNEX, IMPL_EVM, IMPL_FIL, IMPL_KASPA, @@ -133,6 +134,11 @@ export function createVaultSettings(options: { if (impl === IMPL_NEURAI) { return require('./impl/neurai/settings').default as IVaultSettings; } + + if (impl === IMPL_DYNEX) { + return require('./impl/dynex/settings').default as IVaultSettings; + } + throw new OneKeyInternalError( `VaultSettings not found for: networkId=${options.networkId ?? ''}, impl=${ impl ?? '' diff --git a/packages/engine/src/vaults/factory.ts b/packages/engine/src/vaults/factory.ts index e5353171153..409273d52d1 100644 --- a/packages/engine/src/vaults/factory.ts +++ b/packages/engine/src/vaults/factory.ts @@ -10,6 +10,7 @@ import { IMPL_COSMOS, IMPL_DOGE, IMPL_DOT, + IMPL_DYNEX, IMPL_EVM, IMPL_FIL, IMPL_KASPA, @@ -49,6 +50,7 @@ import VaultHelperCfx from './impl/cfx/VaultHelper'; import VaultHelperCosmos from './impl/cosmos/VaultHelper'; import VaultHelperDoge from './impl/doge/VaultHelper'; import VaultHelperDot from './impl/dot/VaultHelper'; +import VaultHelperDynex from './impl/dynex/VaultHelper'; import VaultHelperEvm from './impl/evm/VaultHelper'; import VaultHelperFil from './impl/fil/VaultHelper'; import VaultHelperKaspa from './impl/kaspa/VaultHelper'; @@ -157,6 +159,9 @@ export async function createVaultHelperInstance( if (impl === IMPL_NEURAI) { return new VaultHelperNeurai(options); } + if (impl === IMPL_DYNEX) { + return new VaultHelperDynex(options); + } throw new OneKeyInternalError( `VaultHelper Class not found for: networkId=${options.networkId}, accountId=${options.accountId}`, ); @@ -310,6 +315,10 @@ export async function createVaultInstance(options: IVaultOptions) { const VaultNeurai = (await import('./impl/neurai/Vault')).default; vault = new VaultNeurai(options); } + if (network.impl === IMPL_DYNEX) { + const VaultDynex = (await import('./impl/dynex/Vault')).default; + vault = new VaultDynex(options); + } if (!vault) { throw new OneKeyInternalError( `Vault Class not found for: networkId=${options.networkId}, accountId=${options.accountId}`, diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts new file mode 100644 index 00000000000..ef3e3f54339 --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -0,0 +1,42 @@ +import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; + +import type { DBAccount } from '../../../types/account'; +import type { + IHardwareGetAddressParams, + IPrepareAccountsParams, + ISignCredentialOptions, + ISignedTxPro, + IUnsignedTxPro, +} from '../../types'; + +export class KeyringHardware extends KeyringHardwareBase { + override signTransaction( + unsignedTx: IUnsignedTxPro, + options: ISignCredentialOptions, + ): Promise { + throw new Error('Method not implemented.'); + } + + override signMessage( + messages: any[], + options: ISignCredentialOptions, + ): Promise { + throw new Error('Method not implemented.'); + } + + override prepareAccounts( + params: IPrepareAccountsParams, + ): Promise { + throw new Error('Method not implemented.'); + } + + override getAddress(params: IHardwareGetAddressParams): Promise { + throw new Error('Method not implemented.'); + } + + override batchGetAddress( + params: IHardwareGetAddressParams[], + ): Promise<{ path: string; address: string }[]> { + throw new Error('Method not implemented.'); + } +} diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHd.ts b/packages/engine/src/vaults/impl/dynex/KeyringHd.ts new file mode 100644 index 00000000000..2500ea39c5e --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/KeyringHd.ts @@ -0,0 +1,4 @@ +import { KeyringHdBase } from '../../keyring/KeyringHdBase'; + +// @ts-ignore +export class KeyringHd extends KeyringHdBase {} diff --git a/packages/engine/src/vaults/impl/dynex/KeyringImported.ts b/packages/engine/src/vaults/impl/dynex/KeyringImported.ts new file mode 100644 index 00000000000..caa78973973 --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/KeyringImported.ts @@ -0,0 +1,4 @@ +import { KeyringImportedBase } from '../../keyring/KeyringImportedBase'; + +// @ts-ignore +export class KeyringImported extends KeyringImportedBase {} diff --git a/packages/engine/src/vaults/impl/dynex/KeyringWatching.ts b/packages/engine/src/vaults/impl/dynex/KeyringWatching.ts new file mode 100644 index 00000000000..70caac9be4e --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/KeyringWatching.ts @@ -0,0 +1,4 @@ +import { KeyringWatchingBase } from '../../keyring/KeyringWatchingBase'; + +// @ts-ignore +export class KeyringWatching extends KeyringWatchingBase {} diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts new file mode 100644 index 00000000000..58c6774478a --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -0,0 +1,131 @@ +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ + +import { NotImplemented } from '../../../errors'; +import { VaultBase } from '../../VaultBase'; + +import { KeyringHardware } from './KeyringHardware'; +import { KeyringHd } from './KeyringHd'; +import { KeyringImported } from './KeyringImported'; +import { KeyringWatching } from './KeyringWatching'; +import settings from './settings'; + +import type { AccountCredentialType } from '../../../types/account'; +import type { PartialTokenInfo } from '../../../types/provider'; +import type { + IApproveInfo, + IDecodedTx, + IEncodedTx, + IEncodedTxUpdateOptions, + IFeeInfo, + IFeeInfoUnit, + ITransferInfo, + IUnsignedTxPro, +} from '../../types'; +import type { EVMDecodedItem } from '../evm/decoder/types'; +import { memoizee } from '@onekeyhq/shared/src/utils/cacheUtils'; +import { ClientDynex } from './helper/ClientDynex'; +import { getTimeDurationMs } from '@onekeyhq/kit/src/utils/helper'; + +export default class Vault extends VaultBase { + keyringMap = { + hd: KeyringHd, + hw: KeyringHardware, + imported: KeyringImported, + watching: KeyringWatching, + external: KeyringWatching, + }; + + settings = settings; + + private async getClient(url?: string): Promise { + const rpcURL = await this.getRpcUrl(); + return this.createClientFromURL(url ?? rpcURL); + } + + override async getClientEndpointStatus( + url: string, + ): Promise<{ responseTime: number; latestBlock: number }> { + const client = await this.getClient(url); + const start = performance.now(); + const latestBlock = await client.getBlockCount(); + return { responseTime: Math.floor(performance.now() - start), latestBlock }; + } + + override createClientFromURL = memoizee( + (rpcURL: string) => new ClientDynex(rpcURL), + { + max: 1, + maxAge: getTimeDurationMs({ minute: 3 }), + }, + ); + + override updateEncodedTxTokenApprove( + encodedTx: IEncodedTx, + amount: string, + ): Promise { + throw new Error('Method not implemented.'); + } + + override buildUnsignedTxFromEncodedTx( + encodedTx: IEncodedTx, + ): Promise { + throw new Error('Method not implemented.'); + } + + override fetchFeeInfo( + encodedTx: IEncodedTx, + signOnly?: boolean | undefined, + specifiedFeeRate?: string | undefined, + transferCount?: number | undefined, + ): Promise { + throw new Error('Method not implemented.'); + } + + override getExportedCredential( + password: string, + credentialType: AccountCredentialType, + ): Promise { + throw new Error('Method not implemented.'); + } + + override fetchTokenInfos( + tokenAddresses: string[], + ): Promise<(PartialTokenInfo | undefined)[]> { + throw new Error('Method not implemented.'); + } + + override updateEncodedTx( + encodedTx: IEncodedTx, + payload: any, + options: IEncodedTxUpdateOptions, + ): Promise { + throw new NotImplemented(); + } + + override decodeTx(encodedTx: IEncodedTx, payload?: any): Promise { + throw new NotImplemented(); + } + + override decodedTxToLegacy(decodedTx: IDecodedTx): Promise { + throw new NotImplemented(); + } + + override buildEncodedTxFromApprove( + approveInfo: IApproveInfo, + ): Promise { + throw new NotImplemented(); + } + + override buildEncodedTxFromTransfer( + transferInfo: ITransferInfo, + ): Promise { + throw new NotImplemented(); + } + + override attachFeeInfoToEncodedTx(params: { + encodedTx: IEncodedTx; + feeInfoValue: IFeeInfoUnit; + }): Promise { + return Promise.resolve(params.encodedTx); + } +} diff --git a/packages/engine/src/vaults/impl/dynex/VaultHelper.ts b/packages/engine/src/vaults/impl/dynex/VaultHelper.ts new file mode 100644 index 00000000000..d63a8da13de --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/VaultHelper.ts @@ -0,0 +1,8 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import { NotImplemented } from '../../../errors'; +import { VaultHelperBase } from '../../VaultHelperBase'; + +import type { IEncodedTx } from '../../types'; + +// @ts-ignore +export default class VaultHelper extends VaultHelperBase {} diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts new file mode 100644 index 00000000000..8607b73fdc9 --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -0,0 +1,90 @@ +// eslint-disable-next-line @typescript-eslint/naming-convention +import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import axios from 'axios'; +import BigNumber from 'bignumber.js'; +import { map, max } from 'lodash'; + +import { BaseClient } from '@onekeyhq/engine/src/client/BaseClient'; +import { JsonRPCRequest } from '@onekeyhq/shared/src/request/JsonRPCRequest'; + +import { getSolScanEndpoint } from '../../../../endpoint'; +import { TransactionStatus } from '../../../../types/provider'; + +import type { CoinInfo } from '../../../../types/chain'; +import type { + AddressInfo, + ClientInfo, + FeePricePerUnit, + PartialTokenInfo, +} from '../../../../types/provider'; +import type { ISolScanTokenMeta } from '../types'; +import type { AccountInfo, PublicKey } from '@solana/web3.js'; + +// eslint-disable-next-line @typescript-eslint/naming-convention +export enum RPC_METHODS { + GET_TRANSACTIONS_BY_ADDRESS = 'gettransactionsbyaddress', + GET_BALANCE_OF_ADDRESS = 'getbalanceofaddress', + GET_TRANSACTION = 'gettransaction', + GET_BLOCK_COUNT = 'getblockcount', +} +// eslint-disable-next-line @typescript-eslint/naming-convention +export enum PARAMS_ENCODINGS { + BASE64 = 'base64', + JSON_PARSED = 'jsonParsed', +} + +export class ClientDynex extends BaseClient { + readonly baseURL: string; + + readonly rpc: JsonRPCRequest; + + constructor(url: string) { + super(); + this.baseURL = url; + this.rpc = new JsonRPCRequest(`${url}/json_rpc`); + } + + async getBlockCount(): Promise { + const resp = await this.rpc.call<{ count: number }>( + RPC_METHODS.GET_BLOCK_COUNT, + [], + ); + return resp.count; + } + + async getTransactionStatuses( + txids: string[], + ): Promise> { + const calls: Array = txids.map((txid) => [ + RPC_METHODS.GET_TRANSACTION, + [ + txid, + { + encoding: PARAMS_ENCODINGS.JSON_PARSED, + maxSupportedTransactionVersion: 0, + }, + ], + ]); + const result = []; + const resp: Array<{ [key: string]: any } | undefined> = + await this.rpc.batchCall(calls); + for (const tx of resp) { + if (typeof tx !== 'undefined') { + if (tx === null) { + result.push(TransactionStatus.NOT_FOUND); + } else { + result.push( + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + tx.meta.err === null + ? TransactionStatus.CONFIRM_AND_SUCCESS + : TransactionStatus.CONFIRM_BUT_FAILED, + ); + } + } else { + result.push(undefined); + } + } + + return result; + } +} diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts new file mode 100644 index 00000000000..4010ec491cd --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -0,0 +1,20 @@ +import type { IVaultSettings } from '../../types'; + +const settings: IVaultSettings = Object.freeze({ + feeInfoEditable: false, + privateKeyExportEnabled: false, + tokenEnabled: true, + txCanBeReplaced: false, + + importedAccountEnabled: false, + hardwareAccountEnabled: true, + externalAccountEnabled: false, + watchingAccountEnabled: false, + softwareAccountDisabled: true, + + isUTXOModel: true, + + accountNameInfo: {}, +}); + +export default settings; diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index eaf136fb6ce..26f89fb809e 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -86,6 +86,8 @@ export type IVaultSettings = { externalAccountEnabled: boolean; hardwareAccountEnabled: boolean; + softwareAccountDisabled?: boolean; + addressDerivationDisabled?: boolean; validationRequired?: boolean; disabledInExtension?: boolean; diff --git a/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx b/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx index 976d39a5bfc..83857fba246 100644 --- a/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx +++ b/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx @@ -223,6 +223,8 @@ export function SendEditFeeStandardForm({ isEIP1559Fee, ]); + console.log('feeInfoPayload', feeInfoPayload); + return ( {selectedFeeInfo} diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index 6403e030f28..ed38cda3b19 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4488,7 +4488,7 @@ const serverPresetNetworks = [ 'name': 'Dynex', 'rpcURLs': [ { - 'url': 'http://node.dynexcoin.org:18333/', + 'url': 'http://node.dynexcoin.org:18333', }, ], 'shortcode': 'dnx', From 5b3a7ef7f8c466795c99c90e83b6f7118fe79eed Mon Sep 17 00:00:00 2001 From: weatherstar Date: Thu, 11 Apr 2024 20:47:55 +0700 Subject: [PATCH 03/16] fix: dnx get address --- package.json | 10 +- packages/engine/src/managers/derivation.ts | 2 + .../src/vaults/impl/dynex/KeyringHardware.ts | 144 +++++++++++++++--- .../engine/src/vaults/impl/dynex/Vault.ts | 21 ++- .../vaults/impl/dynex/helper/ClientDynex.ts | 144 +++++++++++++----- .../engine/src/vaults/impl/dynex/settings.ts | 18 ++- .../engine/src/vaults/impl/dynex/types.ts | 89 +++++++++++ packages/shared/src/config/appConfig.ts | 2 +- packages/shared/src/config/presetNetworks.ts | 4 +- packages/shared/src/engine/engineConsts.ts | 2 +- yarn.lock | 104 ++++++------- 11 files changed, 413 insertions(+), 127 deletions(-) create mode 100644 packages/engine/src/vaults/impl/dynex/types.ts diff --git a/package.json b/package.json index 52b5e1955da..d94a6084333 100644 --- a/package.json +++ b/package.json @@ -70,11 +70,11 @@ "@onekeyfe/cross-inpage-provider-injected": "1.1.57", "@onekeyfe/cross-inpage-provider-types": "1.1.57", "@onekeyfe/extension-bridge-hosted": "1.1.57", - "@onekeyfe/hd-ble-sdk": "0.3.42", - "@onekeyfe/hd-core": "0.3.42", - "@onekeyfe/hd-shared": "0.3.42", - "@onekeyfe/hd-transport": "0.3.42", - "@onekeyfe/hd-web-sdk": "0.3.42", + "@onekeyfe/hd-ble-sdk": "0.3.43-alpha.0", + "@onekeyfe/hd-core": "0.3.43-alpha.0", + "@onekeyfe/hd-shared": "0.3.43-alpha.0", + "@onekeyfe/hd-transport": "0.3.43-alpha.0", + "@onekeyfe/hd-web-sdk": "0.3.43-alpha.0", "@onekeyfe/onekey-cross-webview": "1.1.57", "@starcoin/starcoin": "2.1.5", "@web3-react/core": "8.0.35-beta.0", diff --git a/packages/engine/src/managers/derivation.ts b/packages/engine/src/managers/derivation.ts index 1cd2fbc5182..44ffc651b63 100644 --- a/packages/engine/src/managers/derivation.ts +++ b/packages/engine/src/managers/derivation.ts @@ -8,6 +8,7 @@ import { COINTYPE_COSMOS, COINTYPE_DOGE, COINTYPE_DOT, + COINTYPE_DYNEX, COINTYPE_ETC, COINTYPE_ETH, COINTYPE_FIL, @@ -125,6 +126,7 @@ const derivationPathTemplates: Record = { [COINTYPE_NOSTR]: `m/44'/${COINTYPE_NOSTR}'/${INCREMENT_LEVEL_TAG}'`, [COINTYPE_NERVOS]: `m/44'/${COINTYPE_NERVOS}'/0'/0/${INCREMENT_LEVEL_TAG}`, [COINTYPE_NEURAI]: `m/44'/${COINTYPE_NEURAI}'/${INCREMENT_LEVEL_TAG}'`, + [COINTYPE_DYNEX]: `m/44'/${COINTYPE_DYNEX}'/0'/0'/${INCREMENT_LEVEL_TAG}'`, }; function getDerivationPaths( diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index ef3e3f54339..e635a861778 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -1,42 +1,146 @@ +import { isNil } from 'lodash'; + +import { convertDeviceError } from '@onekeyhq/shared/src/device/deviceErrorUtils'; +import { + IMPL_DYNEX as COIN_IMPL, + COINTYPE_DYNEX as COIN_TYPE, +} from '@onekeyhq/shared/src/engine/engineConsts'; +import debugLogger from '@onekeyhq/shared/src/logger/debugLogger'; + +import { OneKeyHardwareError } from '../../../errors'; +import { slicePathTemplate } from '../../../managers/derivation'; +import { getAccountNameInfoByImpl } from '../../../managers/impl'; +import { AccountType, type DBAccount } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; -import type { DBAccount } from '../../../types/account'; +import type { DBSimpleAccount } from '../../../types/account'; import type { IHardwareGetAddressParams, - IPrepareAccountsParams, + IPrepareHardwareAccountsParams, ISignCredentialOptions, ISignedTxPro, IUnsignedTxPro, } from '../../types'; export class KeyringHardware extends KeyringHardwareBase { - override signTransaction( - unsignedTx: IUnsignedTxPro, - options: ISignCredentialOptions, - ): Promise { - throw new Error('Method not implemented.'); + override async prepareAccounts( + params: IPrepareHardwareAccountsParams, + ): Promise { + const { indexes, names, template } = params; + const { pathPrefix } = slicePathTemplate(template); + const paths = indexes.map((index) => `${pathPrefix}/${index}'`); + const showOnOneKey = false; + const HardwareSDK = await this.getHardwareSDKInstance(); + const { connectId, deviceId } = await this.getHardwareInfo(); + const passphraseState = await this.getWalletPassphraseState(); + + const { prefix } = getAccountNameInfoByImpl(COIN_IMPL).default; + let addressesResponse; + try { + addressesResponse = await HardwareSDK.dnxGetAddress(connectId, deviceId, { + bundle: paths.map((path) => ({ + path, + showOnOneKey, + })), + ...passphraseState, + }); + } catch (error: any) { + debugLogger.common.error(error); + throw new OneKeyHardwareError(error); + } + + if (!addressesResponse.success) { + debugLogger.common.error(addressesResponse.payload); + throw convertDeviceError(addressesResponse.payload); + } + + const ret: DBSimpleAccount[] = []; + let index = 0; + for (const addressInfo of addressesResponse.payload) { + const { address, path } = addressInfo; + + if (isNil(address)) { + throw new OneKeyHardwareError({ message: 'Get Dynex Address error.' }); + } + const name = (names || [])[index] || `${prefix} #${indexes[index] + 1}`; + ret.push({ + id: `${this.walletId}--${path}`, + name, + type: AccountType.SIMPLE, + path, + coinType: COIN_TYPE, + pub: '', + address, + }); + index += 1; + } + return ret; } - override signMessage( - messages: any[], - options: ISignCredentialOptions, - ): Promise { - throw new Error('Method not implemented.'); + override async getAddress( + params: IHardwareGetAddressParams, + ): Promise { + const HardwareSDK = await this.getHardwareSDKInstance(); + const { connectId, deviceId } = await this.getHardwareInfo(); + const passphraseState = await this.getWalletPassphraseState(); + const addressesResponse = await HardwareSDK.dnxGetAddress( + connectId, + deviceId, + { + path: params.path, + showOnOneKey: params.showOnOneKey, + ...passphraseState, + }, + ); + + if (addressesResponse.success && !!addressesResponse.payload?.address) { + return addressesResponse.payload.address; + } + throw convertDeviceError(addressesResponse.payload); } - override prepareAccounts( - params: IPrepareAccountsParams, - ): Promise { - throw new Error('Method not implemented.'); + override async batchGetAddress( + params: IHardwareGetAddressParams[], + ): Promise<{ path: string; address: string }[]> { + const HardwareSDK = await this.getHardwareSDKInstance(); + const { connectId, deviceId } = await this.getHardwareInfo(); + const passphraseState = await this.getWalletPassphraseState(); + + try { + const addressesResponse = await HardwareSDK.dnxGetAddress( + connectId, + deviceId, + { + bundle: params.map(({ path, showOnOneKey }) => ({ + path, + showOnOneKey: !!showOnOneKey, + })), + ...passphraseState, + }, + ); + if (!addressesResponse.success) { + debugLogger.common.error(addressesResponse.payload); + throw convertDeviceError(addressesResponse.payload); + } + + return addressesResponse.payload.map((item) => ({ + path: item.path ?? '', + address: item.address ?? '', + })); + } catch (error: any) { + debugLogger.common.error(error); + throw new OneKeyHardwareError(error); + } } - override getAddress(params: IHardwareGetAddressParams): Promise { + override signTransaction( + unsignedTx: IUnsignedTxPro, + options: ISignCredentialOptions, + ): Promise { throw new Error('Method not implemented.'); } - override batchGetAddress( - params: IHardwareGetAddressParams[], - ): Promise<{ path: string; address: string }[]> { + override signMessage(): Promise { throw new Error('Method not implemented.'); } } diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index 58c6774478a..a34e3f4b1ce 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -1,8 +1,12 @@ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ +import { getTimeDurationMs } from '@onekeyhq/kit/src/utils/helper'; +import { memoizee } from '@onekeyhq/shared/src/utils/cacheUtils'; + import { NotImplemented } from '../../../errors'; import { VaultBase } from '../../VaultBase'; +import { ClientDynex } from './helper/ClientDynex'; import { KeyringHardware } from './KeyringHardware'; import { KeyringHd } from './KeyringHd'; import { KeyringImported } from './KeyringImported'; @@ -22,9 +26,7 @@ import type { IUnsignedTxPro, } from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; -import { memoizee } from '@onekeyhq/shared/src/utils/cacheUtils'; -import { ClientDynex } from './helper/ClientDynex'; -import { getTimeDurationMs } from '@onekeyhq/kit/src/utils/helper'; +import type { BigNumber } from 'bignumber.js'; export default class Vault extends VaultBase { keyringMap = { @@ -59,6 +61,19 @@ export default class Vault extends VaultBase { }, ); + override async getBalances( + requests: Array<{ address: string; tokenAddress?: string }>, + ): Promise<(BigNumber | undefined)[]> { + const client = await this.getClient(); + const balances: (BigNumber | undefined)[] = []; + for (let i = 0; i < requests.length; i += 1) { + const balance = await client.getBalanceOfAddress(requests[i].address); + balances.push(balance); + } + + return balances; + } + override updateEncodedTxTokenApprove( encodedTx: IEncodedTx, amount: string, diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts index 8607b73fdc9..d697668f275 100644 --- a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -1,24 +1,22 @@ -// eslint-disable-next-line @typescript-eslint/naming-convention -import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import axios from 'axios'; import BigNumber from 'bignumber.js'; -import { map, max } from 'lodash'; import { BaseClient } from '@onekeyhq/engine/src/client/BaseClient'; import { JsonRPCRequest } from '@onekeyhq/shared/src/request/JsonRPCRequest'; -import { getSolScanEndpoint } from '../../../../endpoint'; -import { TransactionStatus } from '../../../../types/provider'; +import { NotImplemented } from '../../../../errors'; import type { CoinInfo } from '../../../../types/chain'; import type { AddressInfo, ClientInfo, FeePricePerUnit, - PartialTokenInfo, + TransactionStatus, } from '../../../../types/provider'; -import type { ISolScanTokenMeta } from '../types'; -import type { AccountInfo, PublicKey } from '@solana/web3.js'; +import type { + IOnChainBalance, + IOnChainTransaction, + IOnChainTransactionsItem, +} from '../types'; // eslint-disable-next-line @typescript-eslint/naming-convention export enum RPC_METHODS { @@ -26,6 +24,7 @@ export enum RPC_METHODS { GET_BALANCE_OF_ADDRESS = 'getbalanceofaddress', GET_TRANSACTION = 'gettransaction', GET_BLOCK_COUNT = 'getblockcount', + VALIDATE_ADDRESS = 'validateaddress', } // eslint-disable-next-line @typescript-eslint/naming-convention export enum PARAMS_ENCODINGS { @@ -44,6 +43,18 @@ export class ClientDynex extends BaseClient { this.rpc = new JsonRPCRequest(`${url}/json_rpc`); } + checkDynexNodeResponse({ + resp, + method, + }: { + resp: { status: string }; + method: string; + }) { + if (!resp || !resp.status || resp.status.toLowerCase() !== 'ok') { + throw new Error(`${method}: Failed to get response from Dynex node`); + } + } + async getBlockCount(): Promise { const resp = await this.rpc.call<{ count: number }>( RPC_METHODS.GET_BLOCK_COUNT, @@ -52,39 +63,90 @@ export class ClientDynex extends BaseClient { return resp.count; } - async getTransactionStatuses( - txids: string[], - ): Promise> { - const calls: Array = txids.map((txid) => [ - RPC_METHODS.GET_TRANSACTION, - [ - txid, - { - encoding: PARAMS_ENCODINGS.JSON_PARSED, - maxSupportedTransactionVersion: 0, - }, - ], - ]); - const result = []; - const resp: Array<{ [key: string]: any } | undefined> = - await this.rpc.batchCall(calls); - for (const tx of resp) { - if (typeof tx !== 'undefined') { - if (tx === null) { - result.push(TransactionStatus.NOT_FOUND); - } else { - result.push( - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - tx.meta.err === null - ? TransactionStatus.CONFIRM_AND_SUCCESS - : TransactionStatus.CONFIRM_BUT_FAILED, - ); - } - } else { - result.push(undefined); - } + async getBalanceOfAddress(address: string): Promise { + const resp = await this.rpc.call<{ + status: string; + balance: IOnChainBalance; + }>(RPC_METHODS.GET_BALANCE_OF_ADDRESS, { + address, + }); + + try { + this.checkDynexNodeResponse({ resp, method: 'getBalanceOfAddress' }); + } catch { + return new BigNumber(0); } - return result; + return new BigNumber(resp.balance.balance ?? 0); + } + + async validateAddress(address: string): Promise { + const resp = await this.rpc.call<{ + status: string; + isvalid: boolean; + }>(RPC_METHODS.VALIDATE_ADDRESS, { + address, + }); + + try { + this.checkDynexNodeResponse({ resp, method: 'getBalanceOfAddress' }); + } catch { + return false; + } + + return resp.isvalid; + } + + async getTransaction(txid: string): Promise { + const resp = await this.rpc.call<{ + status: string; + transaction: IOnChainTransaction; + }>(RPC_METHODS.GET_TRANSACTION, { + hash: txid, + }); + this.checkDynexNodeResponse({ resp, method: 'getTransaction' }); + + return resp.transaction; + } + + async getTransactionsByAddress( + address: string, + ): Promise { + const resp = await this.rpc.call<{ + status: string; + transactions: IOnChainTransactionsItem[]; + }>(RPC_METHODS.GET_TRANSACTIONS_BY_ADDRESS, { + address, + }); + + this.checkDynexNodeResponse({ resp, method: 'getTransactionsByAddress' }); + + return resp.transactions; + } + + override getInfo(): Promise { + throw new NotImplemented(); + } + + override getAddresses(): Promise<(AddressInfo | undefined)[]> { + throw new NotImplemented(); + } + + override getTransactionStatuses(): Promise< + (TransactionStatus | undefined)[] + > { + throw new NotImplemented(); + } + + override getFeePricePerUnit(): Promise { + throw new NotImplemented(); + } + + override broadcastTransaction(): Promise { + throw new NotImplemented(); + } + + override getBalances(): Promise<(BigNumber | undefined)[]> { + throw new NotImplemented(); } } diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts index 4010ec491cd..dc5795f422a 100644 --- a/packages/engine/src/vaults/impl/dynex/settings.ts +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -1,3 +1,8 @@ +import { + COINTYPE_DYNEX, + INDEX_PLACEHOLDER, +} from '@onekeyhq/shared/src/engine/engineConsts'; + import type { IVaultSettings } from '../../types'; const settings: IVaultSettings = Object.freeze({ @@ -12,9 +17,18 @@ const settings: IVaultSettings = Object.freeze({ watchingAccountEnabled: false, softwareAccountDisabled: true, - isUTXOModel: true, + isUTXOModel: false, + + hideInAllNetworksMode: true, - accountNameInfo: {}, + accountNameInfo: { + default: { + prefix: 'DNX', + category: `44'/${COINTYPE_DYNEX}'`, + template: `m/44'/${COINTYPE_DYNEX}'/0'/0'/${INDEX_PLACEHOLDER}'`, + coinType: COINTYPE_DYNEX, + }, + }, }); export default settings; diff --git a/packages/engine/src/vaults/impl/dynex/types.ts b/packages/engine/src/vaults/impl/dynex/types.ts new file mode 100644 index 00000000000..d2f5402244f --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/types.ts @@ -0,0 +1,89 @@ +export type IOnChainTransaction = { + address_from: string; + address_to: string[]; + amount: string[]; + blockHash: string; + blockIndex: number; + extra: { + nonce: string[]; + publicKey: string; + raw: string; + }; + fee: number; + hash: string; + inBlockchain: boolean; + inputs: { + data: { + input: { + amount: number; + k_image: string; + key_offsets: number[]; + }; + mixin: number; + outputs: { + number: number; + transactionHash: string; + }[]; + }; + type: string; + }[]; + mixin: number; + outputs: { + globalIndex: number; + output: { + amount: number; + target: { + data: { + key: string; + }; + type: string; + }; + }; + }[]; + outputs_with_address: { + address_to: string; + globalIndex: number; + output: { + amount: number; + target: { + data: { + key: string; + }; + type: string; + }; + }; + }[]; + paymentId: string; + signatures: { + first: number; + second: string; + }[]; + signaturesSize: number; + size: number; + timestamp: number; + totalInputsAmount: number; + totalOutputsAmount: number; + unlockTime: number; + version: number; +}; + +export type IOnChainTransactionsItem = { + amount: string[]; + amount_out: number; + fee: number; + from_address: string; + hash: string; + height: number; + size: number; + timestamp: number; + to_address: string[]; +}; + +export type IOnChainBalance = { + amount_in: number; + amount_out: number; + balance: number; + fees: number; + legacy_wallet: boolean; + wallet: string; +}; diff --git a/packages/shared/src/config/appConfig.ts b/packages/shared/src/config/appConfig.ts index bb2b93ead3e..57e182b0157 100644 --- a/packages/shared/src/config/appConfig.ts +++ b/packages/shared/src/config/appConfig.ts @@ -14,7 +14,7 @@ export const HARDWARE_SDK_IFRAME_SRC_ONEKEYSO = export const HARDWARE_SDK_IFRAME_SRC_ONEKEYCN = process.env.HARDWARE_SDK_CONNECT_SRC_ONEKEYCN || 'https://jssdk.onekeycn.com'; -export const HARDWARE_SDK_VERSION = '0.3.42'; +export const HARDWARE_SDK_VERSION = '0.3.43-alpha.0'; export const HARDWARE_BRIDGE_DOWNLOAD_URL = 'https://onekey.so/download/?client=bridge'; diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index ed38cda3b19..8bca9a5edbc 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4488,7 +4488,7 @@ const serverPresetNetworks = [ 'name': 'Dynex', 'rpcURLs': [ { - 'url': 'http://node.dynexcoin.org:18333', + 'url': 'https://node.onekeytest.com/dynex', }, ], 'shortcode': 'dnx', @@ -4508,7 +4508,7 @@ const serverPresetNetworks = [ ], 'explorers': [ { - 'address': 'https://blockexplorer.dynexcoin.org/?wallet={wallet}', + 'address': 'https://blockexplorer.dynexcoin.org/?wallet={address}', 'block': 'https://blockexplorer.dynexcoin.org/?block={block}', 'name': 'https://blockexplorer.dynexcoin.org', 'transaction': 'https://blockexplorer.dynexcoin.org/?tx={transaction}', diff --git a/packages/shared/src/engine/engineConsts.ts b/packages/shared/src/engine/engineConsts.ts index 3c6b8c30231..bed4f36aeed 100644 --- a/packages/shared/src/engine/engineConsts.ts +++ b/packages/shared/src/engine/engineConsts.ts @@ -90,7 +90,7 @@ const IMPL_NEURAI = 'neurai'; const COINTYPE_NEURAI = '1900'; const IMPL_DYNEX = 'dynex'; -const COINTYPE_DYNEX = '42514524'; +const COINTYPE_DYNEX = '29538'; const IMPL_ALLNETWORKS = 'all'; const COINTYPE_ALLNETWORKS = '0000'; diff --git a/yarn.lock b/yarn.lock index 69506d732ab..db8409720e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6270,23 +6270,23 @@ __metadata: languageName: node linkType: hard -"@onekeyfe/hd-ble-sdk@npm:0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-ble-sdk@npm:0.3.42" +"@onekeyfe/hd-ble-sdk@npm:0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-ble-sdk@npm:0.3.43-alpha.0" dependencies: - "@onekeyfe/hd-core": ^0.3.42 - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport-react-native": ^0.3.42 - checksum: a84e3b09b6907c031d6a007f04ade9708714eeff337e6f88f7327f6d9dd8006eb143a34b81a826b31807d15ced288d7df6cfac0662c00654c7da7c839629af42 + "@onekeyfe/hd-core": ^0.3.43-alpha.0 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport-react-native": ^0.3.43-alpha.0 + checksum: 1c1028397588a21be770965ae2cf75a63ab8c370a1720c6b06ad36ed270b17eaa7050f4a7f1d159b66d46349bcc25ea7ef7403d4499f9522ae415956e7237e86 languageName: node linkType: hard -"@onekeyfe/hd-core@npm:0.3.42, @onekeyfe/hd-core@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-core@npm:0.3.42" +"@onekeyfe/hd-core@npm:0.3.43-alpha.0, @onekeyfe/hd-core@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-core@npm:0.3.43-alpha.0" dependencies: - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport": ^0.3.42 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport": ^0.3.43-alpha.0 axios: ^0.27.2 bignumber.js: ^9.0.2 bytebuffer: ^5.0.1 @@ -6296,71 +6296,71 @@ __metadata: peerDependencies: "@noble/hashes": ^1.1.3 ripple-keypairs: ^1.1.4 - checksum: 43a209cd93a11dd28d97b8bcf2ec6ab0cb2ffc7618525875339097f6cab5fddee946869d4ec37dd5f32221e1268287ae5aba51aebcbc9147734b013c6160b0bd + checksum: 470c400c973933348a8c8d9b49b3be8ad7fd4f82548752ac66522f65e5dd010fb826215d6ecee9834df1311ccdacbce098fc95c214dc69822b671173d53815f7 languageName: node linkType: hard -"@onekeyfe/hd-shared@npm:0.3.42, @onekeyfe/hd-shared@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-shared@npm:0.3.42" - checksum: f71d4cce6db2b647be0d159235684774d9411a0f6450d3c7dd63da3b547b0b5c09d3eabee10d34032a6882dd5131aeb259e3d56fe5fb3fb4b842832e64b96bec +"@onekeyfe/hd-shared@npm:0.3.43-alpha.0, @onekeyfe/hd-shared@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-shared@npm:0.3.43-alpha.0" + checksum: ef0b37b1e6906d9d76d5dcad68342ac18e94ef0e082e71068e4fe96dfafc5e2d82b15267e22f5c5adbc6a2a56084a91a7b41caf586af077eed24972b95528b71 languageName: node linkType: hard -"@onekeyfe/hd-transport-http@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-transport-http@npm:0.3.42" +"@onekeyfe/hd-transport-http@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-transport-http@npm:0.3.43-alpha.0" dependencies: - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport": ^0.3.42 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport": ^0.3.43-alpha.0 axios: ^0.27.2 - checksum: 7a2623d503d5df73e4fa2dc6d99522cf9efc99036372e4e7ce14432b5ae8342e4636b85a6f60863d9644c3a5a379943873d3168e8f9705237e120f0d066af504 + checksum: 5cf13f2b1ef9c87a3d0a243453649dca0d34150a548537b361f753ec6ab25c3dbfa2ebcb7f9624f5bd6bb8095d8116f1a3bfe880049ceec4e8892794df2a21f1 languageName: node linkType: hard -"@onekeyfe/hd-transport-react-native@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-transport-react-native@npm:0.3.42" +"@onekeyfe/hd-transport-react-native@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-transport-react-native@npm:0.3.43-alpha.0" dependencies: - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport": ^0.3.42 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport": ^0.3.43-alpha.0 "@onekeyfe/react-native-ble-plx": 3.0.0 react-native-ble-manager: ^8.1.0 - checksum: efce25000f6fd604a3180fa3309790544bcf70fe23edf165058d296268d53ce68ef90f46637939499b8cd4e32e8b2a629aa68b0fe4b45b6c6b3c3055da661d28 + checksum: 5031514ecd449e71a2c6d05bc428552e90d951dc953704d2fad8f79a64e3bf96b5b03795ab118f64a95acf4581a3e6beaad03f8b668195ea63cce28f03324e8f languageName: node linkType: hard -"@onekeyfe/hd-transport-webusb@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-transport-webusb@npm:0.3.42" +"@onekeyfe/hd-transport-webusb@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-transport-webusb@npm:0.3.43-alpha.0" dependencies: - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport": ^0.3.42 - checksum: 184ccaac15ecf1c127ab3e295f8b2384069ead5ed2388866def27e3bf8fbd9e3f315406a987c1d5204289a1c0518e4554431d92fd98185afdc8101546423e954 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport": ^0.3.43-alpha.0 + checksum: 3b5d6de340fd89132f14b39274af20761e71b329941f2cde62a62712c85e98f1fe57eec41b1391a16e60c2cac8ff335879e34e5d3cc25da003079c0ecb636cd8 languageName: node linkType: hard -"@onekeyfe/hd-transport@npm:0.3.42, @onekeyfe/hd-transport@npm:^0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-transport@npm:0.3.42" +"@onekeyfe/hd-transport@npm:0.3.43-alpha.0, @onekeyfe/hd-transport@npm:^0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-transport@npm:0.3.43-alpha.0" dependencies: bytebuffer: ^5.0.1 long: ^4.0.0 protobufjs: ^6.11.2 - checksum: 25ea6dd6d78f7a3c929a01d16533ed39ddefa5c60b6f276462a6cb35ee282948468971498c3d8650bad48bfefa04ac04648f1a1da5888ce65ea6336af08f6a4f + checksum: 4eca9c2d1101801d8d64a4261671dce2a04605107804bae6c4bd7545cb19dc6cfb21cea215bd0621b384f1b75de22201dc312d0972a860a1602276debf66156d languageName: node linkType: hard -"@onekeyfe/hd-web-sdk@npm:0.3.42": - version: 0.3.42 - resolution: "@onekeyfe/hd-web-sdk@npm:0.3.42" +"@onekeyfe/hd-web-sdk@npm:0.3.43-alpha.0": + version: 0.3.43-alpha.0 + resolution: "@onekeyfe/hd-web-sdk@npm:0.3.43-alpha.0" dependencies: "@onekeyfe/cross-inpage-provider-core": ^0.0.17 - "@onekeyfe/hd-core": ^0.3.42 - "@onekeyfe/hd-shared": ^0.3.42 - "@onekeyfe/hd-transport-http": ^0.3.42 - "@onekeyfe/hd-transport-webusb": ^0.3.42 - checksum: 6938ed51017ea4b57a13f5c6843b1c62239a938abf7a06af629ce12fcd14fc88a861176a5f87f50f4d413b1053782d8f6636934c6110e5a9b1f98f0fc3c112c9 + "@onekeyfe/hd-core": ^0.3.43-alpha.0 + "@onekeyfe/hd-shared": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport-http": ^0.3.43-alpha.0 + "@onekeyfe/hd-transport-webusb": ^0.3.43-alpha.0 + checksum: 6bfb7a3ed22ee417cde641c38a9eeb33b8e912c543183c36ff2f9177c84196d7e1c7a233dc9eb740f6028b81ab48cebe8f0851257c55b705dac8f99efd4f9c0a languageName: node linkType: hard @@ -6647,11 +6647,11 @@ __metadata: "@onekeyfe/cross-inpage-provider-injected": 1.1.57 "@onekeyfe/cross-inpage-provider-types": 1.1.57 "@onekeyfe/extension-bridge-hosted": 1.1.57 - "@onekeyfe/hd-ble-sdk": 0.3.42 - "@onekeyfe/hd-core": 0.3.42 - "@onekeyfe/hd-shared": 0.3.42 - "@onekeyfe/hd-transport": 0.3.42 - "@onekeyfe/hd-web-sdk": 0.3.42 + "@onekeyfe/hd-ble-sdk": 0.3.43-alpha.0 + "@onekeyfe/hd-core": 0.3.43-alpha.0 + "@onekeyfe/hd-shared": 0.3.43-alpha.0 + "@onekeyfe/hd-transport": 0.3.43-alpha.0 + "@onekeyfe/hd-web-sdk": 0.3.43-alpha.0 "@onekeyfe/onekey-cross-webview": 1.1.57 "@open-wc/webpack-import-meta-loader": ^0.4.7 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.10 From 42da6cffc8186e307b34a039c6be32a5210cced3 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Thu, 18 Apr 2024 09:52:58 +0800 Subject: [PATCH 04/16] feat: serialize tx --- .../src/vaults/impl/dynex/KeyringHardware.ts | 114 ++++++- .../engine/src/vaults/impl/dynex/Vault.ts | 317 +++++++++++++++--- .../vaults/impl/dynex/helper/ClientDynex.ts | 46 ++- .../engine/src/vaults/impl/dynex/settings.ts | 2 + .../engine/src/vaults/impl/dynex/types.ts | 29 ++ .../engine/src/vaults/impl/dynex/utils.ts | 31 ++ packages/engine/src/vaults/types.ts | 8 +- .../src/views/Send/modals/PreSendAddress.tsx | 4 + .../views/Send/utils/useFeeInfoPayload.tsx | 14 +- packages/shared/src/config/presetNetworks.ts | 6 +- 10 files changed, 514 insertions(+), 57 deletions(-) create mode 100644 packages/engine/src/vaults/impl/dynex/utils.ts diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index e635a861778..d04c0711bde 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -1,3 +1,4 @@ +import BigNumber from 'bignumber.js'; import { isNil } from 'lodash'; import { convertDeviceError } from '@onekeyhq/shared/src/device/deviceErrorUtils'; @@ -13,14 +14,16 @@ import { getAccountNameInfoByImpl } from '../../../managers/impl'; import { AccountType, type DBAccount } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; +import { encodeVarInt, encodeVarIntLittleEndian } from './utils'; + import type { DBSimpleAccount } from '../../../types/account'; import type { IHardwareGetAddressParams, IPrepareHardwareAccountsParams, - ISignCredentialOptions, ISignedTxPro, IUnsignedTxPro, } from '../../types'; +import type { IEncodedTxDynex } from './types'; export class KeyringHardware extends KeyringHardwareBase { override async prepareAccounts( @@ -133,11 +136,114 @@ export class KeyringHardware extends KeyringHardwareBase { } } - override signTransaction( + override async signTransaction( unsignedTx: IUnsignedTxPro, - options: ISignCredentialOptions, ): Promise { - throw new Error('Method not implemented.'); + debugLogger.common.info('signTransaction', unsignedTx); + const { payload } = unsignedTx; + const encodedTx = payload.encodedTx as IEncodedTxDynex; + const dbAccount = await this.getDbAccount(); + const network = await this.getNetwork(); + const params = { + path: dbAccount.path, + inputs: encodedTx.inputs, + toAddress: encodedTx.to, + amount: new BigNumber(encodedTx.amount) + .shiftedBy(network.decimals) + .toFixed(), + fee: new BigNumber(encodedTx.fee) + .shiftedBy(network.feeDecimals) + .toFixed(), + paymentIdHex: encodedTx.paymentId, + }; + const { connectId, deviceId } = await this.getHardwareInfo(); + const passphraseState = await this.getWalletPassphraseState(); + + const HardwareSDK = await this.getHardwareSDKInstance(); + const response = await HardwareSDK.dnxSignTransaction(connectId, deviceId, { + ...passphraseState, + ...(params as unknown as any), + }); + + if (response.success) { + const { txKey, computedKeyImages, signatures, outputKeys } = + response.payload; + console.log('signTransaction response', response.payload); + + let rawTx = ''; + + const version = '01'; + const unlockTime = '00'; + const inputTypeTag = '02'; + const outputTypeTag = '02'; + const txPubkeyTag = '01'; + const fromAddressTag = '04'; + const toAddressTag = '05'; + const amountTag = '06'; + const txSecTag = '07'; + const { decodedFrom, decodedTo } = encodedTx; + const totalInputAmountBN = params.inputs.reduce( + (acc, input) => acc.plus(input.amount), + new BigNumber(0), + ); + const chargeAmount = totalInputAmountBN + .minus(params.amount) + .minus(params.fee); + debugger; + rawTx += version; + rawTx += unlockTime; + rawTx += encodeVarInt(params.inputs.length); + + for (let i = 0; i < params.inputs.length; i += 1) { + const input = params.inputs[i]; + rawTx += inputTypeTag; + rawTx += encodeVarInt(input.amount); + rawTx += encodeVarInt(1); + rawTx += encodeVarInt(input.globalIndex); + rawTx += computedKeyImages[i]; + } + + rawTx += encodeVarInt(outputKeys.length); + + for (let i = 0; i < outputKeys.length; i += 1) { + const outputKey = outputKeys[i]; + rawTx += encodeVarInt( + i === outputKeys.length - 1 + ? chargeAmount.toNumber() + : Number(params.amount), + ); + rawTx += outputTypeTag; + rawTx += outputKey; + } + + rawTx += txPubkeyTag; + rawTx += txKey.ephemeralTxPubKey; + + rawTx += fromAddressTag; + rawTx += decodedFrom.spend; + rawTx += decodedFrom.view; + + rawTx += toAddressTag; + rawTx += decodedTo.spend; + rawTx += decodedTo.view; + + rawTx += amountTag; + rawTx += encodeVarIntLittleEndian(Number(params.amount)); + + rawTx += txSecTag; + rawTx += txKey.ephemeralTxSecKey; + + for (const signature of signatures) { + rawTx += signature; + } + + return { + txid: '', + rawTx, + }; + } + + throw convertDeviceError(response.payload); } override signMessage(): Promise { diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index a34e3f4b1ce..bf915f85c03 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -1,9 +1,25 @@ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/require-await */ +import BigNumber from 'bignumber.js'; + import { getTimeDurationMs } from '@onekeyhq/kit/src/utils/helper'; +import debugLogger from '@onekeyhq/shared/src/logger/debugLogger'; import { memoizee } from '@onekeyhq/shared/src/utils/cacheUtils'; -import { NotImplemented } from '../../../errors'; +import { InvalidAddress, NotImplemented } from '../../../errors'; +import { + type IApproveInfo, + type IDecodedTx, + IDecodedTxActionType, + IDecodedTxDirection, + type IDecodedTxLegacy, + IDecodedTxStatus, + type IEncodedTxUpdateOptions, + type IFeeInfo, + type IFeeInfoUnit, + type ITransferInfo, + type IUnsignedTxPro, +} from '../../types'; import { VaultBase } from '../../VaultBase'; import { ClientDynex } from './helper/ClientDynex'; @@ -15,18 +31,11 @@ import settings from './settings'; import type { AccountCredentialType } from '../../../types/account'; import type { PartialTokenInfo } from '../../../types/provider'; -import type { - IApproveInfo, - IDecodedTx, - IEncodedTx, - IEncodedTxUpdateOptions, - IFeeInfo, - IFeeInfoUnit, - ITransferInfo, - IUnsignedTxPro, -} from '../../types'; +import type { ISignedTxPro } from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; -import type { BigNumber } from 'bignumber.js'; +import type { IEncodedTxDynex, IUnspentOutput } from './types'; + +const DEFAULT_TX_MIN_FEE = 1000000; export default class Vault extends VaultBase { keyringMap = { @@ -75,25 +84,50 @@ export default class Vault extends VaultBase { } override updateEncodedTxTokenApprove( - encodedTx: IEncodedTx, + encodedTx: IEncodedTxDynex, amount: string, - ): Promise { + ): Promise { throw new Error('Method not implemented.'); } override buildUnsignedTxFromEncodedTx( - encodedTx: IEncodedTx, + encodedTx: IEncodedTxDynex, ): Promise { - throw new Error('Method not implemented.'); + return Promise.resolve({ + inputs: [], + outputs: [], + payload: { encodedTx }, + encodedTx, + }); } - override fetchFeeInfo( - encodedTx: IEncodedTx, - signOnly?: boolean | undefined, - specifiedFeeRate?: string | undefined, - transferCount?: number | undefined, - ): Promise { - throw new Error('Method not implemented.'); + override async fetchFeeInfo(): Promise { + const client = await this.getClient(); + const network = await this.getNetwork(); + let minFee = DEFAULT_TX_MIN_FEE; + + try { + const nodeInfo = await client.getNodeInfo(); + minFee = nodeInfo.min_tx_fee; + } catch (e) { + // pass + } + + const prices = [ + new BigNumber(minFee).shiftedBy(-network.feeDecimals).toFixed(), + ]; + + return { + nativeSymbol: network.symbol, + nativeDecimals: network.decimals, + feeSymbol: network.feeSymbol, + feeDecimals: network.feeDecimals, + + limit: '1', + prices, + defaultPresetIndex: '0', + tx: null, + }; } override getExportedCredential( @@ -110,37 +144,238 @@ export default class Vault extends VaultBase { } override updateEncodedTx( - encodedTx: IEncodedTx, - payload: any, - options: IEncodedTxUpdateOptions, - ): Promise { - throw new NotImplemented(); + encodedTx: IEncodedTxDynex, + ): Promise { + return Promise.resolve(encodedTx); } - override decodeTx(encodedTx: IEncodedTx, payload?: any): Promise { - throw new NotImplemented(); + override async decodeTx( + encodedTx: IEncodedTxDynex, + payload?: any, + ): Promise { + const network = await this.engine.getNetwork(this.networkId); + const address = await this.getAccountAddress(); + const token = await this.engine.getNativeTokenInfo(this.networkId); + + const actions = []; + + actions.push({ + type: IDecodedTxActionType.NATIVE_TRANSFER, + nativeTransfer: { + tokenInfo: token, + from: encodedTx.from, + to: encodedTx.to, + amount: encodedTx.amount, + amountValue: new BigNumber(encodedTx.amount) + .shiftedBy(network.decimals) + .toFixed(), + extraInfo: null, + }, + direction: + encodedTx.to === address + ? IDecodedTxDirection.SELF + : IDecodedTxDirection.OUT, + }); + + const decodedTx: IDecodedTx = { + txid: '', + owner: address, + signer: encodedTx.from || address, + nonce: 0, + actions, + status: IDecodedTxStatus.Pending, + networkId: this.networkId, + accountId: this.accountId, + encodedTx, + payload, + extraInfo: null, + }; + + return decodedTx; } - override decodedTxToLegacy(decodedTx: IDecodedTx): Promise { - throw new NotImplemented(); + override decodedTxToLegacy(decodedTx: IDecodedTx): Promise { + return Promise.resolve({} as IDecodedTxLegacy); } override buildEncodedTxFromApprove( approveInfo: IApproveInfo, - ): Promise { + ): Promise { throw new NotImplemented(); } - override buildEncodedTxFromTransfer( + override attachFeeInfoToEncodedTx({ + encodedTx, + }: { + encodedTx: IEncodedTxDynex; + feeInfoValue: IFeeInfoUnit; + }): Promise { + return Promise.resolve(encodedTx); + } + + override async buildEncodedTxFromTransfer( transferInfo: ITransferInfo, - ): Promise { - throw new NotImplemented(); + ): Promise { + if (!transferInfo.to) { + throw new Error('Invalid transferInfo.to params'); + } + const client = await this.getClient(); + const network = await this.getNetwork(); + + const unspentOutputs = await this._collectUnspentOutputs(); + + const { inputs, finalAmount } = this._collectInputs({ + unspentOutputs: Object.values(unspentOutputs), + amount: new BigNumber(transferInfo.amount) + .shiftedBy(network.decimals) + .toFixed(), + fee: new BigNumber(DEFAULT_TX_MIN_FEE).toFixed(), + }); + + const [decodedFrom, decodedTo] = await Promise.all([ + client.decodeAddress(transferInfo.from), + client.decodeAddress(transferInfo.to), + ]); + + return { + from: transferInfo.from, + to: transferInfo.to, + amount: new BigNumber(finalAmount).shiftedBy(-network.decimals).toFixed(), + paymentId: transferInfo.paymentId, + fee: new BigNumber(DEFAULT_TX_MIN_FEE) + .shiftedBy(-network.feeDecimals) + .toFixed(), + inputs, + decodedFrom, + decodedTo, + }; } - override attachFeeInfoToEncodedTx(params: { - encodedTx: IEncodedTx; - feeInfoValue: IFeeInfoUnit; - }): Promise { - return Promise.resolve(params.encodedTx); + override async validateAddress(address: string): Promise { + return this._validateAddressMemo(address); + } + + override async broadcastTransaction( + signedTx: ISignedTxPro, + options?: any, + ): Promise { + debugLogger.engine.info('broadcastTransaction START:', { + rawTx: signedTx.rawTx, + }); + const client = await this.getClient(); + const txid = await client.sendRawTransaction(signedTx.rawTx); + debugLogger.engine.info('broadcastTransaction END:', { + txid, + rawTx: signedTx.rawTx, + }); + return { + ...signedTx, + txid, + encodedTx: signedTx.encodedTx, + }; + } + + _validateAddressMemo = memoizee( + async (address: string) => { + const client = await this.getClient(); + const isValid = await client.validateAddress(address); + + if (!isValid) { + throw new InvalidAddress(); + } + return Promise.resolve(address); + }, + { + max: 1, + maxAge: getTimeDurationMs({ minute: 3 }), + }, + ); + + _collectInputs({ + unspentOutputs, + amount, + fee, + }: { + amount: string; + fee: string; + unspentOutputs: IUnspentOutput[]; + }) { + const inputs = []; + let finalAmount = new BigNumber(amount); + const totalAmount = finalAmount.plus(fee); + const totalUnspentOutputsAmount = unspentOutputs.reduce( + (acc, output) => acc.plus(output.amount), + new BigNumber(0), + ); + + if (totalUnspentOutputsAmount.lt(fee)) { + throw new Error('Insufficient balance'); + } + + if (totalUnspentOutputsAmount.lt(new BigNumber(finalAmount).plus(fee))) { + finalAmount = totalUnspentOutputsAmount.minus(fee); + } + + unspentOutputs.sort((a, b) => a.amount - b.amount); + + let currentAmount = new BigNumber(0); + for (const output of unspentOutputs) { + if (currentAmount.plus(output.amount).gte(totalAmount)) { + inputs.push(output); + break; + } else { + inputs.push(output); + currentAmount = currentAmount.plus(output.amount); + } + } + + return { + finalAmount: finalAmount.toFixed(), + inputs, + }; + } + + async _collectUnspentOutputs() { + const client = await this.getClient(); + const accountAddress = await this.getAccountAddress(); + const transactions = await client.getTransactionsByAddress(accountAddress); + const unspentOutputs: Record< + string, + { + prevIndex: number; + globalIndex: number; + txPubkey: string; + prevOutPubkey: string; + amount: number; + } + > = {}; + + for (let i = 0; i < transactions.length; i += 1) { + const tx = transactions[i]; + const transaction = await client.getTransaction(tx.hash); + + if (transaction.address_from === accountAddress) { + transaction.inputs.forEach((input) => { + const output = unspentOutputs[input.data.input.key_offsets[0]]; + if (output && output.amount === input.data.input.amount) { + delete unspentOutputs[input.data.input.key_offsets[0]]; + } + }); + } + + transaction.outputs_with_address.forEach((output, index) => { + if (output.address_to === accountAddress) { + unspentOutputs[output.globalIndex] = { + prevIndex: index, + globalIndex: output.globalIndex, + txPubkey: transaction.extra.publicKey, + prevOutPubkey: output.output.target.data.key, + amount: output.output.amount, + }; + } + }); + } + + return unspentOutputs; } } diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts index d697668f275..4b5e39a46a2 100644 --- a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -1,3 +1,4 @@ +import axios from 'axios'; import BigNumber from 'bignumber.js'; import { BaseClient } from '@onekeyhq/engine/src/client/BaseClient'; @@ -5,7 +6,6 @@ import { JsonRPCRequest } from '@onekeyhq/shared/src/request/JsonRPCRequest'; import { NotImplemented } from '../../../../errors'; -import type { CoinInfo } from '../../../../types/chain'; import type { AddressInfo, ClientInfo, @@ -14,6 +14,7 @@ import type { } from '../../../../types/provider'; import type { IOnChainBalance, + IOnChainNodeInfo, IOnChainTransaction, IOnChainTransactionsItem, } from '../types'; @@ -25,6 +26,7 @@ export enum RPC_METHODS { GET_TRANSACTION = 'gettransaction', GET_BLOCK_COUNT = 'getblockcount', VALIDATE_ADDRESS = 'validateaddress', + SEND_RAW_TRANSACTION = 'sendrawtransaction', } // eslint-disable-next-line @typescript-eslint/naming-convention export enum PARAMS_ENCODINGS { @@ -55,6 +57,21 @@ export class ClientDynex extends BaseClient { } } + async sendRawTransaction(tx: string): Promise { + const resp = await axios.post<{ status: string }>( + `${this.baseURL}/sendrawtransaction`, + { + tx_as_hex: tx, + do_not_relay: false, + }, + ); + + if (!resp.data.status || resp.data.status !== 'OK') + throw new Error('Failed to send transaction'); + + return resp.data.status; + } + async getBlockCount(): Promise { const resp = await this.rpc.call<{ count: number }>( RPC_METHODS.GET_BLOCK_COUNT, @@ -89,7 +106,7 @@ export class ClientDynex extends BaseClient { }); try { - this.checkDynexNodeResponse({ resp, method: 'getBalanceOfAddress' }); + this.checkDynexNodeResponse({ resp, method: 'validateAddress' }); } catch { return false; } @@ -97,6 +114,31 @@ export class ClientDynex extends BaseClient { return resp.isvalid; } + async decodeAddress(address: string): Promise<{ + spend: string; + view: string; + }> { + const resp = await this.rpc.call<{ + status: string; + spendPublicKey: string; + viewPublicKey: string; + }>(RPC_METHODS.VALIDATE_ADDRESS, { + address, + }); + + this.checkDynexNodeResponse({ resp, method: 'decodeAddress' }); + + return { + spend: resp.spendPublicKey, + view: resp.viewPublicKey, + }; + } + + async getNodeInfo(): Promise { + const resp = await axios.get(`${this.baseURL}/getinfo`); + return resp.data; + } + async getTransaction(txid: string): Promise { const resp = await this.rpc.call<{ status: string; diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts index dc5795f422a..b1e2ff9cfed 100644 --- a/packages/engine/src/vaults/impl/dynex/settings.ts +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -21,6 +21,8 @@ const settings: IVaultSettings = Object.freeze({ hideInAllNetworksMode: true, + withPaymentId: true, + accountNameInfo: { default: { prefix: 'DNX', diff --git a/packages/engine/src/vaults/impl/dynex/types.ts b/packages/engine/src/vaults/impl/dynex/types.ts index d2f5402244f..1c61e69a02d 100644 --- a/packages/engine/src/vaults/impl/dynex/types.ts +++ b/packages/engine/src/vaults/impl/dynex/types.ts @@ -87,3 +87,32 @@ export type IOnChainBalance = { legacy_wallet: boolean; wallet: string; }; + +export type IOnChainNodeInfo = { + min_tx_fee: number; +}; + +export type IUnspentOutput = { + prevIndex: number; + globalIndex: number; + txPubkey: string; + prevOutPubkey: string; + amount: number; +}; + +export type IEncodedTxDynex = { + from: string; + to: string; + amount: string; + fee: string; + paymentId?: string; + inputs: IUnspentOutput[]; + decodedFrom: { + spend: string; + view: string; + }; + decodedTo: { + spend: string; + view: string; + }; +}; diff --git a/packages/engine/src/vaults/impl/dynex/utils.ts b/packages/engine/src/vaults/impl/dynex/utils.ts new file mode 100644 index 00000000000..3459e0bc8af --- /dev/null +++ b/packages/engine/src/vaults/impl/dynex/utils.ts @@ -0,0 +1,31 @@ +/* eslint-disable no-param-reassign */ +/* eslint-disable no-bitwise */ + +export function encodeVarInt(number: number) { + const result = []; + do { + let byte = number & 0x7f; + number >>>= 7; + if (number !== 0) { + byte |= 0x80; + } + result.push(byte); + } while (number !== 0); + return result.map((byte) => byte.toString(16).padStart(2, '0')).join(''); +} + +export function encodeVarIntLittleEndian(number: number) { + const result = []; + do { + let byte = number & 0x7f; + number >>>= 7; + if (number !== 0) { + byte |= 0x80; + } + result.push(byte); + } while (number !== 0); + return result + .reverse() + .map((byte) => byte.toString(16).padStart(2, '0')) + .join(''); +} diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index 26f89fb809e..f89fed331b4 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -35,6 +35,7 @@ import type { import type { IEncodedTxCfx } from './impl/cfx/types'; import type { IEncodedTxCosmos } from './impl/cosmos/type'; import type { IEncodedTxDot } from './impl/dot/types'; +import type { IEncodedTxDynex } from './impl/dynex/types'; import type { EVMDecodedItem } from './impl/evm/decoder/types'; import type { INativeTxEvm } from './impl/evm/types'; import type { IEncodedTxEvm } from './impl/evm/Vault'; @@ -128,6 +129,9 @@ export type IVaultSettings = { */ withDestinationTag?: boolean; + // dynex payment id + withPaymentId?: boolean; + subNetworkSettings?: { [networkId: string]: IVaultSubNetworkSettings; }; @@ -207,6 +211,7 @@ export type ITransferInfo = { ignoreInscriptions?: boolean; useCustomAddressesBalance?: boolean; opReturn?: string; + paymentId?: string; // dynex payment id }; export type IApproveInfo = { from: string; // token owner @@ -282,7 +287,8 @@ export type IEncodedTx = | IEncodedTxNexa | IEncodedTxLightning | IEncodedTxNostr - | IEncodedTxNervos; + | IEncodedTxNervos + | IEncodedTxDynex; export type INativeTx = | INativeTxEvm diff --git a/packages/kit/src/views/Send/modals/PreSendAddress.tsx b/packages/kit/src/views/Send/modals/PreSendAddress.tsx index 1f313c6ec93..af9c021f2a8 100644 --- a/packages/kit/src/views/Send/modals/PreSendAddress.tsx +++ b/packages/kit/src/views/Send/modals/PreSendAddress.tsx @@ -55,6 +55,7 @@ type RouteProps = RouteProp; type FormValues = { to: string; destinationTag?: string; + paymentId?: string; }; function PreSendAddress() { @@ -66,6 +67,7 @@ function PreSendAddress() { const [isLoadingAssets, setIsLoadingAssets] = useState(false); const [isValidatingAddress, setIsValidatingAddress] = useState(false); const [displayDestinationTag, setDisplayDestinationTag] = useState(false); + const [displayPaymentId, setDisplayPaymentId] = useState(false); const [isAddressBook, setIsAddressBook] = useState(false); const [addressBookLabel, setAddressBookLabel] = useState< string | undefined @@ -219,6 +221,7 @@ function PreSendAddress() { networkId, ); setDisplayDestinationTag(vaultSettings?.withDestinationTag ?? false); + setDisplayPaymentId(vaultSettings?.withPaymentId ?? false); })(); }, [networkId]); // @@ -449,6 +452,7 @@ function PreSendAddress() { accountId, to: toVal, destinationTag: values.destinationTag, + paymentId: values.paymentId, }, }, }); diff --git a/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx b/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx index 4bd157c894b..c0feb5dd8f8 100644 --- a/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx +++ b/packages/kit/src/views/Send/utils/useFeeInfoPayload.tsx @@ -204,14 +204,16 @@ export function useFeeInfoPayload({ eip1559: info.eip1559, ...(info.eip1559 ? { - price1559: info.prices[ - Number(info.defaultPresetIndex) - ] as EIP1559Fee, + price1559: + (info.prices[ + Number(info.defaultPresetIndex) + ] as EIP1559Fee) ?? info.prices[0], } : { - price: info.prices[ - Number(info.defaultPresetIndex) - ] as string, + price: + (info.prices[ + Number(info.defaultPresetIndex) + ] as string) ?? info.prices[0], }), }; } diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index 8bca9a5edbc..a9fcb980354 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4480,7 +4480,7 @@ const serverPresetNetworks = [ 'balance2FeeDecimals': 0, 'chainId': '0', 'code': 'dynex', - 'decimals': 8, + 'decimals': 9, 'id': 'dynex--0', 'impl': 'dynex', 'isTestnet': false, @@ -4488,7 +4488,7 @@ const serverPresetNetworks = [ 'name': 'Dynex', 'rpcURLs': [ { - 'url': 'https://node.onekeytest.com/dynex', + 'url': 'http://node.dynexcoin.org:18333', }, ], 'shortcode': 'dnx', @@ -4496,7 +4496,7 @@ const serverPresetNetworks = [ 'symbol': 'DNX', 'feeMeta': { 'code': 'dnx', - 'decimals': 8, + 'decimals': 9, 'symbol': 'DNX', }, 'defaultEnabled': true, From 2b734785fb0868c97eec55f43e5082d0d0a9d9ba Mon Sep 17 00:00:00 2001 From: weatherstar Date: Fri, 19 Apr 2024 10:39:20 +0800 Subject: [PATCH 05/16] feat: on chain txs --- .../src/vaults/impl/dynex/KeyringHardware.ts | 49 ++++++---- .../engine/src/vaults/impl/dynex/Vault.ts | 97 +++++++++++++++---- .../vaults/impl/dynex/helper/ClientDynex.ts | 31 +++--- .../engine/src/vaults/impl/dynex/settings.ts | 4 +- .../engine/src/vaults/impl/dynex/utils.ts | 28 +++--- 5 files changed, 142 insertions(+), 67 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index d04c0711bde..3ab5360c245 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -14,9 +14,9 @@ import { getAccountNameInfoByImpl } from '../../../managers/impl'; import { AccountType, type DBAccount } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; -import { encodeVarInt, encodeVarIntLittleEndian } from './utils'; +import { encodeVarInt, integerToLittleEndianHex } from './utils'; -import type { DBSimpleAccount } from '../../../types/account'; +import type { DBUTXOAccount } from '../../../types/account'; import type { IHardwareGetAddressParams, IPrepareHardwareAccountsParams, @@ -28,7 +28,7 @@ import type { IEncodedTxDynex } from './types'; export class KeyringHardware extends KeyringHardwareBase { override async prepareAccounts( params: IPrepareHardwareAccountsParams, - ): Promise { + ): Promise { const { indexes, names, template } = params; const { pathPrefix } = slicePathTemplate(template); const paths = indexes.map((index) => `${pathPrefix}/${index}'`); @@ -57,7 +57,7 @@ export class KeyringHardware extends KeyringHardwareBase { throw convertDeviceError(addressesResponse.payload); } - const ret: DBSimpleAccount[] = []; + const ret: DBUTXOAccount[] = []; let index = 0; for (const addressInfo of addressesResponse.payload) { const { address, path } = addressInfo; @@ -66,14 +66,17 @@ export class KeyringHardware extends KeyringHardwareBase { throw new OneKeyHardwareError({ message: 'Get Dynex Address error.' }); } const name = (names || [])[index] || `${prefix} #${indexes[index] + 1}`; + const addressRelPath = '0/0'; ret.push({ id: `${this.walletId}--${path}`, name, - type: AccountType.SIMPLE, + type: AccountType.UTXO, path, coinType: COIN_TYPE, pub: '', + xpub: '', address, + addresses: { [addressRelPath]: address }, }); index += 1; } @@ -189,7 +192,7 @@ export class KeyringHardware extends KeyringHardwareBase { const chargeAmount = totalInputAmountBN .minus(params.amount) .minus(params.fee); - debugger; + rawTx += version; rawTx += unlockTime; rawTx += encodeVarInt(params.inputs.length); @@ -208,7 +211,7 @@ export class KeyringHardware extends KeyringHardwareBase { for (let i = 0; i < outputKeys.length; i += 1) { const outputKey = outputKeys[i]; rawTx += encodeVarInt( - i === outputKeys.length - 1 + i === outputKeys.length - 1 && chargeAmount.gt(0) ? chargeAmount.toNumber() : Number(params.amount), ); @@ -216,22 +219,30 @@ export class KeyringHardware extends KeyringHardwareBase { rawTx += outputKey; } - rawTx += txPubkeyTag; - rawTx += txKey.ephemeralTxPubKey; + let extra = ''; + + extra += txPubkeyTag; + extra += txKey.ephemeralTxPubKey; - rawTx += fromAddressTag; - rawTx += decodedFrom.spend; - rawTx += decodedFrom.view; + extra += fromAddressTag; + extra += decodedFrom.spend; + extra += decodedFrom.view; - rawTx += toAddressTag; - rawTx += decodedTo.spend; - rawTx += decodedTo.view; + extra += toAddressTag; + extra += decodedTo.spend; + extra += decodedTo.view; + + extra += amountTag; + extra += integerToLittleEndianHex({ + number: Number(params.amount), + bytes: 8, + }); - rawTx += amountTag; - rawTx += encodeVarIntLittleEndian(Number(params.amount)); + extra += txSecTag; + extra += txKey.ephemeralTxSecKey; - rawTx += txSecTag; - rawTx += txKey.ephemeralTxSecKey; + rawTx += encodeVarInt(extra.length / 2); + rawTx += extra; for (const signature of signatures) { rawTx += signature; diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index bf915f85c03..5dfec988d54 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -31,7 +31,7 @@ import settings from './settings'; import type { AccountCredentialType } from '../../../types/account'; import type { PartialTokenInfo } from '../../../types/provider'; -import type { ISignedTxPro } from '../../types'; +import type { IHistoryTx, ISignedTxPro } from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; import type { IEncodedTxDynex, IUnspentOutput } from './types'; @@ -102,16 +102,8 @@ export default class Vault extends VaultBase { } override async fetchFeeInfo(): Promise { - const client = await this.getClient(); const network = await this.getNetwork(); - let minFee = DEFAULT_TX_MIN_FEE; - - try { - const nodeInfo = await client.getNodeInfo(); - minFee = nodeInfo.min_tx_fee; - } catch (e) { - // pass - } + const minFee = DEFAULT_TX_MIN_FEE; const prices = [ new BigNumber(minFee).shiftedBy(-network.feeDecimals).toFixed(), @@ -223,7 +215,6 @@ export default class Vault extends VaultBase { const network = await this.getNetwork(); const unspentOutputs = await this._collectUnspentOutputs(); - const { inputs, finalAmount } = this._collectInputs({ unspentOutputs: Object.values(unspentOutputs), amount: new BigNumber(transferInfo.amount) @@ -263,16 +254,88 @@ export default class Vault extends VaultBase { rawTx: signedTx.rawTx, }); const client = await this.getClient(); - const txid = await client.sendRawTransaction(signedTx.rawTx); + const txid = await client.broadcastTransaction(signedTx.rawTx); debugLogger.engine.info('broadcastTransaction END:', { txid, rawTx: signedTx.rawTx, }); - return { - ...signedTx, - txid, - encodedTx: signedTx.encodedTx, - }; + return signedTx; + } + + override async fetchOnChainHistory(options: { + tokenIdOnNetwork?: string; + localHistory: IHistoryTx[]; + }): Promise { + const client = await this.getClient(); + const { localHistory } = options; + const address = await this.getAccountAddress(); + const token = await this.engine.getNativeTokenInfo(this.networkId); + const network = await this.getNetwork(); + const txs = await client.getTransactionsByAddress(address); + + const promises = txs.map(async (tx) => { + try { + const historyTxToMerge = localHistory.find( + (item) => item.decodedTx.txid === tx.hash, + ); + if (historyTxToMerge && historyTxToMerge.decodedTx.isFinal) { + return null; + } + + let direction = IDecodedTxDirection.OUT; + if (tx.to_address.includes(address)) { + direction = + tx.from_address === address + ? IDecodedTxDirection.SELF + : IDecodedTxDirection.IN; + } + + const amountValue = parseInt(tx.amount[0], 16); + + const decodedTx: IDecodedTx = { + txid: tx.hash ?? '', + owner: address, + signer: tx.from_address, + nonce: 0, + actions: [ + { + type: IDecodedTxActionType.NATIVE_TRANSFER, + direction, + nativeTransfer: { + tokenInfo: token, + from: tx.from_address, + to: tx.to_address[0], + amount: new BigNumber(amountValue) + .shiftedBy(-network.decimals) + .toFixed(), + amountValue: amountValue.toString(10), + extraInfo: null, + }, + }, + ], + status: IDecodedTxStatus.Confirmed, + totalFeeInNative: new BigNumber(tx.fee) + .shiftedBy(-network.feeDecimals) + .toFixed(), + networkId: this.networkId, + accountId: this.accountId, + extraInfo: null, + }; + decodedTx.updatedAt = new Date(tx.timestamp * 1000).getTime(); + decodedTx.createdAt = + historyTxToMerge?.decodedTx.createdAt ?? decodedTx.updatedAt; + decodedTx.isFinal = decodedTx.status === IDecodedTxStatus.Confirmed; + return await this.buildHistoryTx({ + decodedTx, + historyTxToMerge, + }); + } catch (e) { + console.error(e); + return Promise.resolve(null); + } + }); + + return (await Promise.all(promises)).filter(Boolean); } _validateAddressMemo = memoizee( diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts index 4b5e39a46a2..898707b55af 100644 --- a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -26,7 +26,6 @@ export enum RPC_METHODS { GET_TRANSACTION = 'gettransaction', GET_BLOCK_COUNT = 'getblockcount', VALIDATE_ADDRESS = 'validateaddress', - SEND_RAW_TRANSACTION = 'sendrawtransaction', } // eslint-disable-next-line @typescript-eslint/naming-convention export enum PARAMS_ENCODINGS { @@ -57,21 +56,6 @@ export class ClientDynex extends BaseClient { } } - async sendRawTransaction(tx: string): Promise { - const resp = await axios.post<{ status: string }>( - `${this.baseURL}/sendrawtransaction`, - { - tx_as_hex: tx, - do_not_relay: false, - }, - ); - - if (!resp.data.status || resp.data.status !== 'OK') - throw new Error('Failed to send transaction'); - - return resp.data.status; - } - async getBlockCount(): Promise { const resp = await this.rpc.call<{ count: number }>( RPC_METHODS.GET_BLOCK_COUNT, @@ -184,8 +168,19 @@ export class ClientDynex extends BaseClient { throw new NotImplemented(); } - override broadcastTransaction(): Promise { - throw new NotImplemented(); + override async broadcastTransaction(rawTx: string): Promise { + const resp = await axios.post<{ status: string }>( + `${this.baseURL}/sendrawtransaction`, + { + tx_as_hex: rawTx, + do_not_relay: false, + }, + ); + + if (!resp.data.status || resp.data.status !== 'OK') + throw new Error('Failed to send transaction'); + + return resp.data.status; } override getBalances(): Promise<(BigNumber | undefined)[]> { diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts index b1e2ff9cfed..029460612f1 100644 --- a/packages/engine/src/vaults/impl/dynex/settings.ts +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -17,7 +17,7 @@ const settings: IVaultSettings = Object.freeze({ watchingAccountEnabled: false, softwareAccountDisabled: true, - isUTXOModel: false, + isUTXOModel: true, hideInAllNetworksMode: true, @@ -29,6 +29,8 @@ const settings: IVaultSettings = Object.freeze({ category: `44'/${COINTYPE_DYNEX}'`, template: `m/44'/${COINTYPE_DYNEX}'/0'/0'/${INDEX_PLACEHOLDER}'`, coinType: COINTYPE_DYNEX, + label: 'Default', + subDesc: `m/44'/${COINTYPE_DYNEX}'/0'/0'/x'`, }, }, }); diff --git a/packages/engine/src/vaults/impl/dynex/utils.ts b/packages/engine/src/vaults/impl/dynex/utils.ts index 3459e0bc8af..5f29d760b60 100644 --- a/packages/engine/src/vaults/impl/dynex/utils.ts +++ b/packages/engine/src/vaults/impl/dynex/utils.ts @@ -14,18 +14,22 @@ export function encodeVarInt(number: number) { return result.map((byte) => byte.toString(16).padStart(2, '0')).join(''); } -export function encodeVarIntLittleEndian(number: number) { - const result = []; - do { - let byte = number & 0x7f; - number >>>= 7; - if (number !== 0) { - byte |= 0x80; - } - result.push(byte); - } while (number !== 0); - return result +export function integerToLittleEndianHex({ + number, + bytes, +}: { + number: number; + bytes: number; +}) { + let hexString: string = number.toString(16); + if (hexString.length % 2 !== 0) { + hexString = `0${hexString}`; + } + while (hexString.length / 2 < bytes) { + hexString = `00${hexString}`; + } + const littleEndianHex: string = (hexString.match(/.{2}/g) ?? []) .reverse() - .map((byte) => byte.toString(16).padStart(2, '0')) .join(''); + return littleEndianHex; } From 503c49ca47baca53ef0befdf1cc4f216447d22c8 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Fri, 19 Apr 2024 19:41:21 +0800 Subject: [PATCH 06/16] feat: payment id --- .../src/vaults/impl/dynex/KeyringHardware.ts | 23 ++- .../engine/src/vaults/impl/dynex/Vault.ts | 56 +++--- .../vaults/impl/dynex/helper/ClientDynex.ts | 20 -- .../engine/src/vaults/impl/dynex/types.ts | 8 - .../engine/src/vaults/impl/dynex/utils.ts | 181 ++++++++++++++++++ .../src/views/Send/modals/PreSendAddress.tsx | 30 +++ packages/shared/src/config/presetNetworks.ts | 2 +- 7 files changed, 266 insertions(+), 54 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index 3ab5360c245..acb4dbee206 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -14,7 +14,12 @@ import { getAccountNameInfoByImpl } from '../../../managers/impl'; import { AccountType, type DBAccount } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; -import { encodeVarInt, integerToLittleEndianHex } from './utils'; +import { + cnFastHash, + decodeAddress, + encodeVarInt, + integerToLittleEndianHex, +} from './utils'; import type { DBUTXOAccount } from '../../../types/account'; import type { @@ -184,7 +189,10 @@ export class KeyringHardware extends KeyringHardwareBase { const toAddressTag = '05'; const amountTag = '06'; const txSecTag = '07'; - const { decodedFrom, decodedTo } = encodedTx; + const extraNonceTag = '02'; + const extraNoncePaymentIdTag = '00'; + const decodedFrom = decodeAddress(encodedTx.from); + const decodedTo = decodeAddress(encodedTx.to); const totalInputAmountBN = params.inputs.reduce( (acc, input) => acc.plus(input.amount), new BigNumber(0), @@ -221,6 +229,15 @@ export class KeyringHardware extends KeyringHardwareBase { let extra = ''; + if (params.paymentIdHex) { + extra += extraNonceTag; + extra += encodeVarInt( + (extraNoncePaymentIdTag + params.paymentIdHex).length, + ); + extra += extraNoncePaymentIdTag; + extra += params.paymentIdHex; + } + extra += txPubkeyTag; extra += txKey.ephemeralTxPubKey; @@ -249,7 +266,7 @@ export class KeyringHardware extends KeyringHardwareBase { } return { - txid: '', + txid: cnFastHash(rawTx), rawTx, }; } diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index 5dfec988d54..51a8a28d690 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -20,6 +20,7 @@ import { type ITransferInfo, type IUnsignedTxPro, } from '../../types'; +import { coinSelect } from '../../utils/btcForkChain/utils'; import { VaultBase } from '../../VaultBase'; import { ClientDynex } from './helper/ClientDynex'; @@ -211,7 +212,6 @@ export default class Vault extends VaultBase { if (!transferInfo.to) { throw new Error('Invalid transferInfo.to params'); } - const client = await this.getClient(); const network = await this.getNetwork(); const unspentOutputs = await this._collectUnspentOutputs(); @@ -223,11 +223,6 @@ export default class Vault extends VaultBase { fee: new BigNumber(DEFAULT_TX_MIN_FEE).toFixed(), }); - const [decodedFrom, decodedTo] = await Promise.all([ - client.decodeAddress(transferInfo.from), - client.decodeAddress(transferInfo.to), - ]); - return { from: transferInfo.from, to: transferInfo.to, @@ -237,8 +232,6 @@ export default class Vault extends VaultBase { .shiftedBy(-network.feeDecimals) .toFixed(), inputs, - decodedFrom, - decodedTo, }; } @@ -371,7 +364,7 @@ export default class Vault extends VaultBase { new BigNumber(0), ); - if (totalUnspentOutputsAmount.lt(fee)) { + if (totalUnspentOutputsAmount.lte(fee)) { throw new Error('Insufficient balance'); } @@ -379,22 +372,41 @@ export default class Vault extends VaultBase { finalAmount = totalUnspentOutputsAmount.minus(fee); } - unspentOutputs.sort((a, b) => a.amount - b.amount); + const inputsForCoinSelect = unspentOutputs.map((output) => ({ + txId: '', + vout: 0, + value: output.amount, + address: '', + path: '', + ...output, + })); - let currentAmount = new BigNumber(0); - for (const output of unspentOutputs) { - if (currentAmount.plus(output.amount).gte(totalAmount)) { - inputs.push(output); - break; - } else { - inputs.push(output); - currentAmount = currentAmount.plus(output.amount); - } - } + const outputsForCoinSelect = [ + { + address: '', + value: finalAmount.toNumber(), + }, + ]; + + const { inputs: inputsFromCoinSelect } = coinSelect({ + inputsForCoinSelect, + outputsForCoinSelect, + feeRate: '0', + }); return { finalAmount: finalAmount.toFixed(), - inputs, + inputs: + inputsFromCoinSelect?.map((input) => { + const tempInput = input as unknown as IUnspentOutput; + return { + globalIndex: tempInput.globalIndex, + prevIndex: tempInput.prevIndex, + prevOutPubkey: tempInput.prevOutPubkey, + txPubkey: tempInput.txPubkey, + amount: tempInput.amount, + }; + }) ?? [], }; } @@ -426,7 +438,7 @@ export default class Vault extends VaultBase { }); } - transaction.outputs_with_address.forEach((output, index) => { + transaction.outputs_with_address?.forEach((output, index) => { if (output.address_to === accountAddress) { unspentOutputs[output.globalIndex] = { prevIndex: index, diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts index 898707b55af..169372fa1bc 100644 --- a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -98,26 +98,6 @@ export class ClientDynex extends BaseClient { return resp.isvalid; } - async decodeAddress(address: string): Promise<{ - spend: string; - view: string; - }> { - const resp = await this.rpc.call<{ - status: string; - spendPublicKey: string; - viewPublicKey: string; - }>(RPC_METHODS.VALIDATE_ADDRESS, { - address, - }); - - this.checkDynexNodeResponse({ resp, method: 'decodeAddress' }); - - return { - spend: resp.spendPublicKey, - view: resp.viewPublicKey, - }; - } - async getNodeInfo(): Promise { const resp = await axios.get(`${this.baseURL}/getinfo`); return resp.data; diff --git a/packages/engine/src/vaults/impl/dynex/types.ts b/packages/engine/src/vaults/impl/dynex/types.ts index 1c61e69a02d..9e8a54923d2 100644 --- a/packages/engine/src/vaults/impl/dynex/types.ts +++ b/packages/engine/src/vaults/impl/dynex/types.ts @@ -107,12 +107,4 @@ export type IEncodedTxDynex = { fee: string; paymentId?: string; inputs: IUnspentOutput[]; - decodedFrom: { - spend: string; - view: string; - }; - decodedTo: { - spend: string; - view: string; - }; }; diff --git a/packages/engine/src/vaults/impl/dynex/utils.ts b/packages/engine/src/vaults/impl/dynex/utils.ts index 5f29d760b60..924a63cb160 100644 --- a/packages/engine/src/vaults/impl/dynex/utils.ts +++ b/packages/engine/src/vaults/impl/dynex/utils.ts @@ -1,5 +1,11 @@ /* eslint-disable no-param-reassign */ /* eslint-disable no-bitwise */ +import { keccak256 } from '@ethersproject/keccak256'; +import BigNumber from 'bignumber.js'; + +const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 185; +const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 29; +const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 52; export function encodeVarInt(number: number) { const result = []; @@ -33,3 +39,178 @@ export function integerToLittleEndianHex({ .join(''); return littleEndianHex; } + +export function hexToBin(hex: string): Uint8Array { + if (hex.length % 2 !== 0) throw new Error('Hex string has invalid length!'); + const result = new Uint8Array(hex.length / 2); + for (let i = 0; i < hex.length / 2; i += 1) { + result[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); + } + return result; +} + +export function validHex(hex: string) { + const exp = new RegExp(`[0-9a-fA-F]{${hex.length}}`); + return exp.test(hex); +} + +export function cnFastHash(input: string): string { + if (input.length % 2 !== 0 || !validHex(input)) { + throw new Error('Input invalid'); + } + return keccak256(hexToBin(input)); +} + +export function strToBin(str: string): Uint8Array { + const res = new Uint8Array(str.length); + for (let i = 0; i < str.length; i += 1) { + res[i] = str.charCodeAt(i); + } + return res; +} + +export function binToHex(bin: Uint8Array): string { + const out: string[] = []; + for (let i = 0; i < bin.length; i += 1) { + out.push(`0${bin[i].toString(16)}`.slice(-2)); + } + return out.join(''); +} + +export function uint64To8be( + num: BigNumber | number | string, + size: number, +): Uint8Array { + let numBN = new BigNumber(num); + const res: Uint8Array = new Uint8Array(size); + if (size < 1 || size > 8) { + throw new Error('Invalid input length'); + } + const twopow8 = new BigNumber(2).pow(8); + for (let i = size - 1; i >= 0; i -= 1) { + res[i] = numBN.mod(twopow8).toNumber(); + numBN = numBN.dividedToIntegerBy(twopow8); + } + return res; +} + +export function cnBase58Decode(address: string) { + const alphabetStr = + '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + + const alphabet: number[] = []; + for (let i = 0; i < alphabetStr.length; i += 1) { + alphabet.push(alphabetStr.charCodeAt(i)); + } + + const encodedBlockSizes: number[] = [0, 2, 3, 5, 6, 7, 9, 10, 11]; + + const alphabetSize = alphabet.length; + const fullBlockSize = 8; + const fullEncodedBlockSize = 11; + const UINT64_MAX = new BigNumber(2).pow(64); + + const decodeBlock = function ( + data: Uint8Array, + buf: Uint8Array, + index: number, + ): Uint8Array { + if (data.length < 1 || data.length > fullEncodedBlockSize) { + throw new Error(`Invalid block length: ${data.length}`); + } + + const resSize: number = encodedBlockSizes.indexOf(data.length); + if (resSize <= 0) { + throw new Error('Invalid block size'); + } + let resNum = new BigNumber(0); + let order = new BigNumber(1); + for (let i = data.length - 1; i >= 0; i -= 1) { + const digit: number = alphabet.indexOf(data[i]); + if (digit < 0) { + throw new Error('Invalid symbol'); + } + const product = order.multipliedBy(digit).plus(resNum); + // if product > UINT64_MAX + if (product.comparedTo(UINT64_MAX) === 1) { + throw new Error('Overflow'); + } + resNum = product; + order = order.multipliedBy(alphabetSize); + } + if ( + resSize < fullBlockSize && + new BigNumber(2).pow(8 * resSize).comparedTo(resNum) <= 0 + ) { + throw new Error('Overflow 2'); + } + buf.set(uint64To8be(resNum, resSize), index); + return buf; + }; + + const addressArray = strToBin(address); + if (addressArray.length === 0) { + return ''; + } + const fullBlockCount: number = Math.floor( + addressArray.length / fullEncodedBlockSize, + ); + const lastBlockSize: number = addressArray.length % fullEncodedBlockSize; + const lastBlockDecodedSize: number = encodedBlockSizes.indexOf(lastBlockSize); + if (lastBlockDecodedSize < 0) { + throw new Error('Invalid encoded length'); + } + const dataSize: number = + fullBlockCount * fullBlockSize + lastBlockDecodedSize; + const data: Uint8Array = new Uint8Array(dataSize); + for (let i = 0; i < fullBlockCount; i += 1) { + decodeBlock( + addressArray.subarray( + i * fullEncodedBlockSize, + i * fullEncodedBlockSize + fullEncodedBlockSize, + ), + data, + i * fullBlockSize, + ); + } + if (lastBlockSize > 0) { + decodeBlock( + addressArray.subarray( + fullBlockCount * fullEncodedBlockSize, + fullBlockCount * fullEncodedBlockSize + lastBlockSize, + ), + data, + fullBlockCount * fullBlockSize, + ); + } + return binToHex(data); +} + +export function decodeAddress(address: string) { + let dec = cnBase58Decode(address); + const expectedPrefix = encodeVarInt(CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX); + const expectedPrefixInt = encodeVarInt( + CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX, + ); + const expectedPrefixSub = encodeVarInt( + CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX, + ); + const prefix = dec.slice(0, expectedPrefix.length); + + if ( + prefix !== expectedPrefix && + prefix !== expectedPrefixInt && + prefix !== expectedPrefixSub + ) { + throw new Error('Invalid address prefix'); + } + + dec = dec.slice(expectedPrefix.length); + const spend = dec.slice(0, 64); + const view = dec.slice(64, 128); + + return { + spend, + view, + }; +} diff --git a/packages/kit/src/views/Send/modals/PreSendAddress.tsx b/packages/kit/src/views/Send/modals/PreSendAddress.tsx index af9c021f2a8..1171692a3db 100644 --- a/packages/kit/src/views/Send/modals/PreSendAddress.tsx +++ b/packages/kit/src/views/Send/modals/PreSendAddress.tsx @@ -25,6 +25,11 @@ import type { INFTInfo, ITransferInfo, } from '@onekeyhq/engine/src/vaults/types'; +import { + addHexPrefix, + isHexString, + stripHexPrefix, +} from '@onekeyhq/engine/src/vaults/utils/hexUtils'; import { makeTimeoutPromise } from '@onekeyhq/shared/src/background/backgroundUtils'; import { isLightningNetworkByImpl } from '@onekeyhq/shared/src/engine/engineConsts'; @@ -500,6 +505,30 @@ function PreSendAddress() { ); }, [control, displayDestinationTag, intl]); + const PaymentIdForm = useMemo(() => { + if (!displayPaymentId) return null; + + return ( + { + if (!value) return undefined; + if ( + !isHexString(addHexPrefix(value)) || + stripHexPrefix(value).length !== 64 + ) { + return 'Payment ID must be a 64 char hex string'; + } + }, + }} + > + + + ); + }, [control, displayPaymentId]); + const helpTextOfNameServiceResolver = useCallback( (value) => ( {DestinationTagForm} + {PaymentIdForm} {!validateMessage.errorMessage && ( Date: Mon, 22 Apr 2024 15:58:50 +0800 Subject: [PATCH 07/16] fix: tx status --- .../src/vaults/impl/dynex/KeyringHardware.ts | 9 +++---- .../engine/src/vaults/impl/dynex/Vault.ts | 21 ++++++++++++--- .../vaults/impl/dynex/helper/ClientDynex.ts | 26 ++++++++++++++----- packages/shared/src/config/presetNetworks.ts | 3 ++- 4 files changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index acb4dbee206..05dd972a68b 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -11,8 +11,9 @@ import debugLogger from '@onekeyhq/shared/src/logger/debugLogger'; import { OneKeyHardwareError } from '../../../errors'; import { slicePathTemplate } from '../../../managers/derivation'; import { getAccountNameInfoByImpl } from '../../../managers/impl'; -import { AccountType, type DBAccount } from '../../../types/account'; +import { AccountType } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; +import { stripHexPrefix } from '../../utils/hexUtils'; import { cnFastHash, @@ -231,9 +232,7 @@ export class KeyringHardware extends KeyringHardwareBase { if (params.paymentIdHex) { extra += extraNonceTag; - extra += encodeVarInt( - (extraNoncePaymentIdTag + params.paymentIdHex).length, - ); + extra += '21'; extra += extraNoncePaymentIdTag; extra += params.paymentIdHex; } @@ -266,7 +265,7 @@ export class KeyringHardware extends KeyringHardwareBase { } return { - txid: cnFastHash(rawTx), + txid: stripHexPrefix(cnFastHash(rawTx)), rawTx, }; } diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index 51a8a28d690..b2b6874545a 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -31,7 +31,10 @@ import { KeyringWatching } from './KeyringWatching'; import settings from './settings'; import type { AccountCredentialType } from '../../../types/account'; -import type { PartialTokenInfo } from '../../../types/provider'; +import type { + PartialTokenInfo, + TransactionStatus, +} from '../../../types/provider'; import type { IHistoryTx, ISignedTxPro } from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; import type { IEncodedTxDynex, IUnspentOutput } from './types'; @@ -331,6 +334,18 @@ export default class Vault extends VaultBase { return (await Promise.all(promises)).filter(Boolean); } + override async getTransactionStatuses( + txids: string[], + ): Promise<(TransactionStatus | undefined)[]> { + const client = await this.getClient(); + return Promise.all( + txids.map(async (txid) => { + const response = client.getTransactionStatus(txid); + return response; + }), + ); + } + _validateAddressMemo = memoizee( async (address: string) => { const client = await this.getClient(); @@ -342,7 +357,7 @@ export default class Vault extends VaultBase { return Promise.resolve(address); }, { - max: 1, + max: 5, maxAge: getTimeDurationMs({ minute: 3 }), }, ); @@ -384,7 +399,7 @@ export default class Vault extends VaultBase { const outputsForCoinSelect = [ { address: '', - value: finalAmount.toNumber(), + value: finalAmount.plus(fee).toNumber(), }, ]; diff --git a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts index 169372fa1bc..21015039fda 100644 --- a/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts +++ b/packages/engine/src/vaults/impl/dynex/helper/ClientDynex.ts @@ -5,12 +5,12 @@ import { BaseClient } from '@onekeyhq/engine/src/client/BaseClient'; import { JsonRPCRequest } from '@onekeyhq/shared/src/request/JsonRPCRequest'; import { NotImplemented } from '../../../../errors'; +import { TransactionStatus } from '../../../../types/provider'; import type { AddressInfo, ClientInfo, FeePricePerUnit, - TransactionStatus, } from '../../../../types/provider'; import type { IOnChainBalance, @@ -115,6 +115,18 @@ export class ClientDynex extends BaseClient { return resp.transaction; } + async getTransactionStatus(txid: string): Promise { + try { + const tx = await this.getTransaction(txid); + if (tx.inBlockchain) { + return TransactionStatus.CONFIRM_AND_SUCCESS; + } + return TransactionStatus.PENDING; + } catch (err) { + return TransactionStatus.PENDING; + } + } + async getTransactionsByAddress( address: string, ): Promise { @@ -138,12 +150,6 @@ export class ClientDynex extends BaseClient { throw new NotImplemented(); } - override getTransactionStatuses(): Promise< - (TransactionStatus | undefined)[] - > { - throw new NotImplemented(); - } - override getFeePricePerUnit(): Promise { throw new NotImplemented(); } @@ -166,4 +172,10 @@ export class ClientDynex extends BaseClient { override getBalances(): Promise<(BigNumber | undefined)[]> { throw new NotImplemented(); } + + override getTransactionStatuses(): Promise< + (TransactionStatus | undefined)[] + > { + throw new NotImplemented(); + } } diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index 5d28c7e5e59..48c7f31fa21 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4488,7 +4488,8 @@ const serverPresetNetworks = [ 'name': 'Dynex', 'rpcURLs': [ { - 'url': 'https://dynexrpc.dynexcoin.org:18334', + 'url': + 'https://node-fra-infra-01.dynexcoin.org/rpc/34e68104-238a-43ad-953d-83e9ea76c810', }, ], 'shortcode': 'dnx', From e97af8ce1309da2a4a799b992ce107de7a6a6e9c Mon Sep 17 00:00:00 2001 From: weatherstar Date: Mon, 22 Apr 2024 16:00:09 +0800 Subject: [PATCH 08/16] chore: update locale --- packages/components/src/locale/ar.json | 3 +++ packages/components/src/locale/bn.json | 3 +++ packages/components/src/locale/de.json | 3 +++ packages/components/src/locale/en-US.json | 3 +++ packages/components/src/locale/es.json | 3 +++ packages/components/src/locale/fil.json | 3 +++ packages/components/src/locale/fr_FR.json | 3 +++ packages/components/src/locale/hi_IN.json | 3 +++ packages/components/src/locale/id.json | 3 +++ packages/components/src/locale/it_IT.json | 3 +++ packages/components/src/locale/ja_JP.json | 3 +++ packages/components/src/locale/ko_KR.json | 3 +++ packages/components/src/locale/mn_MN.json | 3 +++ packages/components/src/locale/pt.json | 3 +++ packages/components/src/locale/pt_BR.json | 3 +++ packages/components/src/locale/ru.json | 3 +++ packages/components/src/locale/th_TH.json | 3 +++ packages/components/src/locale/uk_UA.json | 3 +++ packages/components/src/locale/vi.json | 3 +++ packages/components/src/locale/zh-CN.json | 3 +++ packages/components/src/locale/zh_HK.json | 3 +++ 21 files changed, 63 insertions(+) diff --git a/packages/components/src/locale/ar.json b/packages/components/src/locale/ar.json index 8cc7e5cb0fc..12b923a8ed4 100644 --- a/packages/components/src/locale/ar.json +++ b/packages/components/src/locale/ar.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "الوجوه الضاحكة والأشخاص", "emoji_type_symbols": "حرف او رمز", "emoji_type_travel_places": "السفر والأماكن", + "empty__chain_support_wallettype_only": "الشبكة المحددة تدعم فقط {walletType}", "empty__connection_failed_desc": "يُرجى تحديث رمز الاستجابة السريعة أو التحقق من اتصال الشبكة والمحاولة مرة أخرى.", "empty__creating_data": "جارٍ تحضير محفظتك ...", "empty__creating_data_desc": "قد يستغرق التحميل الأولي بعض الوقت ، فلا تتردد في العودة إلى هذه الصفحة لاحقًا.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "تم إرسال المعاملة", "modal__transaction_submitted_unconfirmed": "تم تقديم المعاملة ، لكن حالة المعاملة غير معروفة.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "قم بتشغيل NFC ودع \"OneKey\" يقوم بتوصيل أجهزتك", + "modal__unable_to_accelerate": "غير قادر على التسريع", + "modal__unable_to_accelerate_desc": "هناك معاملة جارية قبل هذه. يرجى تسريع تلك المعاملة أولاً.", "modal__unable_to_connect": "غير قادر على الاتصال", "modal__unable_to_connect_desc": "جهازك الحالي لا يدعم NFC ، استبدله بجهاز مزود بتقنية NFC وحاول مرة أخرى", "modal__unlock_vault": "افتح Vault", diff --git a/packages/components/src/locale/bn.json b/packages/components/src/locale/bn.json index b128c23f9ed..f7a10d84e93 100644 --- a/packages/components/src/locale/bn.json +++ b/packages/components/src/locale/bn.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "স্মাইলিস এবং মানুষ", "emoji_type_symbols": "প্রতীক", "emoji_type_travel_places": "ভ্রমণ এবং স্থান", + "empty__chain_support_wallettype_only": "নির্বাচিত নেটওয়ার্ক শুধুমাত্র {walletType} সমর্থন করে", "empty__connection_failed_desc": "অনুগ্রহ করে QR কোড রিফ্রেশ করুন বা আপনার নেটওয়ার্ক সংযোগ পরীক্ষা করুন এবং আবার চেষ্টা করুন।", "empty__creating_data": "আপনার পোর্টফোলিও প্রস্তুত করা হচ্ছে...", "empty__creating_data_desc": "প্রাথমিক লোড হতে কিছু সময় লাগতে পারে, পরে নির্দ্বিধায় এই পৃষ্ঠায় ফিরে যান।", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "লেনদেন জমা দেওয়া হয়েছে", "modal__transaction_submitted_unconfirmed": "লেনদেন দায়ের করা হয়েছে, কিন্তু লেনদেনের অবস্থা অজানা.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "NFC চালু করুন এবং \"OneKey\" কে আপনার হার্ডওয়্যার ডিভাইসগুলিকে সংযুক্ত করতে দিন৷", + "modal__unable_to_accelerate": "ত্বরণ করতে অক্ষম", + "modal__unable_to_accelerate_desc": "এটির আগে একটি লেনদেন চলছে। দয়া করে প্রথমে ঐ লেনদেনটি ত্বরিত করুন।", "modal__unable_to_connect": "সংযোগ স্থাপন করতে ব্যর্থ", "modal__unable_to_connect_desc": "আপনার বর্তমান ডিভাইসটি NFC সমর্থন করে না, এটিকে একটি NFC-সক্ষম ডিভাইস দিয়ে প্রতিস্থাপন করুন এবং আবার চেষ্টা করুন", "modal__unlock_vault": "ভল্ট আনলক করুন", diff --git a/packages/components/src/locale/de.json b/packages/components/src/locale/de.json index 093ca1cab51..bb461eef786 100644 --- a/packages/components/src/locale/de.json +++ b/packages/components/src/locale/de.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Smileys & Menschen", "emoji_type_symbols": "Symbole", "emoji_type_travel_places": "Reisen & Orte", + "empty__chain_support_wallettype_only": "Das ausgewählte Netzwerk unterstützt nur {walletType}", "empty__connection_failed_desc": "Bitte aktualisieren Sie den QR-Code oder überprüfen Sie Ihre Netzwerkverbindung und versuchen Sie es erneut.", "empty__creating_data": "Vorbereiten Ihres Portfolios…", "empty__creating_data_desc": "Das erste Laden kann eine Weile dauern. Sie können später gerne zu dieser Seite zurückkehren.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transaktion übermittelt", "modal__transaction_submitted_unconfirmed": "Die Transaktion wurde eingereicht, der Status der Transaktion ist jedoch unbekannt.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "NFC einschalten und \"OneKey\" Ihre Hardware-Geräte verbinden lassen", + "modal__unable_to_accelerate": "Beschleunigen nicht möglich", + "modal__unable_to_accelerate_desc": "Es gibt eine laufende Transaktion vor dieser. Bitte beschleunigen Sie zuerst diese Transaktion.", "modal__unable_to_connect": "Keine Verbindung möglich", "modal__unable_to_connect_desc": "Ihr aktuelles Gerät unterstützt kein NFC, ersetzen Sie es durch ein NFC-fähiges Gerät und versuchen Sie es erneut.", "modal__unlock_vault": "Tresor freischalten", diff --git a/packages/components/src/locale/en-US.json b/packages/components/src/locale/en-US.json index 061671f112e..92f17565f75 100644 --- a/packages/components/src/locale/en-US.json +++ b/packages/components/src/locale/en-US.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Smileys & People", "emoji_type_symbols": "Symbols", "emoji_type_travel_places": "Travel & Places", + "empty__chain_support_wallettype_only": "Selected network supports only {walletType}", "empty__connection_failed_desc": "Please refresh the QR code or check your network connection and try again.", "empty__creating_data": "Preparing your Portfolio…", "empty__creating_data_desc": "Initial loading may take a while, feel free to return to this page later.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transaction Submitted", "modal__transaction_submitted_unconfirmed": "The transaction has been filed, but the status of the transaction is unknown.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Turn on NFC and Let \"OneKey\" Connect Your Hardware Devices", + "modal__unable_to_accelerate": "Unable to Accelerate", + "modal__unable_to_accelerate_desc": "There is an ongoing transaction before this one. Please accelerate that transaction first.", "modal__unable_to_connect": "Unable to connect", "modal__unable_to_connect_desc": "Your current device does not support NFC, replace it with an NFC-enabled device and try again", "modal__unlock_vault": "Unlock Vault", diff --git a/packages/components/src/locale/es.json b/packages/components/src/locale/es.json index 0dfb3fa7b55..2cb6fe17fb5 100644 --- a/packages/components/src/locale/es.json +++ b/packages/components/src/locale/es.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Emoticonos y personas", "emoji_type_symbols": "Símbolos", "emoji_type_travel_places": "Viajes y lugares", + "empty__chain_support_wallettype_only": "La red seleccionada solo admite {walletType}", "empty__connection_failed_desc": "Actualiza el código QR o verifica tu conexión de red y vuelve a intentarlo.", "empty__creating_data": "Preparando tu Portafolio…", "empty__creating_data_desc": "La carga inicial puede tardar un poco, no dude en volver a esta página más tarde.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transacción enviada", "modal__transaction_submitted_unconfirmed": "La transacción se ha archivado, pero se desconoce el estado de la transacción.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Activar NFC y dejar que \"OneKey\" conecte tus dispositivos físicos", + "modal__unable_to_accelerate": "No se puede acelerar", + "modal__unable_to_accelerate_desc": "Hay una transacción en curso antes de esta. Por favor, acelere esa transacción primero.", "modal__unable_to_connect": "No se puede conectar", "modal__unable_to_connect_desc": "Tu dispositivo actual no es compatible con NFC, sustitúyelo por un dispositivo compatible con NFC e inténtalo de nuevo", "modal__unlock_vault": "Desbloquear bóveda", diff --git a/packages/components/src/locale/fil.json b/packages/components/src/locale/fil.json index c6b440b2a9b..5e424d92270 100644 --- a/packages/components/src/locale/fil.json +++ b/packages/components/src/locale/fil.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Mga Smiley at Tao", "emoji_type_symbols": "Mga simbolo", "emoji_type_travel_places": "Paglalakbay at Lugar", + "empty__chain_support_wallettype_only": "Ang napiling network ay sumusuporta lamang sa {walletType}", "empty__connection_failed_desc": "Paki-refresh ang QR code o tingnan ang iyong koneksyon sa network at subukang muli.", "empty__creating_data": "Inihahanda ang iyong Portfolio…", "empty__creating_data_desc": "Maaaring magtagal ang paunang paglo-load, huwag mag-atubiling bumalik sa page na ito mamaya.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Naisumite ang Transaksyon", "modal__transaction_submitted_unconfirmed": "Nai-file na ang transaksyon, ngunit hindi alam ang status ng transaksyon.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "I-on ang NFC at Hayaan ang \"OneKey\" na Ikonekta ang Iyong Mga Hardware Device", + "modal__unable_to_accelerate": "Hindi Makapagpabilis", + "modal__unable_to_accelerate_desc": "May isang kasalukuyang transaksyon bago ito. Pakibilisan muna ang transaksyon na iyon.", "modal__unable_to_connect": "Hindi maka konekta", "modal__unable_to_connect_desc": "Ang iyong kasalukuyang device ay hindi sumusuporta sa NFC, palitan ito ng isang NFC-enabled na device at subukang muli", "modal__unlock_vault": "I-unlock ang Vault", diff --git a/packages/components/src/locale/fr_FR.json b/packages/components/src/locale/fr_FR.json index b0f399e2cb7..e8a332f15e5 100644 --- a/packages/components/src/locale/fr_FR.json +++ b/packages/components/src/locale/fr_FR.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Smileys et personnes", "emoji_type_symbols": "Symboles", "emoji_type_travel_places": "Voyages et lieux", + "empty__chain_support_wallettype_only": "Le réseau sélectionné ne prend en charge que {walletType}", "empty__connection_failed_desc": "Veuillez actualiser le code QR ou vérifier votre connexion réseau et réessayer.", "empty__creating_data": "Préparation de votre dossier…", "empty__creating_data_desc": "Le chargement initial peut prendre un certain temps, n'hésitez pas à revenir sur cette page plus tard.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transaction soumise", "modal__transaction_submitted_unconfirmed": "La transaction a été déposée, mais le statut de la transaction est inconnu.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Activez la technologie NFC et laissez \"OneKey\" connecter vos périphériques matériels.", + "modal__unable_to_accelerate": "Impossible d'accélérer", + "modal__unable_to_accelerate_desc": "Il y a une transaction en cours avant celle-ci. Veuillez accélérer cette transaction en premier.", "modal__unable_to_connect": "Impossible de se connecter", "modal__unable_to_connect_desc": "Votre appareil actuel ne prend pas en charge la technologie NFC. Remplacez-le par un appareil compatible NFC et réessayez.", "modal__unlock_vault": "Déverrouiller le coffre-fort", diff --git a/packages/components/src/locale/hi_IN.json b/packages/components/src/locale/hi_IN.json index 100861070e3..8f5f43264b5 100644 --- a/packages/components/src/locale/hi_IN.json +++ b/packages/components/src/locale/hi_IN.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "स्माइलीज और लोग", "emoji_type_symbols": "प्रतीक", "emoji_type_travel_places": "यात्रा और स्थान", + "empty__chain_support_wallettype_only": "चयनित नेटवर्क केवल {walletType} का समर्थन करता है", "empty__connection_failed_desc": "कृपया क्यूआर कोड रीफ्रेश करें या अपने नेटवर्क कनेक्शन की जांच करें और पुनः प्रयास करें।", "empty__creating_data": "आपका पोर्टफोलियो तैयार किया जा रहा है...", "empty__creating_data_desc": "प्रारंभिक लोडिंग में कुछ समय लग सकता है, बाद में इस पृष्ठ पर लौटने में संकोच न करें।", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "लेन-देन सबमिट किया गया", "modal__transaction_submitted_unconfirmed": "लेन-देन दायर किया गया है, लेकिन लेन-देन की स्थिति अज्ञात है।", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "एनएफसी चालू करें और \"वनकी\" को अपने हार्डवेयर उपकरणों से कनेक्ट करने दें", + "modal__unable_to_accelerate": "गति बढ़ाने में असमर्थ", + "modal__unable_to_accelerate_desc": "इससे पहले एक लेन-देन चल रहा है। कृपया पहले उस लेन-देन को तेज करें।", "modal__unable_to_connect": "कनेक्ट करने में असमर्थ", "modal__unable_to_connect_desc": "आपका वर्तमान डिवाइस एनएफसी का समर्थन नहीं करता है, इसे एनएफसी-सक्षम डिवाइस से बदलें और फिर से प्रयास करें", "modal__unlock_vault": "तिजोरी अनलॉक करें", diff --git a/packages/components/src/locale/id.json b/packages/components/src/locale/id.json index 20b3fcc7df0..775818306b9 100644 --- a/packages/components/src/locale/id.json +++ b/packages/components/src/locale/id.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Smiley & Orang", "emoji_type_symbols": "Simbol", "emoji_type_travel_places": "Perjalanan & Tempat", + "empty__chain_support_wallettype_only": "Jaringan yang dipilih hanya mendukung {walletType}", "empty__connection_failed_desc": "Segarkan kode QR atau periksa koneksi jaringan Anda dan coba lagi.", "empty__creating_data": "Mempersiapkan Portofolio Anda…", "empty__creating_data_desc": "Pemuatan awal mungkin memakan waktu cukup lama, jangan ragu untuk kembali ke halaman ini nanti.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transaksi Dikirim", "modal__transaction_submitted_unconfirmed": "Transaksi telah diajukan, tetapi status transaksi tidak diketahui.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Nyalakan NFC dan Izinkan \"OneKey\" Menghubungkan Perangkat Keras Anda", + "modal__unable_to_accelerate": "Tidak Dapat Mempercepat", + "modal__unable_to_accelerate_desc": "Ada transaksi yang sedang berlangsung sebelum ini. Harap percepat transaksi tersebut terlebih dahulu.", "modal__unable_to_connect": "Tidak dapat terhubung", "modal__unable_to_connect_desc": "Perangkat Anda saat ini tidak mendukung NFC, ganti dengan perangkat yang mendukung NFC dan coba lagi", "modal__unlock_vault": "Buka Kunci Vault", diff --git a/packages/components/src/locale/it_IT.json b/packages/components/src/locale/it_IT.json index 9858a00cb6d..36c2ec9fd3c 100644 --- a/packages/components/src/locale/it_IT.json +++ b/packages/components/src/locale/it_IT.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Smileys e persone", "emoji_type_symbols": "Simboli", "emoji_type_travel_places": "Viaggi e luoghi", + "empty__chain_support_wallettype_only": "La rete selezionata supporta solo {walletType}", "empty__connection_failed_desc": "Aggiorna il codice QR o controlla la connessione di rete e riprova.", "empty__creating_data": "Preparare il tuo portfolio...", "empty__creating_data_desc": "Il caricamento iniziale potrebbe richiedere del tempo, sentiti libero di tornare su questa pagina più tardi.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transazione inviata", "modal__transaction_submitted_unconfirmed": "La transazione è stata archiviata, ma lo stato della transazione è sconosciuto.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Attiva NFC e lascia che \"OneKey\" colleghi i tuoi dispositivi hardware", + "modal__unable_to_accelerate": "Impossibile accelerare", + "modal__unable_to_accelerate_desc": "C'è una transazione in corso prima di questa. Si prega di accelerare prima quella transazione.", "modal__unable_to_connect": "Impossibile connettersi", "modal__unable_to_connect_desc": "Il tuo attuale dispositivo non supporta l'NFC, sostituiscilo con un dispositivo abilitato all'NFC e riprova", "modal__unlock_vault": "Sblocca il caveau", diff --git a/packages/components/src/locale/ja_JP.json b/packages/components/src/locale/ja_JP.json index e4470d3690c..c3f7f44424b 100644 --- a/packages/components/src/locale/ja_JP.json +++ b/packages/components/src/locale/ja_JP.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "笑顔&ピープル", "emoji_type_symbols": "シンボルマーク", "emoji_type_travel_places": "観光&場所", + "empty__chain_support_wallettype_only": "選択されたネットワークは、{walletType}のみをサポートしています", "empty__connection_failed_desc": "QR コードを更新するか、ネットワーク接続を確認してから、もう一度お試しください。", "empty__creating_data": "ポートフォリオを準備中…", "empty__creating_data_desc": "初期の読み込みには時間がかかる場合があるので、後でこのページに戻ってください。", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "取引送信済み", "modal__transaction_submitted_unconfirmed": "トランザクションが提出されたが、ステータスは不明である。", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "NFCをオンにして、「OneKey」をハードウェアデバイスに接続する", + "modal__unable_to_accelerate": "加速できません", + "modal__unable_to_accelerate_desc": "これより前に進行中の取引があります。まずその取引を加速してください。", "modal__unable_to_connect": "接続できない", "modal__unable_to_connect_desc": "現在お使いのデバイスがNFCに対応していないため、NFC対応のデバイスに交換してから、もう一回お試しください。", "modal__unlock_vault": "ボールトのロックを解除します", diff --git a/packages/components/src/locale/ko_KR.json b/packages/components/src/locale/ko_KR.json index 9e746460612..bffe732fc02 100644 --- a/packages/components/src/locale/ko_KR.json +++ b/packages/components/src/locale/ko_KR.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "스마일리 & 사람들", "emoji_type_symbols": "기호", "emoji_type_travel_places": "여행 및 장소", + "empty__chain_support_wallettype_only": "선택한 네트워크는 오직 {walletType}만 지원합니다", "empty__connection_failed_desc": "QR 코드를 새로 고치거나 네트워크 연결을 확인하고 다시 시도하십시오.", "empty__creating_data": "포트폴리오 준비 중…", "empty__creating_data_desc": "초기 로딩에는 다소 시간이 걸릴 수 있습니다. 나중에 이 페이지로 돌아와도 됩니다.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "제출된 거래", "modal__transaction_submitted_unconfirmed": "거래가 접수되었지만 거래 상태를 알 수 없습니다.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "NFC를 켜고 \"OneKey\"가 하드웨어 장치를 연결하게 하십시오", + "modal__unable_to_accelerate": "가속할 수 없음", + "modal__unable_to_accelerate_desc": "이전에 진행 중인 거래가 있습니다. 먼저 그 거래를 가속화해 주세요.", "modal__unable_to_connect": "연결할 수 없습니다", "modal__unable_to_connect_desc": "현재 장치가 NFC를 지원하지 않습니다. NFC 지원 장치로 교체하고 다시 시도하십시오.", "modal__unlock_vault": "볼트 잠금 해제", diff --git a/packages/components/src/locale/mn_MN.json b/packages/components/src/locale/mn_MN.json index e08223a4508..c227648d154 100644 --- a/packages/components/src/locale/mn_MN.json +++ b/packages/components/src/locale/mn_MN.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Инээмсэглэл ба хүмүүс", "emoji_type_symbols": "Тэмдгүүд", "emoji_type_travel_places": "Аялал ба газрууд", + "empty__chain_support_wallettype_only": "Сонгосон сүлжээ нь зөвхөн {walletType} -г дэмждэг", "empty__connection_failed_desc": "QR кодыг сэргээнэ үү эсвэл сүлжээний холболтоо шалгаад дахин оролдоно уу.", "empty__creating_data": "Таны багцыг бэлдэж байна...", "empty__creating_data_desc": "Эхний ачааллахад хэсэг хугацаа шаардагдах тул дараа нь энэ хуудас руу буцна уу.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Гүйлгээ илгээсэн", "modal__transaction_submitted_unconfirmed": "Гүйлгээ хийгдсэн боловч гүйлгээний статус тодорхойгүй байна.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "NFC-г асаагаад \"OneKey\"-д техник хангамжийн төхөөрөмжүүдээ холбохыг зөвшөөрнө үү", + "modal__unable_to_accelerate": "Хурдасгах боломжгүй", + "modal__unable_to_accelerate_desc": "Энэ өмнө нь үргэлжилж буй гүйлгээ байна. Та эхлээд тухайн гүйлгээг хурдасгана уу.", "modal__unable_to_connect": "Холбогдож чадахгүй", "modal__unable_to_connect_desc": "Таны одоогийн төхөөрөмж NFC-г дэмждэггүй тул үүнийг NFC идэвхжүүлсэн төхөөрөмжөөр сольж, дахин оролдоно уу", "modal__unlock_vault": "Vault-н түгжээг тайл", diff --git a/packages/components/src/locale/pt.json b/packages/components/src/locale/pt.json index 7304d8287ec..f8a783aa51d 100644 --- a/packages/components/src/locale/pt.json +++ b/packages/components/src/locale/pt.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Sorrisos e pessoas", "emoji_type_symbols": "Símbolos", "emoji_type_travel_places": "Viagens e lugares", + "empty__chain_support_wallettype_only": "A rede selecionada suporta apenas {walletType}", "empty__connection_failed_desc": "Atualize o código QR ou verifique sua conexão de rede e tente novamente.", "empty__creating_data": "Preparando seu Portfólio...", "empty__creating_data_desc": "O carregamento inicial pode demorar um pouco, sinta-se à vontade para retornar a esta página mais tarde.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transação enviada", "modal__transaction_submitted_unconfirmed": "A transação foi registrada, mas o status da transação é desconhecido.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Ative o NFC e deixe o \"OneKey\" conectar seus dispositivos de hardware", + "modal__unable_to_accelerate": "Incapaz de acelerar", + "modal__unable_to_accelerate_desc": "Existe uma transação em andamento antes desta. Por favor, acelere essa transação primeiro.", "modal__unable_to_connect": "Incapaz de conectar", "modal__unable_to_connect_desc": "Seu dispositivo atual não suporta NFC, substitua-o por um dispositivo habilitado para NFC e tente novamente", "modal__unlock_vault": "Desbloquear cofre", diff --git a/packages/components/src/locale/pt_BR.json b/packages/components/src/locale/pt_BR.json index b9c2af04ceb..1d09e3a33ed 100644 --- a/packages/components/src/locale/pt_BR.json +++ b/packages/components/src/locale/pt_BR.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Emoticons e Pessoas", "emoji_type_symbols": "Símbolos", "emoji_type_travel_places": "Viagens e lugares", + "empty__chain_support_wallettype_only": "A rede selecionada suporta apenas {walletType}", "empty__connection_failed_desc": "Atualize o código QR ou verifique sua conexão de rede e tente novamente.", "empty__creating_data": "Preparando seu portfólio...", "empty__creating_data_desc": "O carregamento inicial pode demorar um pouco, sinta-se à vontade para voltar a esta página mais tarde.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Transação enviada", "modal__transaction_submitted_unconfirmed": "A transação foi registrada, mas o status da transação é desconhecido.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Ative o NFC e permita que \"OneKey\" conecte seus dispositivos de hardware.", + "modal__unable_to_accelerate": "Incapaz de acelerar", + "modal__unable_to_accelerate_desc": "Existe uma transação em andamento antes desta. Por favor, acelere essa transação primeiro.", "modal__unable_to_connect": "Não é possível conectar", "modal__unable_to_connect_desc": "Seu dispositivo atual não é compatível com NFC, substitua-o por um dispositivo compatível com NFC e tente novamente", "modal__unlock_vault": "Desbloquear o cofre", diff --git a/packages/components/src/locale/ru.json b/packages/components/src/locale/ru.json index 9308befd620..faaa4e2b5b9 100644 --- a/packages/components/src/locale/ru.json +++ b/packages/components/src/locale/ru.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Смайлики и люди", "emoji_type_symbols": "Символы", "emoji_type_travel_places": "Путешествия и места", + "empty__chain_support_wallettype_only": "Выбранная сеть поддерживает только {walletType}", "empty__connection_failed_desc": "Обновите QR-код или проверьте подключение к сети и повторите попытку.", "empty__creating_data": "Подготовка портфолио…", "empty__creating_data_desc": "Первоначальная загрузка может занять некоторое время, не стесняйтесь вернуться на эту страницу позже.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Транзакция отправлена", "modal__transaction_submitted_unconfirmed": "Сделка зарегистрирована, но статус транзакции неизвестен.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Включите NFC и позвольте \"OneKey\" подключать ваши аппаратные устройства", + "modal__unable_to_accelerate": "Невозможно ускориться", + "modal__unable_to_accelerate_desc": "Перед этой идет другая транзакция. Пожалуйста, сначала ускорьте эту транзакцию.", "modal__unable_to_connect": "Невозможно подключиться", "modal__unable_to_connect_desc": "Ваше текущее устройство не поддерживает NFC, замените его на устройство с поддержкой NFC и попробуйте снова", "modal__unlock_vault": "Разблокировать хранилище", diff --git a/packages/components/src/locale/th_TH.json b/packages/components/src/locale/th_TH.json index f0efe345361..de71bd12216 100644 --- a/packages/components/src/locale/th_TH.json +++ b/packages/components/src/locale/th_TH.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "รอยยิ้มและผู้คน", "emoji_type_symbols": "สัญลักษณ์", "emoji_type_travel_places": "การเดินทางและสถานที่", + "empty__chain_support_wallettype_only": "เครือข่ายที่เลือกสนับสนุนเฉพาะ {walletType} เท่านั้น", "empty__connection_failed_desc": "โปรดรีเฟรชรหัส QR หรือตรวจสอบการเชื่อมต่อเครือข่ายแล้วลองอีกครั้ง", "empty__creating_data": "กำลังเตรียมพอร์ตโฟลิโอของคุณ…", "empty__creating_data_desc": "การโหลดครั้งแรกอาจใช้เวลาสักครู่ โปรดกลับมาที่หน้านี้ในภายหลัง", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "ส่งธุรกรรมแล้ว", "modal__transaction_submitted_unconfirmed": "ยื่นธุรกรรมแล้ว แต่ไม่ทราบสถานะของธุรกรรม", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "เปิด NFC และให้ \"OneKey\" เชื่อมต่ออุปกรณ์ฮาร์ดแวร์ของคุณ", + "modal__unable_to_accelerate": "ไม่สามารถเร่งความเร็วได้", + "modal__unable_to_accelerate_desc": "มีการทำธุรกรรมที่กำลังดำเนินการอยู่ก่อนการทำธุรกรรมนี้ โปรดเร่งการทำธุรกรรมนั้นก่อน", "modal__unable_to_connect": "ไม่สามารถเชื่อมต่อ", "modal__unable_to_connect_desc": "อุปกรณ์ปัจจุบันของคุณไม่รองรับ NFC ให้แทนที่ด้วยอุปกรณ์ที่เปิดใช้งาน NFC แล้วลองอีกครั้ง", "modal__unlock_vault": "ปลดล็อกห้องนิรภัย", diff --git a/packages/components/src/locale/uk_UA.json b/packages/components/src/locale/uk_UA.json index 305894af070..6127cf8b101 100644 --- a/packages/components/src/locale/uk_UA.json +++ b/packages/components/src/locale/uk_UA.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Смайли та люди", "emoji_type_symbols": "символи", "emoji_type_travel_places": "Подорожі та місця", + "empty__chain_support_wallettype_only": "Обрана мережа підтримує лише {walletType}", "empty__connection_failed_desc": "Оновіть QR-код або перевірте підключення до мережі та повторіть спробу.", "empty__creating_data": "Підготовка вашого портфоліо…", "empty__creating_data_desc": "Початкове завантаження може зайняти деякий час, не соромтеся повернутися до цієї сторінки пізніше.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Трансакцію надіслано", "modal__transaction_submitted_unconfirmed": "Транзакцію подано, але статус трансакції невідомий.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Увімкніть NFC і дозвольте «OneKey» підключити ваші апаратні пристрої", + "modal__unable_to_accelerate": "Неможливо прискорити", + "modal__unable_to_accelerate_desc": "Існує незавершена транзакція перед цією. Будь ласка, спочатку прискоріть ту транзакцію.", "modal__unable_to_connect": "Не може підключитися", "modal__unable_to_connect_desc": "Ваш поточний пристрій не підтримує NFC, замініть його на пристрій із підтримкою NFC та повторіть спробу", "modal__unlock_vault": "Розблокувати сховище", diff --git a/packages/components/src/locale/vi.json b/packages/components/src/locale/vi.json index 30da2effa65..613a5342c02 100644 --- a/packages/components/src/locale/vi.json +++ b/packages/components/src/locale/vi.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "Biểu tượng mặt cười & Con người", "emoji_type_symbols": "Ký hiệu", "emoji_type_travel_places": "Du lịch & Địa điểm", + "empty__chain_support_wallettype_only": "Mạng lựa chọn chỉ hỗ trợ {walletType}", "empty__connection_failed_desc": "Vui lòng làm mới mã QR hoặc kiểm tra kết nối mạng của bạn và thử lại.", "empty__creating_data": "Đang chuẩn bị Danh mục đầu tư của bạn…", "empty__creating_data_desc": "Quá trình tải ban đầu có thể mất một lúc, vui lòng quay lại trang này sau.", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "Giao dịch đã được gửi", "modal__transaction_submitted_unconfirmed": "Giao dịch đã được nộp, nhưng trạng thái của giao dịch không xác định.", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "Bật NFC và cho phép \"OneKey\" kết nối thiết bị phần cứng của bạn", + "modal__unable_to_accelerate": "Không thể tăng tốc", + "modal__unable_to_accelerate_desc": "Có một giao dịch đang diễn ra trước giao dịch này. Vui lòng xử lý giao dịch đó trước.", "modal__unable_to_connect": "Không thể kết nối", "modal__unable_to_connect_desc": "Thiết bị hiện tại của bạn không hỗ trợ NFC, hãy thay thế nó bằng thiết bị hỗ trợ NFC và thử lại", "modal__unlock_vault": "Mở khóa Vault", diff --git a/packages/components/src/locale/zh-CN.json b/packages/components/src/locale/zh-CN.json index 022dbe45461..0f9b6884681 100644 --- a/packages/components/src/locale/zh-CN.json +++ b/packages/components/src/locale/zh-CN.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "笑脸和人像", "emoji_type_symbols": "符号", "emoji_type_travel_places": "旅行和地点", + "empty__chain_support_wallettype_only": "所选网络仅支持{walletType}", "empty__connection_failed_desc": "请刷新二维码或检查您的网络连接再重试", "empty__creating_data": "正在准备数据...", "empty__creating_data_desc": "初始加载可能需要一段时间,您可以先离开稍后再返回此页面查看", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "交易已提交", "modal__transaction_submitted_unconfirmed": "交易已提交,但交易状态未知", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "开启 NFC,让“OneKey”连接您的硬件设备", + "modal__unable_to_accelerate": "无法执行加速", + "modal__unable_to_accelerate_desc": "在这笔交易之前,还有交易正在进行,请先对最早的一笔交易进行加速", "modal__unable_to_connect": "无法连接到设备", "modal__unable_to_connect_desc": "当前设备不支持 NFC,请更换一台支持 NFC 的设备,然后再试一次", "modal__unlock_vault": "解锁保险库", diff --git a/packages/components/src/locale/zh_HK.json b/packages/components/src/locale/zh_HK.json index 2ee763052cf..69036bd6437 100644 --- a/packages/components/src/locale/zh_HK.json +++ b/packages/components/src/locale/zh_HK.json @@ -1087,6 +1087,7 @@ "emoji_type_smileys_people": "笑臉和人像", "emoji_type_symbols": "符號", "emoji_type_travel_places": "旅行和地點", + "empty__chain_support_wallettype_only": "所選網路僅支援{walletType}", "empty__connection_failed_desc": "請刷新二維碼或檢查您的網絡連接再重試", "empty__creating_data": "正在準備數據...", "empty__creating_data_desc": "初始加載可能需要一段時間,您可以先離開稍後再返回此頁面查看", @@ -2155,6 +2156,8 @@ "modal__transaction_submitted": "交易已提交", "modal__transaction_submitted_unconfirmed": "交易已提交,但交易狀態未知。", "modal__turn_on_nfc_and_let_onekey_connect_your_hardware_devices": "開啟 NFC,讓“OneKey”連接您的硬件設備", + "modal__unable_to_accelerate": "無法執行加速", + "modal__unable_to_accelerate_desc": "在這筆交易之前,還有交易正在進行,請先對最早的一筆交易進行加速", "modal__unable_to_connect": "無法連接到設備", "modal__unable_to_connect_desc": "當前設備不支持NFC,請更換一台支持NFC 的設備,然後再試一次", "modal__unlock_vault": "解鎖保險庫", From 7c6060dc9c45112998c49e414a4cc0f3a0d51a8f Mon Sep 17 00:00:00 2001 From: weatherstar Date: Mon, 22 Apr 2024 21:47:29 +0800 Subject: [PATCH 09/16] chore: enable on hw classic only tip --- .../engine/src/vaults/impl/dynex/settings.ts | 2 ++ packages/engine/src/vaults/types.ts | 2 ++ .../components/IdentityAssertion/index.tsx | 23 +++++++++++++++ .../AccountList/index.tsx | 22 +++++++++++++++ .../NetworkAccountSelectorModal/Header.tsx | 28 +++++++++++++++++-- .../kit/src/utils/hardware/OneKeyHardware.ts | 3 ++ packages/kit/src/utils/hardware/index.ts | 2 +- 7 files changed, 79 insertions(+), 3 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts index 029460612f1..2f4013a51b3 100644 --- a/packages/engine/src/vaults/impl/dynex/settings.ts +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -23,6 +23,8 @@ const settings: IVaultSettings = Object.freeze({ withPaymentId: true, + enableOnClassicOnly: true, + accountNameInfo: { default: { prefix: 'DNX', diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index f89fed331b4..40fd48cb398 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -167,6 +167,8 @@ export type IVaultSettings = { hideInAllNetworksMode?: boolean; mnemonicAsPrivatekey?: boolean; + + enableOnClassicOnly?: boolean; }; export type IVaultFactoryOptions = { networkId: string; diff --git a/packages/kit/src/components/IdentityAssertion/index.tsx b/packages/kit/src/components/IdentityAssertion/index.tsx index 303f73a8230..52e505e0b92 100644 --- a/packages/kit/src/components/IdentityAssertion/index.tsx +++ b/packages/kit/src/components/IdentityAssertion/index.tsx @@ -10,6 +10,7 @@ import { useActiveWalletAccount } from '@onekeyhq/kit/src/hooks'; import { RootRoutes } from '@onekeyhq/kit/src/routes/routesEnum'; import backgroundApiProxy from '../../background/instance/backgroundApiProxy'; +import { isHwClassic } from '../../utils/hardware'; import { NETWORK_NOT_SUPPORT_CREATE_ACCOUNT_I18N_KEY, useCreateAccountInWallet, @@ -59,6 +60,28 @@ const IdentityAssertion: FC<{ checkCompatibleNetwork?: boolean }> = ({ const hasNoWallet = !walletId; const isAccountCompatibleNetwork = !!accountId && (checkCompatibleNetwork ? isCompatibleNetwork : true); + const enableOnClassicOnly = network?.settings.enableOnClassicOnly; + + if (enableOnClassicOnly && !isHwClassic(wallet?.deviceType)) { + return ( + + + + ); + } if (hasNoWallet) { return ( diff --git a/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/AccountList/index.tsx b/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/AccountList/index.tsx index 01eeff9e219..64593216792 100644 --- a/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/AccountList/index.tsx +++ b/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/AccountList/index.tsx @@ -25,7 +25,9 @@ import { useActiveWalletAccount, useDebounce, useNetwork, + useWallet, } from '../../../../../hooks'; +import { isHwClassic } from '../../../../../utils/hardware'; import { scrollToSectionItem } from '../../../../WalletSelector'; import { AccountSectionLoadingSkeleton } from '../../../AccountSectionLoadingSkeleton'; import { ACCOUNT_SELECTOR_AUTO_SCROLL_DELAY_ACCOUNT } from '../../../consts'; @@ -78,6 +80,26 @@ const EmptyAccountState: FC = ({ networkId, }); const { network } = useNetwork({ networkId }); + const { wallet } = useWallet({ walletId }); + + if ( + network?.settings.enableOnClassicOnly && + !isHwClassic(wallet?.deviceType) + ) { + return ( + + ); + } return ( s.status.firstTimeShowCheckRPCNodeTooltip, ); const [isOpen, setIsOpen] = useState(false); - const { selectedNetwork, isLoading } = accountSelectorInfo; + const { + selectedNetwork, + selectedNetworkSettings, + selectedWallet, + isLoading, + } = accountSelectorInfo; const { loading, status } = useRpcMeasureStatus( (showCustomLegacyHeader ? selectedNetwork?.id : '') ?? '', ); @@ -70,6 +76,24 @@ function Header({ const navigation = useNavigation(); const close = useModalClose(); + const shouldHideCreateAccount = useMemo(() => { + if (hideCreateAccount) { + return true; + } + + if ( + selectedNetworkSettings?.enableOnClassicOnly && + !isHwClassic(selectedWallet?.deviceType) + ) { + return true; + } + return false; + }, [ + hideCreateAccount, + selectedNetworkSettings?.enableOnClassicOnly, + selectedWallet?.deviceType, + ]); + const toCheckNodePage = useCallback(() => { setIsOpen(false); navigation.navigate(ManageNetworkModalRoutes.RPCNode, { @@ -198,7 +222,7 @@ function Header({ getDeviceTypeByDeviceIdUtil(deviceId); +export const isHwClassic = (deviceType: string | undefined): boolean => + deviceType === 'classic' || deviceType === 'classic1s'; + export const getDeviceFirmwareVersion = ( features: IOneKeyDeviceFeatures | undefined, ): IVersionArray => { diff --git a/packages/kit/src/utils/hardware/index.ts b/packages/kit/src/utils/hardware/index.ts index 5c598e62574..8a5edfa900d 100644 --- a/packages/kit/src/utils/hardware/index.ts +++ b/packages/kit/src/utils/hardware/index.ts @@ -2,4 +2,4 @@ export type { SearchDevice, Features } from '@onekeyfe/hd-core'; export { default as deviceUtils } from './deviceUtils'; -export { getDeviceTypeByDeviceId } from './OneKeyHardware'; +export { getDeviceTypeByDeviceId, isHwClassic } from './OneKeyHardware'; From 6426a5c87163203d5e2874a5dc31e62d4c8e345a Mon Sep 17 00:00:00 2001 From: weatherstar Date: Mon, 22 Apr 2024 22:04:09 +0800 Subject: [PATCH 10/16] fix:lint --- packages/engine/src/vaults/impl/dynex/Vault.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index b2b6874545a..fb1009b20fe 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -14,7 +14,6 @@ import { IDecodedTxDirection, type IDecodedTxLegacy, IDecodedTxStatus, - type IEncodedTxUpdateOptions, type IFeeInfo, type IFeeInfoUnit, type ITransferInfo, @@ -36,10 +35,9 @@ import type { TransactionStatus, } from '../../../types/provider'; import type { IHistoryTx, ISignedTxPro } from '../../types'; -import type { EVMDecodedItem } from '../evm/decoder/types'; import type { IEncodedTxDynex, IUnspentOutput } from './types'; -const DEFAULT_TX_MIN_FEE = 1000000; +const DEFAULT_TX_FEE = 1000000; export default class Vault extends VaultBase { keyringMap = { @@ -107,7 +105,7 @@ export default class Vault extends VaultBase { override async fetchFeeInfo(): Promise { const network = await this.getNetwork(); - const minFee = DEFAULT_TX_MIN_FEE; + const minFee = DEFAULT_TX_FEE; const prices = [ new BigNumber(minFee).shiftedBy(-network.feeDecimals).toFixed(), @@ -223,7 +221,7 @@ export default class Vault extends VaultBase { amount: new BigNumber(transferInfo.amount) .shiftedBy(network.decimals) .toFixed(), - fee: new BigNumber(DEFAULT_TX_MIN_FEE).toFixed(), + fee: new BigNumber(DEFAULT_TX_FEE).toFixed(), }); return { @@ -231,7 +229,7 @@ export default class Vault extends VaultBase { to: transferInfo.to, amount: new BigNumber(finalAmount).shiftedBy(-network.decimals).toFixed(), paymentId: transferInfo.paymentId, - fee: new BigNumber(DEFAULT_TX_MIN_FEE) + fee: new BigNumber(DEFAULT_TX_FEE) .shiftedBy(-network.feeDecimals) .toFixed(), inputs, @@ -371,9 +369,7 @@ export default class Vault extends VaultBase { fee: string; unspentOutputs: IUnspentOutput[]; }) { - const inputs = []; let finalAmount = new BigNumber(amount); - const totalAmount = finalAmount.plus(fee); const totalUnspentOutputsAmount = unspentOutputs.reduce( (acc, output) => acc.plus(output.amount), new BigNumber(0), From 696320caf6d96eb723a3bc3d5754edaa9ea6ae89 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 23 Apr 2024 09:23:18 +0800 Subject: [PATCH 11/16] chore: update locale --- packages/components/src/locale/ar.json | 1 + packages/components/src/locale/bn.json | 1 + packages/components/src/locale/de.json | 1 + packages/components/src/locale/en-US.json | 1 + packages/components/src/locale/es.json | 1 + packages/components/src/locale/fil.json | 1 + packages/components/src/locale/fr_FR.json | 1 + packages/components/src/locale/hi_IN.json | 1 + packages/components/src/locale/id.json | 1 + packages/components/src/locale/it_IT.json | 1 + packages/components/src/locale/ja_JP.json | 1 + packages/components/src/locale/ko_KR.json | 1 + packages/components/src/locale/mn_MN.json | 1 + packages/components/src/locale/pt.json | 1 + packages/components/src/locale/pt_BR.json | 1 + packages/components/src/locale/ru.json | 1 + packages/components/src/locale/th_TH.json | 1 + packages/components/src/locale/uk_UA.json | 1 + packages/components/src/locale/vi.json | 1 + packages/components/src/locale/zh-CN.json | 1 + packages/components/src/locale/zh_HK.json | 1 + packages/kit/src/views/Send/modals/PreSendAddress.tsx | 4 ++-- 22 files changed, 23 insertions(+), 2 deletions(-) diff --git a/packages/components/src/locale/ar.json b/packages/components/src/locale/ar.json index 12b923a8ed4..6108cc1dd48 100644 --- a/packages/components/src/locale/ar.json +++ b/packages/components/src/locale/ar.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "سيتم عرض طلباتك هنا.", "empty__your_watchlist_is_empty": "قائمة الرغبات الخاصة بك فارغة", "empty__your_watchlist_is_empty_desc": "أضف الرموز المميزة المفضلة لديك إلى قائمة المراقبة", + "error__payment_id_standard": "يجب أن يكون Payment ID عبارة عن سلسلة هكساديسيمال من 64 حرفًا", "form__24h%_uppercase": "24 ساعة", "form__24h_high": "24 ساعة عالية", "form__24h_low": "24 ساعة منخفضة", diff --git a/packages/components/src/locale/bn.json b/packages/components/src/locale/bn.json index f7a10d84e93..fe631997984 100644 --- a/packages/components/src/locale/bn.json +++ b/packages/components/src/locale/bn.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "আপনার অর্ডার এখানে প্রদর্শিত হবে.", "empty__your_watchlist_is_empty": "আপনার ওয়াচলিস্ট খালি", "empty__your_watchlist_is_empty_desc": "ওয়াচলিস্টে আপনার প্রিয় টোকেন যোগ করুন", + "error__payment_id_standard": "Payment ID অবশ্যই 64 অক্ষরের হেক্স স্ট্রিং হতে হবে", "form__24h%_uppercase": "24H%", "form__24h_high": "24H উচ্চ", "form__24h_low": "24H কম", diff --git a/packages/components/src/locale/de.json b/packages/components/src/locale/de.json index bb461eef786..d6fb2037f32 100644 --- a/packages/components/src/locale/de.json +++ b/packages/components/src/locale/de.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Hier werden Ihre Bestellungen angezeigt.", "empty__your_watchlist_is_empty": "Ihre Beobachtungsliste ist leer", "empty__your_watchlist_is_empty_desc": "Fügen Sie Ihre Lieblingstoken zur Beobachtungsliste hinzu", + "error__payment_id_standard": "Payment ID muss eine 64 Zeichen lange Hexadezimal-Zeichenkette sein", "form__24h%_uppercase": "24H%", "form__24h_high": "24 Stunden hoch", "form__24h_low": "24 Std. niedrig", diff --git a/packages/components/src/locale/en-US.json b/packages/components/src/locale/en-US.json index 92f17565f75..cb818c20c70 100644 --- a/packages/components/src/locale/en-US.json +++ b/packages/components/src/locale/en-US.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Your orders will be displayed here.", "empty__your_watchlist_is_empty": "Your Watchlist is Empty", "empty__your_watchlist_is_empty_desc": "Add your favoite tokens to watchlist", + "error__payment_id_standard": "Payment ID must be a 64 char hex string", "form__24h%_uppercase": "24H%", "form__24h_high": "24h High", "form__24h_low": "24h Low", diff --git a/packages/components/src/locale/es.json b/packages/components/src/locale/es.json index 2cb6fe17fb5..e507c4c890f 100644 --- a/packages/components/src/locale/es.json +++ b/packages/components/src/locale/es.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Tus pedidos se mostrarán aquí.", "empty__your_watchlist_is_empty": "Tu lista de observación está vacía", "empty__your_watchlist_is_empty_desc": "Agrega tus tokens favoritos a la lista de observación", + "error__payment_id_standard": "El Payment ID debe ser una cadena hexadecimal de 64 caracteres", "form__24h%_uppercase": "24H%", "form__24h_high": "24H alto", "form__24h_low": "24 horas bajo", diff --git a/packages/components/src/locale/fil.json b/packages/components/src/locale/fil.json index 5e424d92270..25044e91c7e 100644 --- a/packages/components/src/locale/fil.json +++ b/packages/components/src/locale/fil.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Ang iyong mga order ay ipapakita dito.", "empty__your_watchlist_is_empty": "Walang laman ang iyong Watchlist", "empty__your_watchlist_is_empty_desc": "Idagdag ang iyong mga paboritong token sa watchlist", + "error__payment_id_standard": "Ang Payment ID ay dapat na isang 64 char hex string", "form__24h%_uppercase": "24H%", "form__24h_high": "24H High", "form__24h_low": "24H Mababa", diff --git a/packages/components/src/locale/fr_FR.json b/packages/components/src/locale/fr_FR.json index e8a332f15e5..52ef300946e 100644 --- a/packages/components/src/locale/fr_FR.json +++ b/packages/components/src/locale/fr_FR.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Vos commandes seront affichées ici.", "empty__your_watchlist_is_empty": "Votre liste de suivi est vide", "empty__your_watchlist_is_empty_desc": "Ajoutez vos jetons préférés à la liste de surveillance", + "error__payment_id_standard": "Le Payment ID doit être une chaîne hexadécimale de 64 caractères", "form__24h%_uppercase": "24H%", "form__24h_high": "24H haute", "form__24h_low": "24H bas", diff --git a/packages/components/src/locale/hi_IN.json b/packages/components/src/locale/hi_IN.json index 8f5f43264b5..5ef0da949fd 100644 --- a/packages/components/src/locale/hi_IN.json +++ b/packages/components/src/locale/hi_IN.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "आपके आदेश यहां प्रदर्शित किए जाएंगे।", "empty__your_watchlist_is_empty": "आपकी ध्यानसूची खाली है", "empty__your_watchlist_is_empty_desc": "वॉचलिस्ट में अपने पसंदीदा टोकन जोड़ें", + "error__payment_id_standard": "Payment ID एक 64 वर्ण हेक्स स्ट्रिंग होना चाहिए", "form__24h%_uppercase": "24एच%", "form__24h_high": "24H उच्च", "form__24h_low": "24H कम", diff --git a/packages/components/src/locale/id.json b/packages/components/src/locale/id.json index 775818306b9..5da2dcc255d 100644 --- a/packages/components/src/locale/id.json +++ b/packages/components/src/locale/id.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Pesanan Anda akan ditampilkan di sini.", "empty__your_watchlist_is_empty": "Daftar Pantauan Anda Kosong", "empty__your_watchlist_is_empty_desc": "Tambahkan token favorit Anda ke daftar pantauan", + "error__payment_id_standard": "Payment ID harus berupa string heksadesimal 64 karakter", "form__24h%_uppercase": "24 jam%", "form__24h_high": "24 jam Tinggi", "form__24h_low": "24 jam Rendah", diff --git a/packages/components/src/locale/it_IT.json b/packages/components/src/locale/it_IT.json index 36c2ec9fd3c..f53134ccc39 100644 --- a/packages/components/src/locale/it_IT.json +++ b/packages/components/src/locale/it_IT.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "I tuoi ordini verranno visualizzati qui.", "empty__your_watchlist_is_empty": "La tua watchlist è vuota", "empty__your_watchlist_is_empty_desc": "Aggiungi i tuoi token preferiti alla watchlist", + "error__payment_id_standard": "Il Payment ID deve essere una stringa esadecimale di 64 caratteri", "form__24h%_uppercase": "24H%", "form__24h_high": "Alta 24 ore", "form__24h_low": "Basso 24 ore", diff --git a/packages/components/src/locale/ja_JP.json b/packages/components/src/locale/ja_JP.json index c3f7f44424b..acfda820e6b 100644 --- a/packages/components/src/locale/ja_JP.json +++ b/packages/components/src/locale/ja_JP.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "ここに注文が表示される。", "empty__your_watchlist_is_empty": "あなたのウォッチリストは空です", "empty__your_watchlist_is_empty_desc": "お気に入りのトークンをウォッチリストに追加する", + "error__payment_id_standard": "Payment IDは64文字の16進数文字列でなければなりません", "form__24h%_uppercase": "24時間%", "form__24h_high": "24時間の最高価格", "form__24h_low": "24時間の最低価格", diff --git a/packages/components/src/locale/ko_KR.json b/packages/components/src/locale/ko_KR.json index bffe732fc02..545dd5ccbf0 100644 --- a/packages/components/src/locale/ko_KR.json +++ b/packages/components/src/locale/ko_KR.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "귀하의 주문이 여기에 표시됩니다.", "empty__your_watchlist_is_empty": "관심 목록이 비어 있습니다.", "empty__your_watchlist_is_empty_desc": "관심 목록에 좋아하는 토큰 추가", + "error__payment_id_standard": "Payment ID는 64자리 16진수 문자열이어야 합니다", "form__24h%_uppercase": "24시간%", "form__24h_high": "24시간 높음", "form__24h_low": "24시간 낮음", diff --git a/packages/components/src/locale/mn_MN.json b/packages/components/src/locale/mn_MN.json index c227648d154..af1bea999de 100644 --- a/packages/components/src/locale/mn_MN.json +++ b/packages/components/src/locale/mn_MN.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Таны захиалга энд харагдах болно.", "empty__your_watchlist_is_empty": "Таны үзэх жагсаалт хоосон байна", "empty__your_watchlist_is_empty_desc": "Өөрийн дуртай токенуудыг үзэх жагсаалтад нэмнэ үү", + "error__payment_id_standard": "Payment ID нь 64 тэмдэгтийн шестеричийн тэмдэгт мөр байх ёстой", "form__24h%_uppercase": "24Ц%", "form__24h_high": "24 цагийн өндөр", "form__24h_low": "24 цаг бага", diff --git a/packages/components/src/locale/pt.json b/packages/components/src/locale/pt.json index f8a783aa51d..2a0ab57bf38 100644 --- a/packages/components/src/locale/pt.json +++ b/packages/components/src/locale/pt.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Seus pedidos serão exibidos aqui.", "empty__your_watchlist_is_empty": "Sua lista de observação está vazia", "empty__your_watchlist_is_empty_desc": "Adicione seus tokens favoritos à lista de observação", + "error__payment_id_standard": "O Payment ID deve ser uma string hexadecimal de 64 caracteres", "form__24h%_uppercase": "24H%", "form__24h_high": "24H Alta", "form__24h_low": "Baixa 24H", diff --git a/packages/components/src/locale/pt_BR.json b/packages/components/src/locale/pt_BR.json index 1d09e3a33ed..76e0c094dcd 100644 --- a/packages/components/src/locale/pt_BR.json +++ b/packages/components/src/locale/pt_BR.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Seus pedidos serão exibidos aqui.", "empty__your_watchlist_is_empty": "Sua lista de observação está vazia", "empty__your_watchlist_is_empty_desc": "Adicione seus tokens favoritos à lista de observação.", + "error__payment_id_standard": "O Payment ID deve ser uma string hexadecimal de 64 caracteres", "form__24h%_uppercase": "24H%", "form__24h_high": "24h Alta", "form__24h_low": "24h Baixo", diff --git a/packages/components/src/locale/ru.json b/packages/components/src/locale/ru.json index faaa4e2b5b9..3b9b45892cb 100644 --- a/packages/components/src/locale/ru.json +++ b/packages/components/src/locale/ru.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Здесь будут отображаться ваши заказы.", "empty__your_watchlist_is_empty": "Ваш список наблюдения пуст", "empty__your_watchlist_is_empty_desc": "Добавьте свои любимые токены в список наблюдения", + "error__payment_id_standard": "Payment ID должен быть 64-значной шестнадцатеричной строкой", "form__24h%_uppercase": "24ч%", "form__24h_high": "24 часа высокий", "form__24h_low": "24 часа Низкий", diff --git a/packages/components/src/locale/th_TH.json b/packages/components/src/locale/th_TH.json index de71bd12216..7e8dad10cc0 100644 --- a/packages/components/src/locale/th_TH.json +++ b/packages/components/src/locale/th_TH.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "คำสั่งซื้อของคุณจะแสดงที่นี่", "empty__your_watchlist_is_empty": "รายการเฝ้าดูของคุณว่างเปล่า", "empty__your_watchlist_is_empty_desc": "เพิ่มโทเค็นที่คุณชื่นชอบในรายการเฝ้าดู", + "error__payment_id_standard": "Payment ID ต้องเป็นสตริงเฮ็กซ์ 64 ตัวอักษร", "form__24h%_uppercase": "24H%", "form__24h_high": "สูง 24 ชั่วโมง", "form__24h_low": "ต่ำ 24 ชั่วโมง", diff --git a/packages/components/src/locale/uk_UA.json b/packages/components/src/locale/uk_UA.json index 6127cf8b101..8ae917bc42a 100644 --- a/packages/components/src/locale/uk_UA.json +++ b/packages/components/src/locale/uk_UA.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Тут відображатимуться ваші замовлення.", "empty__your_watchlist_is_empty": "Ваш список спостереження порожній", "empty__your_watchlist_is_empty_desc": "Додайте свої улюблені токени до списку спостереження", + "error__payment_id_standard": "Payment ID повинен бути 64-значним шістнадцятковим рядком", "form__24h%_uppercase": "24 години%", "form__24h_high": "24 години висока", "form__24h_low": "Низький за 24 години", diff --git a/packages/components/src/locale/vi.json b/packages/components/src/locale/vi.json index 613a5342c02..5468bdbf823 100644 --- a/packages/components/src/locale/vi.json +++ b/packages/components/src/locale/vi.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "Đơn đặt hàng của bạn sẽ được hiển thị ở đây.", "empty__your_watchlist_is_empty": "Danh sách theo dõi của bạn trống", "empty__your_watchlist_is_empty_desc": "Thêm mã thông báo yêu thích của bạn vào danh sách theo dõi", + "error__payment_id_standard": "Payment ID phải là một chuỗi hex gồm 64 ký tự", "form__24h%_uppercase": "24H%", "form__24h_high": "24H cao", "form__24h_low": "24H Thấp", diff --git a/packages/components/src/locale/zh-CN.json b/packages/components/src/locale/zh-CN.json index 0f9b6884681..a5b5adf2e67 100644 --- a/packages/components/src/locale/zh-CN.json +++ b/packages/components/src/locale/zh-CN.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "您的订单将显示在这里", "empty__your_watchlist_is_empty": "您的观察列表是空的", "empty__your_watchlist_is_empty_desc": "添加你喜爱的代币到观察列表", + "error__payment_id_standard": "Payment ID 必须是 64 个字符的十六进制字符串", "form__24h%_uppercase": "24小时波动", "form__24h_high": "24 小时最高价", "form__24h_low": "24 小时最低价", diff --git a/packages/components/src/locale/zh_HK.json b/packages/components/src/locale/zh_HK.json index 69036bd6437..34da9892ad9 100644 --- a/packages/components/src/locale/zh_HK.json +++ b/packages/components/src/locale/zh_HK.json @@ -1129,6 +1129,7 @@ "empty__you_have_no_orders_desc": "您的訂單將顯示在這裡", "empty__your_watchlist_is_empty": "您的觀察列表是空的", "empty__your_watchlist_is_empty_desc": "添加你喜愛的代幣到觀察列表", + "error__payment_id_standard": "Payment ID 必須是 64 個字元的十六進位字串", "form__24h%_uppercase": "24H%", "form__24h_high": "24小時最高價", "form__24h_low": "24 小时最低價", diff --git a/packages/kit/src/views/Send/modals/PreSendAddress.tsx b/packages/kit/src/views/Send/modals/PreSendAddress.tsx index 1171692a3db..ee0f692016f 100644 --- a/packages/kit/src/views/Send/modals/PreSendAddress.tsx +++ b/packages/kit/src/views/Send/modals/PreSendAddress.tsx @@ -519,7 +519,7 @@ function PreSendAddress() { !isHexString(addHexPrefix(value)) || stripHexPrefix(value).length !== 64 ) { - return 'Payment ID must be a 64 char hex string'; + return intl.formatMessage({ id: 'error__payment_id_standard' }); } }, }} @@ -527,7 +527,7 @@ function PreSendAddress() { ); - }, [control, displayPaymentId]); + }, [control, displayPaymentId, intl]); const helpTextOfNameServiceResolver = useCallback( (value) => ( From 1bc7fb3a9a2e54d77a44e5d0a4285bb1101c32e4 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 23 Apr 2024 09:28:24 +0800 Subject: [PATCH 12/16] chore: jssdk --- package.json | 10 +-- packages/shared/src/config/appConfig.ts | 2 +- yarn.lock | 104 ++++++++++++------------ 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index d94a6084333..b754af4406d 100644 --- a/package.json +++ b/package.json @@ -70,11 +70,11 @@ "@onekeyfe/cross-inpage-provider-injected": "1.1.57", "@onekeyfe/cross-inpage-provider-types": "1.1.57", "@onekeyfe/extension-bridge-hosted": "1.1.57", - "@onekeyfe/hd-ble-sdk": "0.3.43-alpha.0", - "@onekeyfe/hd-core": "0.3.43-alpha.0", - "@onekeyfe/hd-shared": "0.3.43-alpha.0", - "@onekeyfe/hd-transport": "0.3.43-alpha.0", - "@onekeyfe/hd-web-sdk": "0.3.43-alpha.0", + "@onekeyfe/hd-ble-sdk": "0.3.43", + "@onekeyfe/hd-core": "0.3.43", + "@onekeyfe/hd-shared": "0.3.43", + "@onekeyfe/hd-transport": "0.3.43", + "@onekeyfe/hd-web-sdk": "0.3.43", "@onekeyfe/onekey-cross-webview": "1.1.57", "@starcoin/starcoin": "2.1.5", "@web3-react/core": "8.0.35-beta.0", diff --git a/packages/shared/src/config/appConfig.ts b/packages/shared/src/config/appConfig.ts index 57e182b0157..b5dfebafc5a 100644 --- a/packages/shared/src/config/appConfig.ts +++ b/packages/shared/src/config/appConfig.ts @@ -14,7 +14,7 @@ export const HARDWARE_SDK_IFRAME_SRC_ONEKEYSO = export const HARDWARE_SDK_IFRAME_SRC_ONEKEYCN = process.env.HARDWARE_SDK_CONNECT_SRC_ONEKEYCN || 'https://jssdk.onekeycn.com'; -export const HARDWARE_SDK_VERSION = '0.3.43-alpha.0'; +export const HARDWARE_SDK_VERSION = '0.3.43'; export const HARDWARE_BRIDGE_DOWNLOAD_URL = 'https://onekey.so/download/?client=bridge'; diff --git a/yarn.lock b/yarn.lock index db8409720e6..1ca36e64eb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6270,23 +6270,23 @@ __metadata: languageName: node linkType: hard -"@onekeyfe/hd-ble-sdk@npm:0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-ble-sdk@npm:0.3.43-alpha.0" +"@onekeyfe/hd-ble-sdk@npm:0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-ble-sdk@npm:0.3.43" dependencies: - "@onekeyfe/hd-core": ^0.3.43-alpha.0 - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport-react-native": ^0.3.43-alpha.0 - checksum: 1c1028397588a21be770965ae2cf75a63ab8c370a1720c6b06ad36ed270b17eaa7050f4a7f1d159b66d46349bcc25ea7ef7403d4499f9522ae415956e7237e86 + "@onekeyfe/hd-core": ^0.3.43 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport-react-native": ^0.3.43 + checksum: aebf9a719869d2657c449f22b8b5aa1c035617174e7e17740f616ddf8341c80af864ae5c5511ab029117566f5aca99e6cd2ee016b70c1b11c6abc07f18a5c025 languageName: node linkType: hard -"@onekeyfe/hd-core@npm:0.3.43-alpha.0, @onekeyfe/hd-core@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-core@npm:0.3.43-alpha.0" +"@onekeyfe/hd-core@npm:0.3.43, @onekeyfe/hd-core@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-core@npm:0.3.43" dependencies: - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport": ^0.3.43-alpha.0 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport": ^0.3.43 axios: ^0.27.2 bignumber.js: ^9.0.2 bytebuffer: ^5.0.1 @@ -6296,71 +6296,71 @@ __metadata: peerDependencies: "@noble/hashes": ^1.1.3 ripple-keypairs: ^1.1.4 - checksum: 470c400c973933348a8c8d9b49b3be8ad7fd4f82548752ac66522f65e5dd010fb826215d6ecee9834df1311ccdacbce098fc95c214dc69822b671173d53815f7 + checksum: 06d69c6a55904912a8d361aebe49ab7b088feaf83011c833916b0148246e1a863f73d452c501ce8d3c3e10bd56e01b6c80d970c8a598eff49c1952a42e80a8d1 languageName: node linkType: hard -"@onekeyfe/hd-shared@npm:0.3.43-alpha.0, @onekeyfe/hd-shared@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-shared@npm:0.3.43-alpha.0" - checksum: ef0b37b1e6906d9d76d5dcad68342ac18e94ef0e082e71068e4fe96dfafc5e2d82b15267e22f5c5adbc6a2a56084a91a7b41caf586af077eed24972b95528b71 +"@onekeyfe/hd-shared@npm:0.3.43, @onekeyfe/hd-shared@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-shared@npm:0.3.43" + checksum: bac1e1e9b7316595abd56e8188adc3df6cef5a6c492119b729ea4a1bc6e6747ab6c35dbac680161cf62fede64077fac03b03a0ce636014c67c9252e4dd7a3e4a languageName: node linkType: hard -"@onekeyfe/hd-transport-http@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-transport-http@npm:0.3.43-alpha.0" +"@onekeyfe/hd-transport-http@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-transport-http@npm:0.3.43" dependencies: - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport": ^0.3.43-alpha.0 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport": ^0.3.43 axios: ^0.27.2 - checksum: 5cf13f2b1ef9c87a3d0a243453649dca0d34150a548537b361f753ec6ab25c3dbfa2ebcb7f9624f5bd6bb8095d8116f1a3bfe880049ceec4e8892794df2a21f1 + checksum: e2d35a319e244c4febff640dca4410ba2b02da24bf702f5376bccaf3a3ccb2b06ceedc32eb8b21bf05a719f2b9b29fc59d9259d1d533ac26f3e69f5cd53bd6f3 languageName: node linkType: hard -"@onekeyfe/hd-transport-react-native@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-transport-react-native@npm:0.3.43-alpha.0" +"@onekeyfe/hd-transport-react-native@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-transport-react-native@npm:0.3.43" dependencies: - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport": ^0.3.43-alpha.0 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport": ^0.3.43 "@onekeyfe/react-native-ble-plx": 3.0.0 react-native-ble-manager: ^8.1.0 - checksum: 5031514ecd449e71a2c6d05bc428552e90d951dc953704d2fad8f79a64e3bf96b5b03795ab118f64a95acf4581a3e6beaad03f8b668195ea63cce28f03324e8f + checksum: bc0ed84a26f90acbd9c3b631ba4d252d38a082ab39b351df1b04c2562cfd0a798813dc1cf1adc369d4acaf9a92373137cd220030bf16924b6d33660a568eff2f languageName: node linkType: hard -"@onekeyfe/hd-transport-webusb@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-transport-webusb@npm:0.3.43-alpha.0" +"@onekeyfe/hd-transport-webusb@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-transport-webusb@npm:0.3.43" dependencies: - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport": ^0.3.43-alpha.0 - checksum: 3b5d6de340fd89132f14b39274af20761e71b329941f2cde62a62712c85e98f1fe57eec41b1391a16e60c2cac8ff335879e34e5d3cc25da003079c0ecb636cd8 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport": ^0.3.43 + checksum: 99a6ee7f23e0ea3d7e49af5e95edb10ac7f86e6a43bd8873acce8d78b8cc14d6d6fecf6eb8f0044153efb0032d604f5b1890d21d685add7f69bc3f0526ecbb84 languageName: node linkType: hard -"@onekeyfe/hd-transport@npm:0.3.43-alpha.0, @onekeyfe/hd-transport@npm:^0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-transport@npm:0.3.43-alpha.0" +"@onekeyfe/hd-transport@npm:0.3.43, @onekeyfe/hd-transport@npm:^0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-transport@npm:0.3.43" dependencies: bytebuffer: ^5.0.1 long: ^4.0.0 protobufjs: ^6.11.2 - checksum: 4eca9c2d1101801d8d64a4261671dce2a04605107804bae6c4bd7545cb19dc6cfb21cea215bd0621b384f1b75de22201dc312d0972a860a1602276debf66156d + checksum: 3f374c709b5ae11fe149355b80b76fd01179a92770cd27ebbdf34535df65422cce1fe81c00729731de24e3bdaa4aaf3fe94cff2940e95777d5d053c8910a7196 languageName: node linkType: hard -"@onekeyfe/hd-web-sdk@npm:0.3.43-alpha.0": - version: 0.3.43-alpha.0 - resolution: "@onekeyfe/hd-web-sdk@npm:0.3.43-alpha.0" +"@onekeyfe/hd-web-sdk@npm:0.3.43": + version: 0.3.43 + resolution: "@onekeyfe/hd-web-sdk@npm:0.3.43" dependencies: "@onekeyfe/cross-inpage-provider-core": ^0.0.17 - "@onekeyfe/hd-core": ^0.3.43-alpha.0 - "@onekeyfe/hd-shared": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport-http": ^0.3.43-alpha.0 - "@onekeyfe/hd-transport-webusb": ^0.3.43-alpha.0 - checksum: 6bfb7a3ed22ee417cde641c38a9eeb33b8e912c543183c36ff2f9177c84196d7e1c7a233dc9eb740f6028b81ab48cebe8f0851257c55b705dac8f99efd4f9c0a + "@onekeyfe/hd-core": ^0.3.43 + "@onekeyfe/hd-shared": ^0.3.43 + "@onekeyfe/hd-transport-http": ^0.3.43 + "@onekeyfe/hd-transport-webusb": ^0.3.43 + checksum: ec9ee2afdc06e975ff6dbc5fba1cecf94602a7d9761fb31852835fa2b2631591a2845d6d86b6a4fedee8f75fd87ed8b646235358c16dd63ec2c1c1f6daff557a languageName: node linkType: hard @@ -6647,11 +6647,11 @@ __metadata: "@onekeyfe/cross-inpage-provider-injected": 1.1.57 "@onekeyfe/cross-inpage-provider-types": 1.1.57 "@onekeyfe/extension-bridge-hosted": 1.1.57 - "@onekeyfe/hd-ble-sdk": 0.3.43-alpha.0 - "@onekeyfe/hd-core": 0.3.43-alpha.0 - "@onekeyfe/hd-shared": 0.3.43-alpha.0 - "@onekeyfe/hd-transport": 0.3.43-alpha.0 - "@onekeyfe/hd-web-sdk": 0.3.43-alpha.0 + "@onekeyfe/hd-ble-sdk": 0.3.43 + "@onekeyfe/hd-core": 0.3.43 + "@onekeyfe/hd-shared": 0.3.43 + "@onekeyfe/hd-transport": 0.3.43 + "@onekeyfe/hd-web-sdk": 0.3.43 "@onekeyfe/onekey-cross-webview": 1.1.57 "@open-wc/webpack-import-meta-loader": ^0.4.7 "@pmmmwh/react-refresh-webpack-plugin": ^0.5.10 From 67f54d2215c24d1725758f3dbbdd038bee5a9abb Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 23 Apr 2024 16:48:56 +0800 Subject: [PATCH 13/16] fix: decode on chain history error --- .../engine/src/vaults/impl/dynex/KeyringHardware.ts | 8 ++++++++ packages/engine/src/vaults/impl/dynex/Vault.ts | 6 +++--- packages/engine/src/vaults/impl/dynex/types.ts | 12 ------------ 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index 05dd972a68b..8862b1042d6 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -1,3 +1,4 @@ +import { HardwareErrorCode } from '@onekeyfe/hd-shared'; import BigNumber from 'bignumber.js'; import { isNil } from 'lodash'; @@ -270,6 +271,13 @@ export class KeyringHardware extends KeyringHardwareBase { }; } + if ( + response.payload.code === HardwareErrorCode.RuntimeError && + response.payload.error.indexOf('Invalid number of inputs') !== -1 + ) { + throw new Error('Exceeded UTXO inputs limit.'); + } + throw convertDeviceError(response.payload); } diff --git a/packages/engine/src/vaults/impl/dynex/Vault.ts b/packages/engine/src/vaults/impl/dynex/Vault.ts index fb1009b20fe..efa182fecdc 100644 --- a/packages/engine/src/vaults/impl/dynex/Vault.ts +++ b/packages/engine/src/vaults/impl/dynex/Vault.ts @@ -284,7 +284,7 @@ export default class Vault extends VaultBase { : IDecodedTxDirection.IN; } - const amountValue = parseInt(tx.amount[0], 16); + const amountValue = tx.amount[0]; const decodedTx: IDecodedTx = { txid: tx.hash ?? '', @@ -302,7 +302,7 @@ export default class Vault extends VaultBase { amount: new BigNumber(amountValue) .shiftedBy(-network.decimals) .toFixed(), - amountValue: amountValue.toString(10), + amountValue, extraInfo: null, }, }, @@ -449,7 +449,7 @@ export default class Vault extends VaultBase { }); } - transaction.outputs_with_address?.forEach((output, index) => { + transaction.outputs?.forEach((output, index) => { if (output.address_to === accountAddress) { unspentOutputs[output.globalIndex] = { prevIndex: index, diff --git a/packages/engine/src/vaults/impl/dynex/types.ts b/packages/engine/src/vaults/impl/dynex/types.ts index 9e8a54923d2..018cc2818e6 100644 --- a/packages/engine/src/vaults/impl/dynex/types.ts +++ b/packages/engine/src/vaults/impl/dynex/types.ts @@ -29,18 +29,6 @@ export type IOnChainTransaction = { }[]; mixin: number; outputs: { - globalIndex: number; - output: { - amount: number; - target: { - data: { - key: string; - }; - type: string; - }; - }; - }[]; - outputs_with_address: { address_to: string; globalIndex: number; output: { From 6c8cdec356325ff87a87a5c0357d61ffb53480f5 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Tue, 23 Apr 2024 16:52:37 +0800 Subject: [PATCH 14/16] fix: lint --- packages/engine/src/vaults/impl/dynex/settings.ts | 2 +- packages/engine/src/vaults/types.ts | 2 +- packages/kit/src/components/IdentityAssertion/index.tsx | 6 +++--- .../NetworkAccountSelectorModal/AccountList/index.tsx | 2 +- .../modals/NetworkAccountSelectorModal/Header.tsx | 4 ++-- .../Send/modals/SendEditFee/SendEditFeeStandardForm.tsx | 2 -- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/settings.ts b/packages/engine/src/vaults/impl/dynex/settings.ts index 2f4013a51b3..3620fef1851 100644 --- a/packages/engine/src/vaults/impl/dynex/settings.ts +++ b/packages/engine/src/vaults/impl/dynex/settings.ts @@ -23,7 +23,7 @@ const settings: IVaultSettings = Object.freeze({ withPaymentId: true, - enableOnClassicOnly: true, + enabledOnClassicOnly: true, accountNameInfo: { default: { diff --git a/packages/engine/src/vaults/types.ts b/packages/engine/src/vaults/types.ts index 40fd48cb398..a2a87583610 100644 --- a/packages/engine/src/vaults/types.ts +++ b/packages/engine/src/vaults/types.ts @@ -168,7 +168,7 @@ export type IVaultSettings = { hideInAllNetworksMode?: boolean; mnemonicAsPrivatekey?: boolean; - enableOnClassicOnly?: boolean; + enabledOnClassicOnly?: boolean; }; export type IVaultFactoryOptions = { networkId: string; diff --git a/packages/kit/src/components/IdentityAssertion/index.tsx b/packages/kit/src/components/IdentityAssertion/index.tsx index 52e505e0b92..b877e1942ea 100644 --- a/packages/kit/src/components/IdentityAssertion/index.tsx +++ b/packages/kit/src/components/IdentityAssertion/index.tsx @@ -60,12 +60,12 @@ const IdentityAssertion: FC<{ checkCompatibleNetwork?: boolean }> = ({ const hasNoWallet = !walletId; const isAccountCompatibleNetwork = !!accountId && (checkCompatibleNetwork ? isCompatibleNetwork : true); - const enableOnClassicOnly = network?.settings.enableOnClassicOnly; + const enabledOnClassicOnly = network?.settings.enabledOnClassicOnly; - if (enableOnClassicOnly && !isHwClassic(wallet?.deviceType)) { + if (enabledOnClassicOnly && !isHwClassic(wallet?.deviceType)) { return ( = ({ const { wallet } = useWallet({ walletId }); if ( - network?.settings.enableOnClassicOnly && + network?.settings.enabledOnClassicOnly && !isHwClassic(wallet?.deviceType) ) { return ( diff --git a/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/Header.tsx b/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/Header.tsx index 41d4b89e116..200bced61b0 100644 --- a/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/Header.tsx +++ b/packages/kit/src/components/NetworkAccountSelector/modals/NetworkAccountSelectorModal/Header.tsx @@ -82,7 +82,7 @@ function Header({ } if ( - selectedNetworkSettings?.enableOnClassicOnly && + selectedNetworkSettings?.enabledOnClassicOnly && !isHwClassic(selectedWallet?.deviceType) ) { return true; @@ -90,7 +90,7 @@ function Header({ return false; }, [ hideCreateAccount, - selectedNetworkSettings?.enableOnClassicOnly, + selectedNetworkSettings?.enabledOnClassicOnly, selectedWallet?.deviceType, ]); diff --git a/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx b/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx index 83857fba246..976d39a5bfc 100644 --- a/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx +++ b/packages/kit/src/views/Send/modals/SendEditFee/SendEditFeeStandardForm.tsx @@ -223,8 +223,6 @@ export function SendEditFeeStandardForm({ isEIP1559Fee, ]); - console.log('feeInfoPayload', feeInfoPayload); - return ( {selectedFeeInfo} From 433b2c88afba19cfa15b8af1fe1e6822f66b31cb Mon Sep 17 00:00:00 2001 From: weatherstar Date: Thu, 25 Apr 2024 10:14:06 +0800 Subject: [PATCH 15/16] refactor: serializeTransaction --- .../src/vaults/impl/dynex/KeyringHardware.ts | 96 +---------------- .../engine/src/vaults/impl/dynex/types.ts | 9 ++ .../engine/src/vaults/impl/dynex/utils.ts | 102 ++++++++++++++++++ 3 files changed, 116 insertions(+), 91 deletions(-) diff --git a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts index 8862b1042d6..113fa312fa3 100644 --- a/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts +++ b/packages/engine/src/vaults/impl/dynex/KeyringHardware.ts @@ -16,12 +16,7 @@ import { AccountType } from '../../../types/account'; import { KeyringHardwareBase } from '../../keyring/KeyringHardwareBase'; import { stripHexPrefix } from '../../utils/hexUtils'; -import { - cnFastHash, - decodeAddress, - encodeVarInt, - integerToLittleEndianHex, -} from './utils'; +import { cnFastHash, serializeTransaction } from './utils'; import type { DBUTXOAccount } from '../../../types/account'; import type { @@ -176,95 +171,14 @@ export class KeyringHardware extends KeyringHardwareBase { }); if (response.success) { - const { txKey, computedKeyImages, signatures, outputKeys } = - response.payload; console.log('signTransaction response', response.payload); - let rawTx = ''; - - const version = '01'; - const unlockTime = '00'; - const inputTypeTag = '02'; - const outputTypeTag = '02'; - const txPubkeyTag = '01'; - const fromAddressTag = '04'; - const toAddressTag = '05'; - const amountTag = '06'; - const txSecTag = '07'; - const extraNonceTag = '02'; - const extraNoncePaymentIdTag = '00'; - const decodedFrom = decodeAddress(encodedTx.from); - const decodedTo = decodeAddress(encodedTx.to); - const totalInputAmountBN = params.inputs.reduce( - (acc, input) => acc.plus(input.amount), - new BigNumber(0), - ); - const chargeAmount = totalInputAmountBN - .minus(params.amount) - .minus(params.fee); - - rawTx += version; - rawTx += unlockTime; - rawTx += encodeVarInt(params.inputs.length); - - for (let i = 0; i < params.inputs.length; i += 1) { - const input = params.inputs[i]; - rawTx += inputTypeTag; - rawTx += encodeVarInt(input.amount); - rawTx += encodeVarInt(1); - rawTx += encodeVarInt(input.globalIndex); - rawTx += computedKeyImages[i]; - } - - rawTx += encodeVarInt(outputKeys.length); - - for (let i = 0; i < outputKeys.length; i += 1) { - const outputKey = outputKeys[i]; - rawTx += encodeVarInt( - i === outputKeys.length - 1 && chargeAmount.gt(0) - ? chargeAmount.toNumber() - : Number(params.amount), - ); - rawTx += outputTypeTag; - rawTx += outputKey; - } - - let extra = ''; - - if (params.paymentIdHex) { - extra += extraNonceTag; - extra += '21'; - extra += extraNoncePaymentIdTag; - extra += params.paymentIdHex; - } - - extra += txPubkeyTag; - extra += txKey.ephemeralTxPubKey; - - extra += fromAddressTag; - extra += decodedFrom.spend; - extra += decodedFrom.view; - - extra += toAddressTag; - extra += decodedTo.spend; - extra += decodedTo.view; - - extra += amountTag; - extra += integerToLittleEndianHex({ - number: Number(params.amount), - bytes: 8, + const rawTx = serializeTransaction({ + encodedTx, + signTxParams: params, + payload: response.payload, }); - extra += txSecTag; - extra += txKey.ephemeralTxSecKey; - - rawTx += encodeVarInt(extra.length / 2); - rawTx += extra; - - for (const signature of signatures) { - rawTx += signature; - } - return { txid: stripHexPrefix(cnFastHash(rawTx)), rawTx, diff --git a/packages/engine/src/vaults/impl/dynex/types.ts b/packages/engine/src/vaults/impl/dynex/types.ts index 018cc2818e6..dde854d9404 100644 --- a/packages/engine/src/vaults/impl/dynex/types.ts +++ b/packages/engine/src/vaults/impl/dynex/types.ts @@ -96,3 +96,12 @@ export type IEncodedTxDynex = { paymentId?: string; inputs: IUnspentOutput[]; }; + +export type ISignTxParams = { + path: string; + inputs: IUnspentOutput[]; + toAddress: string; + amount: string; + fee: string; + paymentIdHex?: string; +}; diff --git a/packages/engine/src/vaults/impl/dynex/utils.ts b/packages/engine/src/vaults/impl/dynex/utils.ts index 924a63cb160..f05ff50f586 100644 --- a/packages/engine/src/vaults/impl/dynex/utils.ts +++ b/packages/engine/src/vaults/impl/dynex/utils.ts @@ -3,6 +3,9 @@ import { keccak256 } from '@ethersproject/keccak256'; import BigNumber from 'bignumber.js'; +import type { IEncodedTxDynex, ISignTxParams } from './types'; +import type { DnxSignature } from '@onekeyfe/hd-core'; + const CRYPTONOTE_PUBLIC_ADDRESS_BASE58_PREFIX = 185; const CRYPTONOTE_PUBLIC_INTEGRATED_ADDRESS_BASE58_PREFIX = 29; const CRYPTONOTE_PUBLIC_SUBADDRESS_BASE58_PREFIX = 52; @@ -214,3 +217,102 @@ export function decodeAddress(address: string) { view, }; } + +export function serializeTransaction({ + signTxParams, + encodedTx, + payload, +}: { + encodedTx: IEncodedTxDynex; + signTxParams: ISignTxParams; + payload: DnxSignature; +}) { + let rawTx = ''; + + const { txKey, computedKeyImages, signatures, outputKeys } = payload; + + const version = '01'; + const unlockTime = '00'; + const inputTypeTag = '02'; + const outputTypeTag = '02'; + const txPubkeyTag = '01'; + const fromAddressTag = '04'; + const toAddressTag = '05'; + const amountTag = '06'; + const txSecTag = '07'; + const extraNonceTag = '02'; + const extraNoncePaymentIdTag = '00'; + const decodedFrom = decodeAddress(encodedTx.from); + const decodedTo = decodeAddress(encodedTx.to); + const totalInputAmountBN = signTxParams.inputs.reduce( + (acc, input) => acc.plus(input.amount), + new BigNumber(0), + ); + const chargeAmount = totalInputAmountBN + .minus(signTxParams.amount) + .minus(signTxParams.fee); + + rawTx += version; + rawTx += unlockTime; + rawTx += encodeVarInt(signTxParams.inputs.length); + + for (let i = 0; i < signTxParams.inputs.length; i += 1) { + const input = signTxParams.inputs[i]; + rawTx += inputTypeTag; + rawTx += encodeVarInt(input.amount); + rawTx += encodeVarInt(1); + rawTx += encodeVarInt(input.globalIndex); + rawTx += computedKeyImages[i]; + } + + rawTx += encodeVarInt(outputKeys.length); + + for (let i = 0; i < outputKeys.length; i += 1) { + const outputKey = outputKeys[i]; + rawTx += encodeVarInt( + i === outputKeys.length - 1 && chargeAmount.gt(0) + ? chargeAmount.toNumber() + : Number(signTxParams.amount), + ); + rawTx += outputTypeTag; + rawTx += outputKey; + } + + let extra = ''; + + if (signTxParams.paymentIdHex) { + extra += extraNonceTag; + extra += '21'; + extra += extraNoncePaymentIdTag; + extra += signTxParams.paymentIdHex; + } + + extra += txPubkeyTag; + extra += txKey.ephemeralTxPubKey; + + extra += fromAddressTag; + extra += decodedFrom.spend; + extra += decodedFrom.view; + + extra += toAddressTag; + extra += decodedTo.spend; + extra += decodedTo.view; + + extra += amountTag; + extra += integerToLittleEndianHex({ + number: Number(signTxParams.amount), + bytes: 8, + }); + + extra += txSecTag; + extra += txKey.ephemeralTxSecKey; + + rawTx += encodeVarInt(extra.length / 2); + rawTx += extra; + + for (const signature of signatures) { + rawTx += signature; + } + + return rawTx; +} From a389f2583e85033c86f252ac4ed5222b2dda8ce0 Mon Sep 17 00:00:00 2001 From: weatherstar Date: Thu, 25 Apr 2024 21:49:16 +0800 Subject: [PATCH 16/16] fix: review issues --- packages/engine/src/proxy.ts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/engine/src/proxy.ts b/packages/engine/src/proxy.ts index 7b876ecf351..01e3108fa58 100644 --- a/packages/engine/src/proxy.ts +++ b/packages/engine/src/proxy.ts @@ -32,7 +32,6 @@ import { IMPL_BCH, IMPL_BTC, IMPL_DOGE, - IMPL_DYNEX, IMPL_LTC, IMPL_NEURAI, IMPL_TBTC, @@ -78,15 +77,9 @@ function fromDBNetworkToChainInfo(dbNetwork: DBNetwork): ChainInfo { let code = dbNetwork.id; if ( - [ - IMPL_BTC, - IMPL_DOGE, - IMPL_LTC, - IMPL_BCH, - IMPL_TBTC, - IMPL_NEURAI, - IMPL_DYNEX, - ].includes(dbNetwork.impl) + [IMPL_BTC, IMPL_DOGE, IMPL_LTC, IMPL_BCH, IMPL_TBTC, IMPL_NEURAI].includes( + dbNetwork.impl, + ) ) { code = dbNetwork.impl; }