Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix dust error in Nexa(OK-27719) #4562

Merged
merged 11 commits into from
May 10, 2024
21 changes: 0 additions & 21 deletions packages/engine/src/vaults/VaultBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -786,27 +786,6 @@ export abstract class VaultBase extends VaultBaseChainOnly {
return nextNonce;
}

getConfirmedUTXOs<T extends { value: string | number }>(
utxos: T[],
amount: string,
): T[] {
const confirmedUTXOs = utxos.sort((a, b) =>
new BigNumber(b.value).gt(a.value) ? 1 : -1,
);
let sum = new BigNumber(0);
let i = 0;
for (i = 0; i < confirmedUTXOs.length; i += 1) {
sum = 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);
}
Expand Down
49 changes: 46 additions & 3 deletions packages/engine/src/vaults/impl/nexa/Vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,29 @@ export default class Vault extends VaultBase {
return Promise.resolve({} as IDecodedTxLegacy);
}

getConfirmedUTXOs<T extends { value: string | number }>(
utxos: T[],
amount: string,
minTransferAmount = '0',
): T[] {
const transactionAmount = new BigNumber(amount).plus(minTransferAmount);
originalix marked this conversation as resolved.
Show resolved Hide resolved
const confirmedUTXOs = utxos.sort((a, b) =>
new BigNumber(b.value).gt(a.value) ? 1 : -1,
);
let sum = new BigNumber(0);
let i = 0;
for (i = 0; i < confirmedUTXOs.length; i += 1) {
sum = sum.plus(confirmedUTXOs[i].value);
if (sum.gt(transactionAmount)) {
break;
}
}
if (sum.lt(transactionAmount)) {
return [];
}
return confirmedUTXOs.slice(0, i + 1);
}

override async buildEncodedTxFromTransfer(
transferInfo: ITransferInfo,
): Promise<IEncodedTxNexa> {
Expand Down Expand Up @@ -288,6 +311,15 @@ export default class Vault extends VaultBase {
return Promise.resolve(encodedTx);
}

async getMinTransferAmount() {
const network = await this.getNetwork();
return network.settings.minTransferAmount
? new BigNumber(network.settings.minTransferAmount)
.shiftedBy(network.decimals)
.toFixed()
: undefined;
}

override async buildUnsignedTxFromEncodedTx(
encodedTx: IEncodedTxNexa,
): Promise<IUnsignedTxPro> {
Expand All @@ -302,6 +334,7 @@ export default class Vault extends VaultBase {
.shiftedBy(network.decimals)
.plus(encodedTx?.gas || 0)
.toFixed(),
await this.getMinTransferAmount(),
);

if (confirmedUTXOs.length > client.MAX_TX_NUM_VIN) {
Expand Down Expand Up @@ -339,11 +372,21 @@ export default class Vault extends VaultBase {
): Promise<IFeeInfo> {
const network = await this.getNetwork();
const client = await this.getSDKClient();
const estimateSizedSize = estimateSize(encodedTx);
const vinLength = this.getConfirmedUTXOs(
encodedTx.inputs.map((input) => ({
...input,
value: input.satoshis,
})),
new BigNumber(encodedTx.transferInfo?.amount || 0)
.shiftedBy(network.decimals)
.toFixed(),
await this.getMinTransferAmount(),
).length;
const estimateSizedSize = estimateSize(vinLength, encodedTx.outputs);
const remoteEstimateFee = await client.estimateFee(estimateSizedSize);
const localEstimateFee = estimateFee(encodedTx);
const localEstimateFee = estimateFee(vinLength, encodedTx.outputs);
const feeInfo = specifiedFeeRate
? estimateFee(encodedTx, Number(specifiedFeeRate))
? estimateFee(vinLength, encodedTx.outputs, Number(specifiedFeeRate))
: Math.max(remoteEstimateFee, localEstimateFee);
return {
nativeSymbol: network.symbol,
Expand Down
1 change: 0 additions & 1 deletion packages/engine/src/vaults/impl/nexa/sdk/nexa.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export class Nexa extends SimpleClient {
async getTransaction(txHash: string): Promise<INexaTransaction> {
return this.rpc.call<INexaTransaction>('blockchain.transaction.get', [
txHash,
true,
]);
}

Expand Down
18 changes: 11 additions & 7 deletions packages/engine/src/vaults/impl/nexa/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,29 @@ const DEFAULT_SEQNUMBER = MAXINT;
const FEE_PER_KB = 1000 * 3;
const CHANGE_OUTPUT_MAX_SIZE = 1 + 8 + 1 + 23;

export function estimateSize(encodedTx: IEncodedTxNexa) {
export function estimateSize(
vinlength: number,
vouts: IEncodedTxNexa['outputs'],
) {
let estimatedSize = 4 + 1; // locktime + version
estimatedSize += encodedTx.inputs.length < 253 ? 1 : 3;
encodedTx.inputs.forEach(() => {
estimatedSize += vinlength < 253 ? 1 : 3;
new Array(vinlength).fill(0).forEach(() => {
// type + outpoint + scriptlen + script + sequence + amount
estimatedSize += 1 + 32 + 1 + 100 + 4 + 8;
});
encodedTx.outputs.forEach((output) => {
vouts.forEach((output) => {
const bfr = getScriptBufferFromScriptTemplateOut(output.address);
estimatedSize += convertScriptToPushBuffer(bfr).length + 1 + 8 + 1;
});
return estimatedSize;
}

export function estimateFee(
encodedTx: IEncodedTxNexa,
vinlength: number,
vouts: IEncodedTxNexa['outputs'],
feeRate = FEE_PER_KB / 1000,
): number {
const size = estimateSize(encodedTx);
const size = estimateSize(vinlength, vouts);
const feeWithChange = Math.ceil(
size * feeRate + CHANGE_OUTPUT_MAX_SIZE * feeRate,
);
Expand Down Expand Up @@ -292,7 +296,7 @@ function buildSignatures(encodedTx: IEncodedTxNexa, dbAccountAddress: string) {
new BN(0),
);

const fee = new BN(gas || estimateFee(encodedTx));
const fee = new BN(gas || estimateFee(inputs.length, outputs));
const available = inputAmount.sub(fee);
if (available.lt(new BN(0))) {
console.error(inputAmount.toString(), fee.toString());
Expand Down
Loading