diff --git a/packages/engine/src/managers/derivation.ts b/packages/engine/src/managers/derivation.ts index 2587bd5b46b..1cd2fbc5182 100644 --- a/packages/engine/src/managers/derivation.ts +++ b/packages/engine/src/managers/derivation.ts @@ -17,6 +17,7 @@ import { COINTYPE_LTC, COINTYPE_NEAR, COINTYPE_NERVOS, + COINTYPE_NEURAI, COINTYPE_NEXA, COINTYPE_NOSTR, COINTYPE_SOL, @@ -41,6 +42,7 @@ import { IMPL_LTC, IMPL_NEAR, IMPL_NERVOS, + IMPL_NEURAI, IMPL_NOSTR, IMPL_SOL, IMPL_STC, @@ -87,6 +89,7 @@ const purposeMap: Record> = { [IMPL_KASPA]: [44], [IMPL_NOSTR]: [44], [IMPL_NERVOS]: [44], + [IMPL_NEURAI]: [44], }; // derive path template by coin types. @@ -121,6 +124,7 @@ const derivationPathTemplates: Record = { [COINTYPE_LIGHTNING_TESTNET]: `m/44'/${COINTYPE_LIGHTNING_TESTNET}'/${INCREMENT_LEVEL_TAG}`, [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}'`, }; function getDerivationPaths( diff --git a/packages/engine/src/managers/impl.ts b/packages/engine/src/managers/impl.ts index c347de25f5d..3a7c52898b1 100644 --- a/packages/engine/src/managers/impl.ts +++ b/packages/engine/src/managers/impl.ts @@ -19,6 +19,7 @@ import { COINTYPE_LTC, COINTYPE_NEAR, COINTYPE_NERVOS, + COINTYPE_NEURAI, COINTYPE_NEXA, COINTYPE_NOSTR, COINTYPE_SOL, @@ -45,6 +46,7 @@ import { IMPL_LTC, IMPL_NEAR, IMPL_NERVOS, + IMPL_NEURAI, IMPL_NEXA, IMPL_NOSTR, IMPL_SOL, @@ -96,6 +98,7 @@ const implToCoinTypes: Partial> = { [IMPL_LIGHTNING_TESTNET]: COINTYPE_LIGHTNING_TESTNET, [IMPL_NOSTR]: COINTYPE_NOSTR, [IMPL_NERVOS]: COINTYPE_NERVOS, + [IMPL_NEURAI]: COINTYPE_NEURAI, }; const coinTypeToImpl: Record = Object.fromEntries( @@ -136,6 +139,7 @@ const implToAccountType: Record = { [IMPL_LIGHTNING_TESTNET]: AccountType.VARIANT, [IMPL_NOSTR]: AccountType.VARIANT, [IMPL_NERVOS]: AccountType.SIMPLE, + [IMPL_NEURAI]: AccountType.UTXO, }; function isCoinTypeCompatibleWithImpl(coinType: string, impl: string): boolean { @@ -171,6 +175,7 @@ const defaultCurveMap: Record = { [IMPL_LIGHTNING_TESTNET]: Curve.SECP256K1, [IMPL_NOSTR]: Curve.SECP256K1, [IMPL_NERVOS]: Curve.SECP256K1, + [IMPL_NEURAI]: Curve.SECP256K1, }; function getCurveByImpl(impl: string): string { @@ -284,7 +289,14 @@ function migrateNextAccountIds(nextAccountIds: Record) { } function isBtcLikeImpl(impl: string): boolean { - return [IMPL_BTC, IMPL_TBTC, IMPL_LTC, IMPL_BCH, IMPL_DOGE].includes(impl); + return [ + IMPL_BTC, + IMPL_TBTC, + IMPL_LTC, + IMPL_BCH, + IMPL_DOGE, + IMPL_NEURAI, + ].includes(impl); } export { diff --git a/packages/engine/src/proxy.ts b/packages/engine/src/proxy.ts index b6eb1a47a77..01e3108fa58 100644 --- a/packages/engine/src/proxy.ts +++ b/packages/engine/src/proxy.ts @@ -33,6 +33,7 @@ import { IMPL_BTC, IMPL_DOGE, IMPL_LTC, + IMPL_NEURAI, IMPL_TBTC, SEPERATOR, } from '@onekeyhq/shared/src/engine/engineConsts'; @@ -76,7 +77,7 @@ function fromDBNetworkToChainInfo(dbNetwork: DBNetwork): ChainInfo { let code = dbNetwork.id; if ( - [IMPL_BTC, IMPL_DOGE, IMPL_LTC, IMPL_BCH, IMPL_TBTC].includes( + [IMPL_BTC, IMPL_DOGE, IMPL_LTC, IMPL_BCH, IMPL_TBTC, IMPL_NEURAI].includes( dbNetwork.impl, ) ) { diff --git a/packages/engine/src/vaults/factory.createVaultSettings.ts b/packages/engine/src/vaults/factory.createVaultSettings.ts index b3fb51d67d6..3cbd1173db0 100644 --- a/packages/engine/src/vaults/factory.createVaultSettings.ts +++ b/packages/engine/src/vaults/factory.createVaultSettings.ts @@ -18,6 +18,7 @@ import { IMPL_LTC, IMPL_NEAR, IMPL_NERVOS, + IMPL_NEURAI, IMPL_NEXA, IMPL_NOSTR, IMPL_SOL, @@ -129,6 +130,9 @@ export function createVaultSettings(options: { if (impl === IMPL_NERVOS) { return require('./impl/nervos/settings').default as IVaultSettings; } + if (impl === IMPL_NEURAI) { + return require('./impl/neurai/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 f80f4e3121e..e5353171153 100644 --- a/packages/engine/src/vaults/factory.ts +++ b/packages/engine/src/vaults/factory.ts @@ -18,6 +18,7 @@ import { IMPL_LTC, IMPL_NEAR, IMPL_NERVOS, + IMPL_NEURAI, IMPL_NEXA, IMPL_NOSTR, IMPL_SOL, @@ -55,6 +56,7 @@ import VaultHelperLightning from './impl/lightning-network/VaultHelper'; import VaultHelperLtc from './impl/ltc/VaultHelper'; import VaultHelperNear from './impl/near/VaultHelper'; import VaultHelperNervos from './impl/nervos/VaultHelper'; +import VaultHelperNeurai from './impl/neurai/VaultHelper'; import VaultHelperNexa from './impl/nexa/VaultHelper'; import VaultHelperNostr from './impl/nostr/VaultHelper'; import VauleHelperSol from './impl/sol/VaultHelper'; @@ -152,6 +154,9 @@ export async function createVaultHelperInstance( if (impl === IMPL_NERVOS) { return new VaultHelperNervos(options); } + if (impl === IMPL_NEURAI) { + return new VaultHelperNeurai(options); + } throw new OneKeyInternalError( `VaultHelper Class not found for: networkId=${options.networkId}, accountId=${options.accountId}`, ); @@ -301,6 +306,10 @@ export async function createVaultInstance(options: IVaultOptions) { const VaultNervos = (await import('./impl/nervos/Vault')).default; vault = new VaultNervos(options); } + if (network.impl === IMPL_NEURAI) { + const VaultNeurai = (await import('./impl/neurai/Vault')).default; + vault = new VaultNeurai(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/nervos/Vault.ts b/packages/engine/src/vaults/impl/nervos/Vault.ts index 92c6ce93380..e2c412503c0 100644 --- a/packages/engine/src/vaults/impl/nervos/Vault.ts +++ b/packages/engine/src/vaults/impl/nervos/Vault.ts @@ -45,8 +45,11 @@ import { KeyringWatching } from './KeyringWatching'; import settings from './settings'; import { isValidateAddress, scriptToAddress } from './utils/address'; import { + DEFAULT_CONFIRM_BLOCK, fetchConfirmCellsByAddress, getBalancesByAddress, + getConfirmBalancesByAddress, + getFrozenBalancesByAddress, } from './utils/balance'; import { getConfig } from './utils/config'; import { @@ -162,6 +165,27 @@ export default class Vault extends VaultBase { return { responseTime: Math.floor(performance.now() - start), latestBlock }; } + override async getFrozenBalance({ + password, + }: { + password?: string; + useRecycleBalance?: boolean; + ignoreInscriptions?: boolean; + useCustomAddressesBalance?: boolean; + } = {}): Promise> { + const indexer = await this.getIndexer(); + const client = await this.getClient(); + const dbAccount = await this.getDbAccount(); + const balance = await getFrozenBalancesByAddress({ + indexer, + address: dbAccount.address, + client, + }); + + const network = await this.engine.getNetwork(this.networkId); + return balance.shiftedBy(-network.decimals).toNumber(); + } + override async getBalances( requests: { address: string; tokenAddress?: string | undefined }[], ): Promise<(BigNumber | undefined)[]> { @@ -174,7 +198,10 @@ export default class Vault extends VaultBase { Object.entries(requestAddress).map(async ([address, tokens]) => { try { try { - balances.set(address, await getBalancesByAddress(indexer, address)); + balances.set( + address, + await getBalancesByAddress({ indexer, address }), + ); } catch (e) { // ignore } @@ -207,11 +234,11 @@ export default class Vault extends VaultBase { const { address: from } = await this.getDbAccount(); const network = await this.getNetwork(); - const confirmCellsByAddress = await fetchConfirmCellsByAddress( + const confirmCellsByAddress = await fetchConfirmCellsByAddress({ indexer, - from, + address: from, client, - ); + }); const transaction = prepareAndBuildTx({ confirmCells: confirmCellsByAddress, from, @@ -468,13 +495,27 @@ export default class Vault extends VaultBase { txids: string[], ): Promise<(TransactionStatus | undefined)[]> { const client = await this.getClient(); + const currentBlockNumber = new BigNumber( + await client.getTipBlockNumber(), + 16, + ); const txStatuses = new Map(); for (const txid of txids) { const tx = await client.getTransaction(txid); + let status = TransactionStatus.PENDING; - if (tx.txStatus.status === 'committed') { - status = TransactionStatus.CONFIRM_AND_SUCCESS; + if (tx.txStatus.blockHash) { + const blockHeader = await client.getHeader( + tx.txStatus.blockHash, + '0x1', + ); + const isConfirmed = currentBlockNumber + .minus(new BigNumber(blockHeader.number, 16)) + .isGreaterThanOrEqualTo(DEFAULT_CONFIRM_BLOCK); + if (tx.txStatus.status === 'committed' && isConfirmed) { + status = TransactionStatus.CONFIRM_AND_SUCCESS; + } } txStatuses.set(txid, status); } @@ -506,6 +547,10 @@ export default class Vault extends VaultBase { }); const config = getConfig(await this.getNetworkChainId()); + const currentBlockNumber = new BigNumber( + await client.getTipBlockNumber(), + 16, + ); const txs = history.map(async (tx) => { try { @@ -521,7 +566,7 @@ export default class Vault extends VaultBase { txStatus: { status }, transaction: { hash: txHash }, } = tx.txWithStatus; - const { timestamp } = tx.txBlockHeader; + const { timestamp, number: blockNumber } = tx.txBlockHeader; const { inputs, outputs } = tx.txSkeleton; const txid = txHash; @@ -630,6 +675,10 @@ export default class Vault extends VaultBase { .shiftedBy(-network.decimals) .toFixed(); + const isConfirmed = currentBlockNumber + .minus(new BigNumber(blockNumber, 16)) + .isGreaterThanOrEqualTo(DEFAULT_CONFIRM_BLOCK); + const decodedTx: IDecodedTx = { txid: txid ?? '', owner: dbAccount.address, @@ -637,7 +686,7 @@ export default class Vault extends VaultBase { nonce: 0, actions, status: - status === 'committed' + status === 'committed' && isConfirmed ? IDecodedTxStatus.Confirmed : IDecodedTxStatus.Pending, networkId: this.networkId, diff --git a/packages/engine/src/vaults/impl/nervos/utils/balance.ts b/packages/engine/src/vaults/impl/nervos/utils/balance.ts index 70e26c1bd3d..effe7cad3b3 100644 --- a/packages/engine/src/vaults/impl/nervos/utils/balance.ts +++ b/packages/engine/src/vaults/impl/nervos/utils/balance.ts @@ -1,56 +1,119 @@ import { parseAddress } from '@ckb-lumos/helpers'; import BigNumber from 'bignumber.js'; -import type { Cell } from '@ckb-lumos/base'; +import type { Cell, Script } from '@ckb-lumos/base'; import type { Indexer } from '@ckb-lumos/ckb-indexer'; import type { RPC } from '@ckb-lumos/rpc'; -export const DEFAULT_CONFIRM_BLOCK = 20; +export const DEFAULT_CONFIRM_BLOCK = 24; -export async function fetchCellsByAddress({ +async function collectFilteredCellsByAddress({ indexer, address, - client, - confirmBlock, + onAllowCollect = () => true, }: { indexer: Indexer; address: string; - client?: RPC; - confirmBlock?: number; + type?: Script | string; + onAllowCollect?: (cell: Cell) => Promise | boolean; }) { const script = parseAddress(address); - const blockNumber = await client?.getTipBlockNumber(); const collector = indexer.collector({ lock: script, type: 'empty' }); const collected: Cell[] = []; for await (const cell of collector.collect()) { - if (!confirmBlock || !client) { - // no confirm block, just collect all cells - collected.push(cell); - } else if ( - // has confirm block, only collect cells that has enough confirmations - blockNumber && - new BigNumber(blockNumber, 16) - .minus(new BigNumber(cell.blockNumber ?? '0x0', 16)) - .isGreaterThan(confirmBlock) - ) { + if (await onAllowCollect?.(cell)) { collected.push(cell); } } - return collected; } -export async function fetchConfirmCellsByAddress( - indexer: Indexer, - address: string, - client: RPC, -) { - return fetchCellsByAddress({ +export async function fetchCellsByAddress({ + indexer, + address, + type, +}: { + indexer: Indexer; + address: string; + type?: Script | string; +}) { + return collectFilteredCellsByAddress({ + indexer, + address, + type, + }); +} + +export async function fetchConfirmCellsByAddress({ + indexer, + address, + type, + client, +}: { + indexer: Indexer; + address: string; + client: RPC; + type?: Script | string; +}) { + const blockNumber = await client.getTipBlockNumber(); + + return collectFilteredCellsByAddress({ + indexer, + address, + type, + onAllowCollect: (cell) => + new BigNumber(blockNumber, 16) + .minus(new BigNumber(cell.blockNumber ?? '0x0', 16)) + .isGreaterThan(DEFAULT_CONFIRM_BLOCK), + }); +} + +export async function fetchFrozenCellsByAddress({ + indexer, + address, + client, + type, +}: { + indexer: Indexer; + address: string; + client: RPC; + type?: Script | string; +}) { + const blockNumber = await client.getTipBlockNumber(); + + return collectFilteredCellsByAddress({ + indexer, + address, + type, + onAllowCollect: (cell) => + new BigNumber(blockNumber, 16) + .minus(new BigNumber(cell.blockNumber ?? '0x0', 16)) + .isLessThan(DEFAULT_CONFIRM_BLOCK), + }); +} + +export async function getFrozenBalancesByAddress({ + indexer, + address, + client, + type, +}: { + indexer: Indexer; + address: string; + client: RPC; + type?: Script | string; +}) { + const cells = await fetchFrozenCellsByAddress({ indexer, address, client, - confirmBlock: DEFAULT_CONFIRM_BLOCK, + type, }); + + return cells.reduce( + (acc, cell) => acc.plus(new BigNumber(cell.cellOutput.capacity, 16)), + new BigNumber(0), + ); } export function selectCellsByAddress( @@ -71,7 +134,13 @@ export function selectCellsByAddress( return { collected, collectedSum }; } -export async function getBalancesByAddress(indexer: Indexer, address: string) { +export async function getBalancesByAddress({ + indexer, + address, +}: { + indexer: Indexer; + address: string; +}) { const cells = await fetchCellsByAddress({ indexer, address }); return cells.reduce( @@ -79,3 +148,20 @@ export async function getBalancesByAddress(indexer: Indexer, address: string) { new BigNumber(0), ); } + +export async function getConfirmBalancesByAddress({ + indexer, + address, + client, +}: { + indexer: Indexer; + address: string; + client: RPC; +}) { + const cells = await fetchConfirmCellsByAddress({ indexer, address, client }); + + return cells.reduce( + (acc, cell) => acc.plus(new BigNumber(cell.cellOutput.capacity, 16)), + new BigNumber(0), + ); +} diff --git a/packages/engine/src/vaults/impl/nervos/utils/transaction.ts b/packages/engine/src/vaults/impl/nervos/utils/transaction.ts index bcecd5ad974..f9c05db6078 100644 --- a/packages/engine/src/vaults/impl/nervos/utils/transaction.ts +++ b/packages/engine/src/vaults/impl/nervos/utils/transaction.ts @@ -1,4 +1,4 @@ -import { blockchain } from '@ckb-lumos/base'; +import { Script, blockchain } from '@ckb-lumos/base'; import { BI } from '@ckb-lumos/bi'; import { bytes } from '@ckb-lumos/codec'; import { common } from '@ckb-lumos/common-scripts'; diff --git a/packages/engine/src/vaults/impl/neurai/KeyringHardware.ts b/packages/engine/src/vaults/impl/neurai/KeyringHardware.ts new file mode 100644 index 00000000000..5321194ec4f --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/KeyringHardware.ts @@ -0,0 +1,3 @@ +import { KeyringHardware as KeyringHardwareBtcFork } from '@onekeyhq/engine/src/vaults/utils/btcForkChain/KeyringHardware'; + +export class KeyringHardware extends KeyringHardwareBtcFork {} diff --git a/packages/engine/src/vaults/impl/neurai/KeyringHd.ts b/packages/engine/src/vaults/impl/neurai/KeyringHd.ts new file mode 100644 index 00000000000..a463791de01 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/KeyringHd.ts @@ -0,0 +1,3 @@ +import { KeyringHd as KeyringHdBtcFork } from '@onekeyhq/engine/src/vaults/utils/btcForkChain/KeyringHd'; + +export class KeyringHd extends KeyringHdBtcFork {} diff --git a/packages/engine/src/vaults/impl/neurai/KeyringImported.ts b/packages/engine/src/vaults/impl/neurai/KeyringImported.ts new file mode 100644 index 00000000000..80d6866f380 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/KeyringImported.ts @@ -0,0 +1,3 @@ +import { KeyringImported as KeyringImportedBtcFork } from '@onekeyhq/engine/src/vaults/utils/btcForkChain/KeyringImported'; + +export class KeyringImported extends KeyringImportedBtcFork {} diff --git a/packages/engine/src/vaults/impl/neurai/KeyringWatching.ts b/packages/engine/src/vaults/impl/neurai/KeyringWatching.ts new file mode 100644 index 00000000000..1bfceb0a6f0 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/KeyringWatching.ts @@ -0,0 +1,3 @@ +import { KeyringWatching as KeyringWatchingBtcFork } from '@onekeyhq/engine/src/vaults/utils/btcForkChain/KeyringWatching'; + +export class KeyringWatching extends KeyringWatchingBtcFork {} diff --git a/packages/engine/src/vaults/impl/neurai/Vault.ts b/packages/engine/src/vaults/impl/neurai/Vault.ts new file mode 100644 index 00000000000..707f5e9ede4 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/Vault.ts @@ -0,0 +1,51 @@ +import VaultBtcFork from '@onekeyhq/engine/src/vaults/utils/btcForkChain/VaultBtcFork'; +import { COINTYPE_NEURAI } from '@onekeyhq/shared/src/engine/engineConsts'; + +import { KeyringHardware } from './KeyringHardware'; +import { KeyringHd } from './KeyringHd'; +import { KeyringImported } from './KeyringImported'; +import { KeyringWatching } from './KeyringWatching'; +import Provider from './provider'; +import settings from './settings'; + +export default class Vault extends VaultBtcFork { + override providerClass = Provider; + + override keyringMap = { + hd: KeyringHd, + hw: KeyringHardware, + imported: KeyringImported, + watching: KeyringWatching, + external: KeyringWatching, + }; + + override settings = settings; + + override getDefaultPurpose() { + return 44; + } + + override getCoinName() { + return 'NEURAI'; + } + + override getCoinType() { + return COINTYPE_NEURAI; + } + + override getXprvReg() { + return /^xgpv/; + } + + override getXpubReg() { + return /^xgub/; + } + + override getDefaultBlockNums(): number[] { + return [25, 5, 2]; + } + + override getDefaultBlockTime(): number { + return 60; + } +} diff --git a/packages/engine/src/vaults/impl/neurai/VaultHelper.ts b/packages/engine/src/vaults/impl/neurai/VaultHelper.ts new file mode 100644 index 00000000000..079a71bfc6c --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/VaultHelper.ts @@ -0,0 +1,3 @@ +import VaultHelperBtcFork from '@onekeyhq/engine/src/vaults/utils/btcForkChain/VaultHelperBtcFork'; + +export default class VaultHelper extends VaultHelperBtcFork {} diff --git a/packages/engine/src/vaults/impl/neurai/provider.ts b/packages/engine/src/vaults/impl/neurai/provider.ts new file mode 100644 index 00000000000..bb9e6c08727 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/provider.ts @@ -0,0 +1,14 @@ +import * as BitcoinJS from 'bitcoinjs-lib'; + +import { Provider as BaseProvider } from '../../utils/btcForkChain/provider'; + +import type { Psbt } from 'bitcoinjs-lib'; + +export default class Provider extends BaseProvider { + override getPsbt(): Psbt { + return new BitcoinJS.Psbt({ + network: this.network, + maximumFeeRate: 10000, + }); + } +} diff --git a/packages/engine/src/vaults/impl/neurai/settings.ts b/packages/engine/src/vaults/impl/neurai/settings.ts new file mode 100644 index 00000000000..1b942c7c104 --- /dev/null +++ b/packages/engine/src/vaults/impl/neurai/settings.ts @@ -0,0 +1,42 @@ +import { + COINTYPE_NEURAI, + INDEX_PLACEHOLDER, +} from '@onekeyhq/shared/src/engine/engineConsts'; + +import { BulkTypeEnum } from '../../../types/batchTransfer'; + +import type { IVaultSettings } from '../../types'; + +const settings: IVaultSettings = Object.freeze({ + feeInfoEditable: true, + privateKeyExportEnabled: true, + publicKeyExportEnabled: true, + tokenEnabled: false, + txCanBeReplaced: false, + + importedAccountEnabled: true, + hardwareAccountEnabled: true, + externalAccountEnabled: false, + watchingAccountEnabled: true, + + minTransferAmount: '0.00000546', + + isUTXOModel: true, + supportBatchTransfer: [BulkTypeEnum.OneToMany], + nativeSupportBatchTransfer: true, + + accountNameInfo: { + default: { + prefix: 'Neurai', + category: `44'/${COINTYPE_NEURAI}'`, + template: `m/44'/${COINTYPE_NEURAI}'/${INDEX_PLACEHOLDER}'/0/0`, + coinType: COINTYPE_NEURAI, + label: 'Legacy', + subDesc: 'BIP44, P2PKH, Base58.', + }, + }, + + isBtcForkChain: true, +}); + +export default settings; diff --git a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts index 1e976ea6ea7..942e36f6d2b 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/VaultBtcFork.ts @@ -27,7 +27,9 @@ import type { SendConfirmPayloadInfo } from '@onekeyhq/kit/src/views/Send/types' import { fetchData } from '@onekeyhq/shared/src/background/backgroundUtils'; import { COINTYPE_BTC, + COINTYPE_NEURAI, IMPL_BTC, + IMPL_NEURAI, } from '@onekeyhq/shared/src/engine/engineConsts'; import debugLogger from '@onekeyhq/shared/src/logger/debugLogger'; import { checkIsUnListOrderPsbt } from '@onekeyhq/shared/src/providerApis/ProviderApiBtc/ProviderApiBtc.utils'; diff --git a/packages/engine/src/vaults/utils/btcForkChain/provider/networks.ts b/packages/engine/src/vaults/utils/btcForkChain/provider/networks.ts index cce01eea92a..23b04667681 100644 --- a/packages/engine/src/vaults/utils/btcForkChain/provider/networks.ts +++ b/packages/engine/src/vaults/utils/btcForkChain/provider/networks.ts @@ -170,12 +170,25 @@ const dash = { wif: 0xcc, }; +const neurai = { + messagePrefix: '\x19Neurai Signed Message:\n', + bech32: '', + bip32: { + private: 0x0488ade4, + public: 0x0488b21e, + }, + pubKeyHash: 0x35, + scriptHash: 0x75, + wif: 0x80, +}; + const extendedNetworks: Record = { btc, tbtc, ltc, bch, doge, + neurai, // TODO not support impl yet // rbtc, // btg, diff --git a/packages/shared/src/config/presetNetworks.ts b/packages/shared/src/config/presetNetworks.ts index da56984bffe..356bccd447a 100644 --- a/packages/shared/src/config/presetNetworks.ts +++ b/packages/shared/src/config/presetNetworks.ts @@ -4434,6 +4434,48 @@ const serverPresetNetworks = [ 'createdAt': '2024-03-05T00:00:00.002Z', 'updatedAt': '2024-03-05T00:00:00.002Z', }, + { + 'balance2FeeDecimals': 0, + 'chainId': '0', + 'code': 'neurai', + 'decimals': 8, + 'id': 'neurai--0', + 'impl': 'neurai', + 'isTestnet': false, + 'logoURI': 'https://onekey-asset.com/assets/neurai/neurai.png', + 'name': 'Neurai', + 'rpcURLs': [ + { + 'url': 'https://blockbook-new-01.neurai.org/', + }, + ], + 'shortcode': 'xna', + 'shortname': 'XNA', + 'symbol': 'XNA', + 'feeMeta': { + 'code': 'xna', + 'decimals': 8, + 'symbol': 'XNA', + }, + 'defaultEnabled': true, + 'priceConfigs': [ + { + 'channel': 'coingecko', + 'native': 'neurai', + }, + ], + 'explorers': [ + { + 'address': 'https://neuraiexplorer.com/address/{address}', + 'block': 'https://neuraiexplorer.com/block/{block}', + 'name': 'https://neuraiexplorer.com', + 'transaction': 'https://neuraiexplorer.com/tx/{transaction}', + }, + ], + 'status': 'LISTED', + 'createdAt': '2024-03-10T00:00:00.001Z', + 'updatedAt': '2024-03-10T00: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 5ec6ef250a7..6465a9ba27c 100644 --- a/packages/shared/src/engine/engineConsts.ts +++ b/packages/shared/src/engine/engineConsts.ts @@ -86,6 +86,9 @@ const COINTYPE_NOSTR = '1237'; const IMPL_NERVOS = 'nervos'; const COINTYPE_NERVOS = '309'; +const IMPL_NEURAI = 'neurai'; +const COINTYPE_NEURAI = '1900'; + const IMPL_ALLNETWORKS = 'all'; const COINTYPE_ALLNETWORKS = '0000'; @@ -94,7 +97,8 @@ export type IBtcForkImpls = | typeof IMPL_TBTC | typeof IMPL_LTC | typeof IMPL_BCH - | typeof IMPL_DOGE; + | typeof IMPL_DOGE + | typeof IMPL_NEURAI; const SUPPORTED_IMPLS = new Set([ IMPL_EVM, @@ -122,6 +126,7 @@ const SUPPORTED_IMPLS = new Set([ IMPL_LIGHTNING, IMPL_LIGHTNING_TESTNET, IMPL_NERVOS, + IMPL_NEURAI, IMPL_ALLNETWORKS, ]); @@ -150,7 +155,8 @@ const PRODUCTION_IMPLS = new Set([ IMPL_LIGHTNING, IMPL_LIGHTNING_TESTNET, IMPL_NEXA, - COINTYPE_NERVOS, + IMPL_NERVOS, + IMPL_NEURAI, IMPL_ALLNETWORKS, ]); @@ -219,6 +225,7 @@ export { COINTYPE_XRP, COINTYPE_NOSTR, COINTYPE_NERVOS, + COINTYPE_NEURAI, IMPL_ADA, IMPL_ALGO, IMPL_ALLNETWORKS, @@ -246,6 +253,7 @@ export { IMPL_XRP, IMPL_NOSTR, IMPL_NERVOS, + IMPL_NEURAI, INDEX_PLACEHOLDER, SEPERATOR, getSupportedImpls,