Skip to content

Commit

Permalink
Merge pull request #9126 from LedgerHQ/fix_tezos
Browse files Browse the repository at this point in the history
fix(coin:tezos): handle missing recipients or senders cleanly
  • Loading branch information
hedi-edelbloute authored Feb 14, 2025
2 parents 0ae157c + 339a611 commit d423a3e
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .changeset/wise-hornets-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ledgerhq/coin-tezos": patch
---

Fix addresses on operations
1 change: 1 addition & 0 deletions libs/coin-modules/coin-tezos/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
"lint": "eslint ./src --no-error-on-unmatched-pattern --ext .ts,.tsx --cache",
"lint:fix": "pnpm lint --fix",
"test": "jest",
"test-watch": "jest --watch",
"test-integ": "jest --config=jest.integ.config.js",
"unimported": "unimported"
},
Expand Down
101 changes: 101 additions & 0 deletions libs/coin-modules/coin-tezos/src/logic/listOperations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { listOperations } from "./listOperations";
import { APIDelegationType, APITransactionType } from "../network/types";

const mockNetworkGetTransactions = jest.fn();
jest.mock("../network", () => ({
tzkt: {
getAccountOperations: async () => {
return mockNetworkGetTransactions();
},
},
}));

describe("listOperations", () => {
afterEach(() => {
mockNetworkGetTransactions.mockClear();
});

it("should return no operations", async () => {
// Given
mockNetworkGetTransactions.mockResolvedValue([]);
// When
const [results, token] = await listOperations("any address", {});
// Then
expect(results).toEqual([]);
expect(token).toEqual("");
});

const someDestinationAddress = "tz3Vq38qYD3GEbWcXHMLt5PaASZrkDtEiA8D";
const someSenderAddress = "tz2CVMDVA16dD9A7kpWym2ptGDhs5zUhwWXr";
const delegate: APIDelegationType = {
type: "delegation",
id: 111,
level: 2702551,
block: "BMJ1ZQ6",
timestamp: "2022-09-12T01:36:59Z",
amount: 724846,
sender: {
address: someSenderAddress,
},
counter: 65214462,
prevDelegate: {
address: someDestinationAddress,
},
newDelegate: null,
};

const undelegate: APIDelegationType = {
...delegate,
id: 222,
prevDelegate: null,
newDelegate: { address: someDestinationAddress },
};

const transfer: APITransactionType = {
...delegate,
id: 333,
initiator: null,
type: "transaction",
target: { address: someDestinationAddress },
};

it.each([undelegate, delegate, transfer])(
"should return operation with proper recipient list",
async operation => {
// Given
mockNetworkGetTransactions.mockResolvedValue([operation]);
// When
const [results, token] = await listOperations("any address", {});
// Then
expect(results.length).toEqual(1);
expect(results[0].recipients).toEqual([someDestinationAddress]);
expect(token).toEqual(JSON.stringify(operation.id));
},
);

it.each([
{ ...undelegate, newDelegate: null, prevDelegate: null },
{ ...transfer, target: null },
])("should return empty recipient list when no target can be found", async operation => {
// Given
mockNetworkGetTransactions.mockResolvedValue([operation]);
// When
const [results, token] = await listOperations("any address", {});
// Then
expect(results.length).toEqual(1);
expect(results[0].recipients).toEqual([]);
expect(token).toEqual(JSON.stringify(operation.id));
});

it("should return empty sender list when no sender can be found", async () => {
// Given
const operation = { ...undelegate, sender: null };
mockNetworkGetTransactions.mockResolvedValue([operation]);
// When
const [results, token] = await listOperations("any address", {});
// Then
expect(results.length).toEqual(1);
expect(results[0].senders).toEqual([]);
expect(token).toEqual(JSON.stringify(operation.id));
});
});
31 changes: 24 additions & 7 deletions libs/coin-modules/coin-tezos/src/logic/listOperations.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { tzkt } from "../network";
import { log } from "@ledgerhq/logs";
import {
type APIDelegationType,
type APITransactionType,
Expand Down Expand Up @@ -33,25 +34,41 @@ export async function listOperations(
}
const operations = await tzkt.getAccountOperations(address, options);
const lastOperation = operations.slice(-1)[0];
const nextId = lastOperation ? JSON.stringify(lastOperation?.id) : "";
const nextToken = lastOperation ? JSON.stringify(lastOperation?.id) : "";
return [
operations
.filter(op => isAPITransactionType(op) || isAPIDelegationType(op))
.reduce((acc, op) => acc.concat(convertOperation(address, op)), [] as Operation[]),
nextId,
nextToken,
];
}

// note that "initiator" of APITransactionType is never used in the conversion
function convertOperation(
address: string,
operation: APITransactionType | APIDelegationType,
): Operation {
const { amount, hash, storageFee, sender, timestamp, type, counter } = operation;
let targetAddress = "";
if (isAPITransactionType(operation) && operation.target) {
targetAddress = operation.target.address;

let targetAddress = undefined;
if (isAPITransactionType(operation)) {
targetAddress = operation?.target?.address;
} else if (isAPIDelegationType(operation)) {
// delegate and undelegate has the type, but hold the address in different fields
targetAddress = operation?.newDelegate?.address || operation?.prevDelegate?.address;
}

const recipients = [];
if (!targetAddress) {
log("coin:tezos", "(logic/operations): No target address found for operation", operation);
} else {
recipients.push(targetAddress);
}

const senders = sender?.address ? [sender.address] : [];

return {
// hash id defined nullable in the tzkt API, but I wonder when it would be null ?
hash: hash ?? "",
address,
type: type,
Expand All @@ -63,8 +80,8 @@ function convertOperation(
height: operation.level,
time: new Date(operation.timestamp),
},
senders: [sender?.address ?? ""],
recipients: [targetAddress],
senders: senders,
recipients: recipients,
date: new Date(timestamp),
transactionSequenceNumber: counter,
};
Expand Down

0 comments on commit d423a3e

Please sign in to comment.