diff --git a/extension/e2e-tests/addAsset.test.ts b/extension/e2e-tests/addAsset.test.ts
index fdca9ddb3..7e8a37d95 100644
--- a/extension/e2e-tests/addAsset.test.ts
+++ b/extension/e2e-tests/addAsset.test.ts
@@ -1,5 +1,6 @@
import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures";
import { loginToTestAccount, PASSWORD } from "./helpers/login";
+import { TEST_TOKEN_ADDRESS } from "./helpers/test-token";
test("Adding unverified Soroban token", async ({ page, extensionId }) => {
test.slow();
@@ -15,13 +16,11 @@ test("Adding unverified Soroban token", async ({ page, extensionId }) => {
await expect(page.getByText("Your assets")).toBeVisible();
await page.getByText("Add an asset").click({ force: true });
await page.getByText("Add manually").click({ force: true });
- await page
- .getByTestId("search-token-input")
- .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6");
+ await page.getByTestId("search-token-input").fill(TEST_TOKEN_ADDRESS);
await expect(page.getByTestId("asset-notification")).toHaveText(
"Not on your listsFreighter uses asset lists to check assets you interact with. You can define your own assets lists in Settings.",
);
- await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E Token");
+ await expect(page.getByTestId("ManageAssetCode")).toHaveText("E2E");
await expect(page.getByTestId("ManageAssetRowButton")).toHaveText("Add");
await page.getByTestId("ManageAssetRowButton").click({ force: true });
diff --git a/extension/e2e-tests/helpers/login.ts b/extension/e2e-tests/helpers/login.ts
index c843196ee..ea528b888 100644
--- a/extension/e2e-tests/helpers/login.ts
+++ b/extension/e2e-tests/helpers/login.ts
@@ -22,7 +22,7 @@ export const loginAndFund = async ({ page, extensionId }) => {
});
await page.goto(`chrome-extension://${extensionId}/index.html#/account`);
- await page.getByTestId("BlockaidAnnouncement__accept").click();
+ // await page.getByTestId("BlockaidAnnouncement__accept").click();
await expect(page.getByTestId("network-selector-open")).toBeVisible({
timeout: 10000,
});
@@ -76,7 +76,7 @@ export const loginToTestAccount = async ({ page, extensionId }) => {
});
await page.goto(`chrome-extension://${extensionId}/index.html#/account`);
- await page.getByTestId("BlockaidAnnouncement__accept").click();
+ // await page.getByTestId("BlockaidAnnouncement__accept").click();
await expect(page.getByTestId("network-selector-open")).toBeVisible({
timeout: 50000,
});
diff --git a/extension/e2e-tests/helpers/test-token.ts b/extension/e2e-tests/helpers/test-token.ts
new file mode 100644
index 000000000..ce274d84f
--- /dev/null
+++ b/extension/e2e-tests/helpers/test-token.ts
@@ -0,0 +1,2 @@
+export const TEST_TOKEN_ADDRESS =
+ "CA5F3Q3KQWGG5J4U6OBCJEFVG4B5JVMHFRGQGMLNZXTEMO7YEO6UYMMD";
diff --git a/extension/e2e-tests/sendPayment.test.ts b/extension/e2e-tests/sendPayment.test.ts
index 058e5f5d8..bc59800ed 100644
--- a/extension/e2e-tests/sendPayment.test.ts
+++ b/extension/e2e-tests/sendPayment.test.ts
@@ -1,5 +1,6 @@
import { test, expect, expectPageToHaveScreenshot } from "./test-fixtures";
import { loginAndFund, loginToTestAccount, PASSWORD } from "./helpers/login";
+import { TEST_TOKEN_ADDRESS } from "./helpers/test-token";
test("Send XLM payment to G address", async ({ page, extensionId }) => {
test.slow();
@@ -78,9 +79,7 @@ test("Send XLM payment to C address", async ({ page, extensionId }) => {
// send XLM to C address
await page.getByTitle("Send Payment").click({ force: true });
await expect(page.getByText("Send To")).toBeVisible();
- await page
- .getByTestId("send-to-input")
- .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6");
+ await page.getByTestId("send-to-input").fill(TEST_TOKEN_ADDRESS);
await page.getByText("Continue").click({ force: true });
await expect(page.getByText("Send XLM")).toBeVisible();
@@ -172,9 +171,7 @@ test("Send SAC to C address", async ({ page, extensionId }) => {
// send SAC to C address
await page.getByTitle("Send Payment").click({ force: true });
- await page
- .getByTestId("send-to-input")
- .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6");
+ await page.getByTestId("send-to-input").fill(TEST_TOKEN_ADDRESS);
await page.getByText("Continue").click({ force: true });
await page.getByTestId("send-amount-asset-select").click({ force: true });
@@ -227,17 +224,13 @@ test("Send token payment to C address", async ({ page, extensionId }) => {
await expect(page.getByText("Your assets")).toBeVisible();
await page.getByText("Add an asset").click({ force: true });
await page.getByText("Add manually").click({ force: true });
- await page
- .getByTestId("search-token-input")
- .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6");
+ await page.getByTestId("search-token-input").fill(TEST_TOKEN_ADDRESS);
await page.getByTestId("ManageAssetRowButton").click({ force: true });
await page.getByTestId("add-asset").dispatchEvent("click");
// send E2E token to C address
await page.getByTitle("Send Payment").click({ force: true });
- await page
- .getByTestId("send-to-input")
- .fill("CAHX2LUNQ4YKNJTDEFW2LSFOXDAL4QI4736RV52ZUGCIRJK5U7MWQWW6");
+ await page.getByTestId("send-to-input").fill(TEST_TOKEN_ADDRESS);
await page.getByText("Continue").click({ force: true });
await page.getByTestId("send-amount-asset-select").click({ force: true });
diff --git a/extension/src/popup/components/__tests__/HistoryItem.test.tsx b/extension/src/popup/components/__tests__/HistoryItem.test.tsx
index 9075f5d03..a905c4b5d 100644
--- a/extension/src/popup/components/__tests__/HistoryItem.test.tsx
+++ b/extension/src/popup/components/__tests__/HistoryItem.test.tsx
@@ -4,6 +4,7 @@ import { render, waitFor, screen } from "@testing-library/react";
import { HistoryItem } from "popup/components/accountHistory/HistoryItem";
import { TESTNET_NETWORK_DETAILS } from "@shared/constants/stellar";
import * as sorobanHelpers from "popup/helpers/soroban";
+import * as internalApi from "@shared/api/internal";
describe("HistoryItem", () => {
afterAll(() => {
@@ -20,6 +21,13 @@ describe("HistoryItem", () => {
amount: "100000000",
};
});
+ jest.spyOn(internalApi, "getTokenDetails").mockImplementation(() => {
+ return Promise.resolve({
+ decimals: 7,
+ name: "native",
+ symbol: "XLM",
+ });
+ });
it("renders SAC transfers as payments", async () => {
const props = {
accountBalances: {
diff --git a/extension/src/popup/components/accountHistory/HistoryItem/index.tsx b/extension/src/popup/components/accountHistory/HistoryItem/index.tsx
index 7d618e7c7..2e4f9d792 100644
--- a/extension/src/popup/components/accountHistory/HistoryItem/index.tsx
+++ b/extension/src/popup/components/accountHistory/HistoryItem/index.tsx
@@ -397,17 +397,26 @@ export const HistoryItem = ({
setIconComponent(
,
);
+ setIsLoading(true);
- if (!tokenKey) {
- // TODO: attempt to fetch token details, not stored
- setRowText(operationString);
- setTxDetails((_state) => ({
- ..._state,
- headerTitle: translations("Transaction"),
- operationText: operationString,
- }));
- } else {
- const { token, decimals } = balances[tokenKey] as TokenBalance;
+ try {
+ const tokenDetailsResponse = await getTokenDetails({
+ contractId: attrs.contractId,
+ publicKey,
+ networkDetails,
+ });
+
+ if (!tokenDetailsResponse) {
+ setRowText(operationString);
+ setTxDetails((_state) => ({
+ ..._state,
+ headerTitle: translations("Transaction"),
+ operationText: operationString,
+ }));
+ }
+
+ const { symbol, decimals } = tokenDetailsResponse!;
+ const code = symbol === "native" ? "XLM" : symbol;
const formattedTokenAmount = formatTokenAmount(
new BigNumber(attrs.amount),
decimals,
@@ -418,7 +427,7 @@ export const HistoryItem = ({
setBodyComponent(
<>
{paymentDifference}
- {formattedTokenAmount} {token.code}
+ {formattedTokenAmount} {code}
>,
);
setIconComponent(
@@ -428,7 +437,7 @@ export const HistoryItem = ({
),
);
- setRowText(token.code);
+ setRowText(code);
setDateText(
(_dateText) =>
`${
@@ -440,9 +449,19 @@ export const HistoryItem = ({
isRecipient: _isRecipient,
headerTitle: `${
_isRecipient ? translations("Received") : translations("Sent")
- } ${token.code}`,
- operationText: `${paymentDifference}${formattedTokenAmount} ${token.code}`,
+ } ${code}`,
+ operationText: `${paymentDifference}${formattedTokenAmount} ${code}`,
+ }));
+ } catch (error) {
+ // falls back to only showing contract ID
+ setRowText(operationString);
+ setTxDetails((_state) => ({
+ ..._state,
+ headerTitle: translations("Transaction"),
+ operationText: operationString,
}));
+ } finally {
+ setIsLoading(false);
}
} else {
setRowText(operationString);
diff --git a/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx b/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx
index 9d08d96e2..910d4aa06 100644
--- a/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx
+++ b/extension/src/popup/components/sendPayment/SendConfirm/TransactionDetails/index.tsx
@@ -258,7 +258,13 @@ const getBuiltTx = async (
.setTimeout(transactionTimeout);
};
-export const TransactionDetails = ({ goBack }: { goBack: () => void }) => {
+export const TransactionDetails = ({
+ goBack,
+ shouldScanTx,
+}: {
+ goBack: () => void;
+ shouldScanTx: boolean;
+}) => {
const dispatch: AppDispatch = useDispatch();
const submission = useSelector(transactionSubmissionSelector);
const {
@@ -289,7 +295,7 @@ export const TransactionDetails = ({ goBack }: { goBack: () => void }) => {
const isPathPayment = useSelector(isPathPaymentSelector);
const { isMemoValidationEnabled } = useSelector(settingsSelector);
const isSwap = useIsSwap();
- const { scanTx, data: scanResult, isLoading } = useScanTx();
+ const { scanTx, data: scanResult, isLoading, setLoading } = useScanTx();
const { t } = useTranslation();
@@ -349,6 +355,7 @@ export const TransactionDetails = ({ goBack }: { goBack: () => void }) => {
const url = "internal"; // blockaid prefers a URL for this endpoint, but this does not originate from a URL
const scanSorobanTx = async () => {
if (
+ shouldScanTx &&
submission.submitStatus === ActionStatus.IDLE &&
transactionSimulation.preparedTransaction
) {
@@ -358,28 +365,32 @@ export const TransactionDetails = ({ goBack }: { goBack: () => void }) => {
networkDetails,
);
}
+ setLoading(false);
};
const scanClassicTx = async () => {
- const transaction = await getBuiltTx(
- publicKey,
- {
- sourceAsset,
- destAsset,
- amount,
- destinationAmount,
- destination,
- allowedSlippage,
- path,
- isPathPayment,
- isSwap,
- isFunded: destinationBalances.isFunded!,
- },
- transactionFee,
- transactionTimeout,
- networkDetails,
- );
+ if (shouldScanTx) {
+ const transaction = await getBuiltTx(
+ publicKey,
+ {
+ sourceAsset,
+ destAsset,
+ amount,
+ destinationAmount,
+ destination,
+ allowedSlippage,
+ path,
+ isPathPayment,
+ isSwap,
+ isFunded: destinationBalances.isFunded!,
+ },
+ transactionFee,
+ transactionTimeout,
+ networkDetails,
+ );
- await scanTx(transaction.build().toXDR(), url, networkDetails);
+ await scanTx(transaction.build().toXDR(), url, networkDetails);
+ }
+ setLoading(false);
};
if (isToken || isSoroswap) {
scanSorobanTx();
diff --git a/extension/src/popup/components/sendPayment/SendConfirm/index.tsx b/extension/src/popup/components/sendPayment/SendConfirm/index.tsx
index 02ec3a732..fcfc5c988 100644
--- a/extension/src/popup/components/sendPayment/SendConfirm/index.tsx
+++ b/extension/src/popup/components/sendPayment/SendConfirm/index.tsx
@@ -24,6 +24,7 @@ export const SendConfirm = ({ previous }: { previous: ROUTES }) => {
if (isSendComplete) {
return (
{
dispatch(resetSubmission());
navigateTo(ROUTES.accountHistory);
@@ -33,15 +34,30 @@ export const SendConfirm = ({ previous }: { previous: ROUTES }) => {
}
switch (submission.submitStatus) {
case ActionStatus.IDLE:
- return navigateTo(previous)} />;
+ return (
+ navigateTo(previous)}
+ />
+ );
case ActionStatus.PENDING:
- return navigateTo(previous)} />;
+ return (
+ navigateTo(previous)}
+ />
+ );
case ActionStatus.SUCCESS:
return setIsSendComplete(true)} />;
case ActionStatus.ERROR:
return ;
default:
- return navigateTo(previous)} />;
+ return (
+ navigateTo(previous)}
+ />
+ );
}
};
diff --git a/extension/src/popup/helpers/blockaid.ts b/extension/src/popup/helpers/blockaid.ts
index 0ce50bec5..aa4aa5f67 100644
--- a/extension/src/popup/helpers/blockaid.ts
+++ b/extension/src/popup/helpers/blockaid.ts
@@ -106,6 +106,7 @@ export const useScanTx = () => {
data,
error,
isLoading,
+ setLoading,
scanTx,
};
};
diff --git a/extension/src/popup/helpers/soroban.ts b/extension/src/popup/helpers/soroban.ts
index 70a98b150..3d6aaf415 100644
--- a/extension/src/popup/helpers/soroban.ts
+++ b/extension/src/popup/helpers/soroban.ts
@@ -111,6 +111,13 @@ export const parseTokenAmount = (value: string, decimals: number) => {
return wholeValue.shiftedBy(decimals).plus(fractionValue);
};
+export const addressToString = (address: xdr.ScAddress) => {
+ if (address.switch().name === "scAddressTypeAccount") {
+ return StrKey.encodeEd25519PublicKey(address.accountId().ed25519());
+ }
+ return StrKey.encodeContract(address.contractId());
+};
+
export const getArgsForTokenInvocation = (
fnName: string,
args: xdr.ScVal[],
@@ -121,18 +128,12 @@ export const getArgsForTokenInvocation = (
switch (fnName) {
case SorobanTokenInterface.transfer:
- from = StrKey.encodeEd25519PublicKey(
- args[0].address().accountId().ed25519(),
- );
- to = StrKey.encodeEd25519PublicKey(
- args[1].address().accountId().ed25519(),
- );
+ from = addressToString(args[0].address());
+ to = addressToString(args[1].address());
amount = scValToNative(args[2]);
break;
case SorobanTokenInterface.mint:
- to = StrKey.encodeEd25519PublicKey(
- args[0].address().accountId().ed25519(),
- );
+ to = addressToString(args[0].address());
amount = scValToNative(args[1]);
break;
default:
diff --git a/extension/src/popup/views/__tests__/SendPayment.test.tsx b/extension/src/popup/views/__tests__/SendPayment.test.tsx
index fd8693f0f..4f1fe1e5f 100644
--- a/extension/src/popup/views/__tests__/SendPayment.test.tsx
+++ b/extension/src/popup/views/__tests__/SendPayment.test.tsx
@@ -40,6 +40,7 @@ jest.spyOn(BlockaidHelpers, "useScanTx").mockImplementation(() => {
return {
scanTx: () => Promise.resolve(null),
isLoading: false,
+ setLoading: () => {},
data: null,
error: null,
};
diff --git a/extension/src/popup/views/__tests__/Swap.test.tsx b/extension/src/popup/views/__tests__/Swap.test.tsx
index 094a1f1e7..387522612 100644
--- a/extension/src/popup/views/__tests__/Swap.test.tsx
+++ b/extension/src/popup/views/__tests__/Swap.test.tsx
@@ -111,6 +111,7 @@ jest.spyOn(BlockaidHelpers, "useScanTx").mockImplementation(() => {
return {
scanTx: () => Promise.resolve(null),
isLoading: false,
+ setLoading: () => {},
data: null,
error: null,
};
diff --git a/package.json b/package.json
index cd3b9a609..bea41a107 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"start:unpacked": "yarn workspace extension start:unpacked-extension",
"install-if-package-changed": "git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD | grep --quiet yarn.lock && yarn setup || exit 0",
"test:ci": "jest --ci",
- "test:e2e": "yarn workspace extension test:e2e",
+ "test:e2e": "yarn workspace extension test:e2e --workers=1",
"test": "jest -o --watch",
"prepare": "husky install"
},