From 24689a0254d3fdcdead525246d41bcd71a1032da Mon Sep 17 00:00:00 2001 From: huhuanming Date: Thu, 9 May 2024 22:17:44 +0800 Subject: [PATCH 1/4] feat: add getConfirmedUTXOs --- packages/engine/src/vaults/VaultBase.ts | 21 +++++++++++++++++++ packages/engine/src/vaults/impl/nexa/Vault.ts | 19 ++++++++++++++--- .../engine/src/vaults/impl/nexa/sdk/nexa.ts | 2 ++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/packages/engine/src/vaults/VaultBase.ts b/packages/engine/src/vaults/VaultBase.ts index 07af08d0a54..9f875fa6789 100644 --- a/packages/engine/src/vaults/VaultBase.ts +++ b/packages/engine/src/vaults/VaultBase.ts @@ -786,6 +786,27 @@ export abstract class VaultBase extends VaultBaseChainOnly { return nextNonce; } + getConfirmedUTXOs( + utxos: T[], + amount: string, + ): T[] { + const confirmedUTXOs = utxos.sort((a, b) => + new BigNumber(b.value).gt(a.value) ? 1 : -1, + ); + const sum = new BigNumber(0); + let i = 0; + for (i = 0; i < confirmedUTXOs.length; i += 1) { + sum.plus(confirmedUTXOs[i].value); + if (sum.gt(amount)) { + break; + } + } + if (sum.lt(amount)) { + return []; + } + return confirmedUTXOs.slice(0, i + 1); + } + validateSendAmount(amount: string, tokenBalance: string, to: string) { return Promise.resolve(true); } diff --git a/packages/engine/src/vaults/impl/nexa/Vault.ts b/packages/engine/src/vaults/impl/nexa/Vault.ts index 56e80c3fec9..eedd8ff5aef 100644 --- a/packages/engine/src/vaults/impl/nexa/Vault.ts +++ b/packages/engine/src/vaults/impl/nexa/Vault.ts @@ -53,7 +53,7 @@ import type { Token } from '../../../types/token'; import type { KeyringSoftwareBase } from '../../keyring/KeyringSoftwareBase'; import type { IDecodedTxLegacy, IHistoryTx, ISignedTxPro } from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; -import type { IEncodedTxNexa, INexaTransaction } from './types'; +import type { IEncodedTxNexa, IListUTXO, INexaTransaction } from './types'; export default class Vault extends VaultBase { keyringMap = { @@ -243,13 +243,26 @@ export default class Vault extends VaultBase { ): Promise { const client = await this.getSDKClient(); const fromNexaAddress = transferInfo.from; - const utxos = (await client.getNexaUTXOs(fromNexaAddress)).filter( + const rawUTXOS = (await client.getNexaUTXOs(fromNexaAddress)).filter( (value) => !value.has_token, ); + const confirmedUTXOs = this.getConfirmedUTXOs( + rawUTXOS, + transferInfo.amount, + ); + + if (confirmedUTXOs.length > client.MAX_TX_NUM_VIN) { + const maximumAmount = rawUTXOS + .slice(0, client.MAX_TX_NUM_VIN) + .reduce((acc, cur) => acc.plus(cur.value), new BigNumber(0)); + throw new OneKeyInternalError( + `Too many vins, The maximum amount for this transfer is ${maximumAmount.toString()} satoshi.`, + ); + } const network = await this.getNetwork(); return { - inputs: utxos.map((utxo) => ({ + inputs: confirmedUTXOs.map((utxo) => ({ txId: utxo.outpoint_hash, outputIndex: utxo.tx_pos, satoshis: new BigNumber(utxo.value).toFixed(), diff --git a/packages/engine/src/vaults/impl/nexa/sdk/nexa.ts b/packages/engine/src/vaults/impl/nexa/sdk/nexa.ts index 47dfe6add2c..8b78bb5632f 100644 --- a/packages/engine/src/vaults/impl/nexa/sdk/nexa.ts +++ b/packages/engine/src/vaults/impl/nexa/sdk/nexa.ts @@ -15,6 +15,8 @@ import type { IListUTXO, INexaHistoryItem, INexaTransaction } from '../types'; export class Nexa extends SimpleClient { readonly rpc: WebSocketRequest; + readonly MAX_TX_NUM_VIN = 256; + constructor( url: string, readonly defaultFinality: 'optimistic' | 'final' = 'optimistic', From cb164568b40e6c13ae827be707fd02dd059d675b Mon Sep 17 00:00:00 2001 From: huhuanming Date: Thu, 9 May 2024 22:54:13 +0800 Subject: [PATCH 2/4] fix: fix missing gas --- packages/engine/src/vaults/impl/nexa/Vault.ts | 64 +++++++++++++------ 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/packages/engine/src/vaults/impl/nexa/Vault.ts b/packages/engine/src/vaults/impl/nexa/Vault.ts index eedd8ff5aef..0ce3ff4f3ed 100644 --- a/packages/engine/src/vaults/impl/nexa/Vault.ts +++ b/packages/engine/src/vaults/impl/nexa/Vault.ts @@ -51,7 +51,12 @@ import type { } from '../../../types/provider'; import type { Token } from '../../../types/token'; import type { KeyringSoftwareBase } from '../../keyring/KeyringSoftwareBase'; -import type { IDecodedTxLegacy, IHistoryTx, ISignedTxPro } from '../../types'; +import type { + IDecodedTxLegacy, + IHistoryTx, + ISignCredentialOptions, + ISignedTxPro, +} from '../../types'; import type { EVMDecodedItem } from '../evm/decoder/types'; import type { IEncodedTxNexa, IListUTXO, INexaTransaction } from './types'; @@ -243,26 +248,12 @@ export default class Vault extends VaultBase { ): Promise { const client = await this.getSDKClient(); const fromNexaAddress = transferInfo.from; - const rawUTXOS = (await client.getNexaUTXOs(fromNexaAddress)).filter( + const uxtos = (await client.getNexaUTXOs(fromNexaAddress)).filter( (value) => !value.has_token, ); - const confirmedUTXOs = this.getConfirmedUTXOs( - rawUTXOS, - transferInfo.amount, - ); - - if (confirmedUTXOs.length > client.MAX_TX_NUM_VIN) { - const maximumAmount = rawUTXOS - .slice(0, client.MAX_TX_NUM_VIN) - .reduce((acc, cur) => acc.plus(cur.value), new BigNumber(0)); - throw new OneKeyInternalError( - `Too many vins, The maximum amount for this transfer is ${maximumAmount.toString()} satoshi.`, - ); - } - const network = await this.getNetwork(); return { - inputs: confirmedUTXOs.map((utxo) => ({ + inputs: uxtos.map((utxo) => ({ txId: utxo.outpoint_hash, outputIndex: utxo.tx_pos, satoshis: new BigNumber(utxo.value).toFixed(), @@ -297,15 +288,46 @@ export default class Vault extends VaultBase { return Promise.resolve(encodedTx); } - override buildUnsignedTxFromEncodedTx( + override async buildUnsignedTxFromEncodedTx( encodedTx: IEncodedTxNexa, ): Promise { - return Promise.resolve({ + const client = await this.getSDKClient(); + const network = await this.getNetwork(); + const confirmedUTXOs = this.getConfirmedUTXOs( + encodedTx.inputs.map((input) => ({ + ...input, + value: input.satoshis, + })), + new BigNumber(encodedTx.transferInfo?.amount || 0) + .shiftedBy(network.decimals) + .plus(encodedTx?.gas || 0) + .toFixed(), + ); + + if (confirmedUTXOs.length > client.MAX_TX_NUM_VIN) { + const maximumAmount = confirmedUTXOs + .slice(0, client.MAX_TX_NUM_VIN) + .reduce((acc, cur) => acc.plus(cur.value), new BigNumber(0)); + throw new OneKeyInternalError( + `Too many vins, The maximum amount for this transfer is ${maximumAmount + .shiftedBy(network.decimals) + .toFixed()} satoshi.`, + ); + } + return { inputs: [], outputs: [], - payload: { encodedTx }, + payload: { + encodedTx: { + ...encodedTx, + inputs: confirmedUTXOs.map((utxo) => ({ + ...utxo, + satoshis: utxo.value, + })), + }, + }, encodedTx, - }); + }; } override async getTransactionStatuses( From 7e3cd66be1b8aa7af1995d9257ec74e32293a64c Mon Sep 17 00:00:00 2001 From: huhuanming Date: Thu, 9 May 2024 23:50:24 +0800 Subject: [PATCH 3/4] fix: change to min fee --- packages/engine/src/vaults/impl/nexa/Vault.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/engine/src/vaults/impl/nexa/Vault.ts b/packages/engine/src/vaults/impl/nexa/Vault.ts index 0ce3ff4f3ed..872a324c7c8 100644 --- a/packages/engine/src/vaults/impl/nexa/Vault.ts +++ b/packages/engine/src/vaults/impl/nexa/Vault.ts @@ -349,7 +349,7 @@ export default class Vault extends VaultBase { const localEstimateFee = estimateFee(encodedTx); const feeInfo = specifiedFeeRate ? estimateFee(encodedTx, Number(specifiedFeeRate)) - : Math.max(remoteEstimateFee, localEstimateFee); + : Math.min(remoteEstimateFee, localEstimateFee); return { nativeSymbol: network.symbol, nativeDecimals: network.decimals, From 1eebebbeb983f961053f58ceb1a7c0e7935c4b55 Mon Sep 17 00:00:00 2001 From: huhuanming Date: Fri, 10 May 2024 00:29:11 +0800 Subject: [PATCH 4/4] fix: fix bug --- packages/engine/src/vaults/VaultBase.ts | 4 ++-- packages/engine/src/vaults/impl/nexa/Vault.ts | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/engine/src/vaults/VaultBase.ts b/packages/engine/src/vaults/VaultBase.ts index 9f875fa6789..c8c48e205c2 100644 --- a/packages/engine/src/vaults/VaultBase.ts +++ b/packages/engine/src/vaults/VaultBase.ts @@ -793,10 +793,10 @@ export abstract class VaultBase extends VaultBaseChainOnly { const confirmedUTXOs = utxos.sort((a, b) => new BigNumber(b.value).gt(a.value) ? 1 : -1, ); - const sum = new BigNumber(0); + let sum = new BigNumber(0); let i = 0; for (i = 0; i < confirmedUTXOs.length; i += 1) { - sum.plus(confirmedUTXOs[i].value); + sum = sum.plus(confirmedUTXOs[i].value); if (sum.gt(amount)) { break; } diff --git a/packages/engine/src/vaults/impl/nexa/Vault.ts b/packages/engine/src/vaults/impl/nexa/Vault.ts index 872a324c7c8..8e6c6981688 100644 --- a/packages/engine/src/vaults/impl/nexa/Vault.ts +++ b/packages/engine/src/vaults/impl/nexa/Vault.ts @@ -310,21 +310,16 @@ export default class Vault extends VaultBase { .reduce((acc, cur) => acc.plus(cur.value), new BigNumber(0)); throw new OneKeyInternalError( `Too many vins, The maximum amount for this transfer is ${maximumAmount - .shiftedBy(network.decimals) - .toFixed()} satoshi.`, + .shiftedBy(-network.decimals) + .toFixed()} ${network.symbol}.`, ); } + encodedTx.inputs = confirmedUTXOs; return { inputs: [], outputs: [], payload: { - encodedTx: { - ...encodedTx, - inputs: confirmedUTXOs.map((utxo) => ({ - ...utxo, - satoshis: utxo.value, - })), - }, + encodedTx, }, encodedTx, }; @@ -349,7 +344,7 @@ export default class Vault extends VaultBase { const localEstimateFee = estimateFee(encodedTx); const feeInfo = specifiedFeeRate ? estimateFee(encodedTx, Number(specifiedFeeRate)) - : Math.min(remoteEstimateFee, localEstimateFee); + : Math.max(remoteEstimateFee, localEstimateFee); return { nativeSymbol: network.symbol, nativeDecimals: network.decimals,