Skip to content

Commit

Permalink
Fix: LIVE-8887 pending tx for evm family (#4268)
Browse files Browse the repository at this point in the history
  • Loading branch information
hzheng-ledger authored Aug 9, 2023
1 parent 4b89050 commit 707e59f
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-socks-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/coin-evm": patch
---

fix pending transaction bug in evm family
27 changes: 20 additions & 7 deletions libs/coin-evm/src/__tests__/unit/synchronization.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,18 +621,31 @@ describe("EVM Family", () => {
subAccounts: [tokenAccountWithPending],
pendingOperations: [pendingOperation],
};
const accountWithoutPending = {
...accountWithPending,
subAccounts: [tokenAccountWithPending],
pendingOperations: [],
};

// should not change anything if we maintain the pending op
expect(synchronization.postSync(accountWithPending, accountWithPending)).toEqual(
accountWithPending,
);
// Should remove the pending from tokenAccount as well if removed from main account
expect(
synchronization.postSync(accountWithPending, {
...accountWithPending,
pendingOperations: [],
}),
).toEqual(account);
// should keep pending ops even if the synced account(second parameter of postSync) has no pending op, because the pending op will be recalculated form the initial account
expect(synchronization.postSync(accountWithPending, accountWithoutPending)).toEqual(
accountWithPending,
);

// Should remove the pending from account if the pending operations become operations from main account
const updateAccount = synchronization.postSync(accountWithPending, {
...accountWithPending,
operations: [pendingOperation],
pendingOperations: [],
});
expect(updateAccount.pendingOperations).toHaveLength(0);

// Should remove the pending from tokenAccount if it was confirmed in the main account ops
expect(updateAccount.subAccounts?.[0].pendingOperations).toHaveLength(0);
});

it("should remove pending operation if the token account has confirmed it", () => {
Expand Down
29 changes: 20 additions & 9 deletions libs/coin-evm/src/synchronization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
emptyHistoryCache,
encodeAccountId,
encodeTokenAccountId,
shouldRetainPendingOperation,
} from "@ledgerhq/coin-framework/account/index";
import {
AccountShapeInfo,
Expand Down Expand Up @@ -246,19 +245,30 @@ export const getOperationStatus = async (
* inside of subAccounts.
*/
export const postSync = (initial: Account, synced: Account): Account => {
// Set of hashes from the pending operations of the main account
const coinPendingOperationsHashes = new Set();
for (const coinPendingOperation of synced.pendingOperations) {
coinPendingOperationsHashes.add(coinPendingOperation.hash);
}
// Get the latest nonce from the synced account
const lastOperation = synced.operations.find(op => ["OUT", "FEES", "NFT_OUT"].includes(op.type));
const latestNonce = lastOperation?.transactionSequenceNumber || -1;
// Set of ids from the already existing subAccount from previous sync
const initialSubAccountsIds = new Set();
for (const subAccount of initial.subAccounts || []) {
initialSubAccountsIds.add(subAccount.id);
}

const initialPendingOperations = initial.pendingOperations || [];
const { operations } = synced;
const pendingOperations = initialPendingOperations.filter(
op =>
!operations.some(o => o.hash === op.hash) &&
op.transactionSequenceNumber !== undefined &&
op.transactionSequenceNumber > latestNonce,
);
// Set of hashes from the pending operations of the main account
const coinPendingOperationsHashes = new Set();
for (const op of pendingOperations) {
coinPendingOperationsHashes.add(op.hash);
}
return {
...synced,
pendingOperations,
subAccounts: synced.subAccounts?.map(subAccount => {
// If the subAccount is new, just return the freshly synced subAccount
if (!initialSubAccountsIds.has(subAccount.id)) return subAccount;
Expand All @@ -271,8 +281,9 @@ export const postSync = (initial: Account, synced: Account): Account => {
coinPendingOperationsHashes.has(tokenPendingOperation.hash) &&
// if the transaction has been confirmed, remove it
!subAccount.operations.some(op => op.hash === tokenPendingOperation.hash) &&
// common rule for pending operations retention in the live
shouldRetainPendingOperation(synced, tokenPendingOperation),
// if the nonce is still lower than the last one in operations, keep it
tokenPendingOperation.transactionSequenceNumber !== undefined &&
tokenPendingOperation.transactionSequenceNumber > latestNonce,
),
};
}),
Expand Down

1 comment on commit 707e59f

@vercel
Copy link

@vercel vercel bot commented on 707e59f Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.