Skip to content

Commit

Permalink
wrap sac match logic in try catch to gracefully fall through (#1773)
Browse files Browse the repository at this point in the history
* wrap sac match logic in try catch to gracefully fall through

* add helper for getting token balance by key, add test for history item rendering SAC payments when user has an LP share in balances

* adds test for getBalanceByKey helper

---------

Co-authored-by: Aristides Staffieri <[email protected]>
  • Loading branch information
piyalbasu and aristidesstaffieri authored Jan 10, 2025
1 parent cc23d21 commit 619bb04
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 19 deletions.
3 changes: 2 additions & 1 deletion @shared/helpers/stellar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { INDEXER_URL } from "@shared/constants/mercury";

export const CUSTOM_NETWORK = "STANDALONE";
export const LP_ISSUER_KEY = "lp";

export const isNextSdk = (networkPassphrase: string) =>
[""].includes(networkPassphrase);
Expand All @@ -40,7 +41,7 @@ export function getBalanceIdentifier(
return `${balance.asset_code}:${balance.asset_issuer}`;

case "liquidity_pool_shares":
return `${balance.liquidity_pool_id}:lp`;
return `${balance.liquidity_pool_id}:${LP_ISSUER_KEY}`;

default:
return "native";
Expand Down
53 changes: 53 additions & 0 deletions extension/src/popup/components/__tests__/HistoryItem.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from "react";
import { render, waitFor, screen } from "@testing-library/react";
import BigNumber from "bignumber.js";

import { HistoryItem } from "popup/components/accountHistory/HistoryItem";
import { TESTNET_NETWORK_DETAILS } from "@shared/constants/stellar";
Expand Down Expand Up @@ -73,4 +74,56 @@ describe("HistoryItem", () => {
"+10 XLM",
);
});
it("renders SAC transfer correctly when balance includes LP shares", async () => {
const props = {
accountBalances: {
balances: {
"a468d41d8e9b8f3c7209651608b74b7db7ac9952dcae0cdf24871d1d9c7b0088:lp":
{
liquidityPoolId:
"a468d41d8e9b8f3c7209651608b74b7db7ac9952dcae0cdf24871d1d9c7b0088",
total: new BigNumber(10),
limit: new BigNumber(100),
},
native: {
token: {
code: "XLM",
},
decimals: 7,
},
} as any,
isFunded: true,
subentryCount: 0,
},
operation: {
account: "GCGORBD5DB4JDIKVIA536CJE3EWMWZ6KBUBWZWRQM7Y3NHFRCLOKYVAL",
amount: "10",
asset_code: "XLM",
created_at: Date.now(),
id: "op-id",
to: "GBTYAFHGNZSTE4VBWZYAGB3SRGJEPTI5I4Y22KZ4JTVAN56LESB6JZOF",
from: "GCGORBD5DB4JDIKVIA536CJE3EWMWZ6KBUBWZWRQM7Y3NHFRCLOKYVAL",
starting_balance: "10",
type: "invokeHostFunction",
type_i: 24,
transaction_attr: {
operation_count: 1,
},
isCreateExternalAccount: false,
isPayment: false,
isSwap: false,
} as any,
publicKey: "GBTYAFHGNZSTE4VBWZYAGB3SRGJEPTI5I4Y22KZ4JTVAN56LESB6JZOF",
url: "example.com",
networkDetails: TESTNET_NETWORK_DETAILS,
setDetailViewProps: () => null,
setIsDetailViewShowing: () => null,
};
render(<HistoryItem {...props} />);
await waitFor(() => screen.getByTestId("history-item"));
expect(screen.getByTestId("history-item")).toBeDefined();
expect(screen.getByTestId("history-item-body-component")).toHaveTextContent(
"+10 XLM",
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ $loader-light-color: #444961;

&--lp-share,
&--soroban-token {
border: 1px solid var(--sds-clr-gray-02);
border: 1px solid var(--sds-clr-gray-06);
height: 32px;
width: 32px;
border-radius: 2rem;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import camelCase from "lodash/camelCase";
import { Icon, Loader } from "@stellar/design-system";
import { BigNumber } from "bignumber.js";
import { useTranslation } from "react-i18next";
import { Asset } from "stellar-sdk";

import { OPERATION_TYPES } from "constants/transaction";
import { SorobanTokenInterface } from "@shared/constants/soroban/token";
Expand All @@ -17,9 +16,9 @@ import { emitMetric } from "helpers/metrics";
import {
formatTokenAmount,
getAttrsFromSorobanHorizonOp,
isContractId,
} from "popup/helpers/soroban";
import { formatAmount } from "popup/helpers/formatters";
import { getBalanceByKey } from "popup/helpers/balance";

import {
AccountBalancesInterface,
Expand Down Expand Up @@ -224,21 +223,11 @@ export const HistoryItem = ({
const balances =
accountBalances.balances || ({} as NonNullable<Balances>);

const tokenKey = Object.keys(balances).find((balanceKey) => {
const [code, issuer] =
balanceKey === "native" ? ["XLM"] : balanceKey.split(":");
const matchesIssuer = attrs?.contractId === issuer;

// if issuer if a G address or xlm, check for a SAC match
if ((issuer && !isContractId(issuer)) || code === "XLM") {
const sacAddress = new Asset(code, issuer).contractId(
networkDetails.networkPassphrase,
);
const matchesSac = attrs?.contractId === sacAddress;
return matchesSac;
}
return matchesIssuer;
});
const tokenKey = getBalanceByKey(
attrs.contractId,
balances,
networkDetails,
);

if (!attrs) {
setRowText(operationString);
Expand Down
67 changes: 67 additions & 0 deletions extension/src/popup/helpers/__tests__/balance.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import BigNumber from "bignumber.js";

import { TESTNET_NETWORK_DETAILS } from "@shared/constants/stellar";
import { defaultBlockaidScanAssetResult } from "@shared/helpers/stellar";

import { getBalanceByKey } from "../balance";

const CLASSIC_ISSUER =
"CBANAM7DMVGXE7C3XPE2RZ7RU5FSVGY7RELHN2B6F5XLGFWX7BQ5D7S5";
const CONTRACT_ID = "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";
const TOKEN_BALANCE_KEY = `DT:${CONTRACT_ID}`;
const CLASSIC_BALANCE_KEY =
"USDC:GCK3D3V2XNLLKRFGFFFDEJXA4O2J4X36HET2FE446AV3M4U7DPHO3PEM";
const LP_ID =
"a468d41d8e9b8f3c7209651608b74b7db7ac9952dcae0cdf24871d1d9c7b0088";
const BALANCES = {
[TOKEN_BALANCE_KEY]: {
token: {
code: "DT",
issuer: {
key: "CCXVDIGMR6WTXZQX2OEVD6YM6AYCYPXPQ7YYH6OZMRS7U6VD3AVHNGBJ",
},
},
decimals: 7,
total: new BigNumber("1000000000"),
available: new BigNumber("1000000000"),
blockaidData: defaultBlockaidScanAssetResult,
},
[CLASSIC_BALANCE_KEY]: {
token: {
code: "USDC",
issuer: {
key: "GCK3D3V2XNLLKRFGFFFDEJXA4O2J4X36HET2FE446AV3M4U7DPHO3PEM",
},
},
total: new BigNumber("100"),
available: new BigNumber("100"),
blockaidData: defaultBlockaidScanAssetResult,
},
[`${LP_ID}:lp`]: {
liquidityPoolId:
"a468d41d8e9b8f3c7209651608b74b7db7ac9952dcae0cdf24871d1d9c7b0088",
total: new BigNumber(10),
limit: new BigNumber(100),
},
native: {
token: {
code: "XLM",
},
decimals: 7,
},
};

describe("getBalanceByKey", () => {
it("should return a valid key for a classic asset", () => {
const key = getBalanceByKey(
CLASSIC_ISSUER,
BALANCES,
TESTNET_NETWORK_DETAILS,
);
expect(key).toEqual(CLASSIC_BALANCE_KEY);
});
it("should return a valid key for a token", () => {
const key = getBalanceByKey(CONTRACT_ID, BALANCES, TESTNET_NETWORK_DETAILS);
expect(key).toEqual(TOKEN_BALANCE_KEY);
});
});
48 changes: 48 additions & 0 deletions extension/src/popup/helpers/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Asset } from "stellar-sdk";
import { captureException } from "@sentry/browser";

import { BalanceMap } from "@shared/api/types";
import { LP_ISSUER_KEY } from "@shared/helpers/stellar";
import { NetworkDetails } from "@shared/constants/stellar";
import { isContractId } from "./soroban";

/*
Attempts to match a balance to a related contract ID, expects a token or SAC contract ID.
BalanceMap keys can be one of two variants -
An asset balance - {code}:{issuer}
A token - {symbol}:{contract id}
An LP share - {LP ID}:lp
*/
export const getBalanceByKey = (
contractId: string,
balances: BalanceMap,
networkDetails: NetworkDetails,
) => {
const key = Object.keys(balances).find((balanceKey) => {
const [code, issuer] =
balanceKey === "native" ? ["XLM"] : balanceKey.split(":");
const matchesIssuer = contractId === issuer;

// if issuer is a G address or xlm, check for a SAC match
if (
(issuer && !isContractId(issuer) && issuer !== LP_ISSUER_KEY) ||
code === "XLM"
) {
try {
const sacAddress = new Asset(code, issuer).contractId(
networkDetails.networkPassphrase,
);
const matchesSac = contractId === sacAddress;
return matchesSac;
} catch (e) {
console.error(e);
captureException(
`Error checking for SAC match with code ${code} and issuer ${issuer}. Error: ${e}`,
);
}
}
return matchesIssuer;
});
return key;
};

0 comments on commit 619bb04

Please sign in to comment.