{errors.submit}
diff --git a/src/components/TokenSelector.tsx b/src/components/TokenSelector.tsx
index a157f15..7b56e7f 100644
--- a/src/components/TokenSelector.tsx
+++ b/src/components/TokenSelector.tsx
@@ -43,11 +43,11 @@ function getTokenName(token: TokenLike): string {
return token.name ?? token.symbol;
}
-function getTokenLogo(token: TokenLike): string {
+function getTokenLogo(token: Pick
& Partial>): string {
return token.logo ?? `/tokens/${token.symbol.toLowerCase()}.svg`;
}
-function getTokenIconLabel(token: TokenLike): string {
+function getTokenIconLabel(token: Pick & Partial>): string {
return token.iconLabel ?? (token.symbol.replace(/[^A-Z0-9]/gi, "").slice(0, 2).toUpperCase() || "TK");
}
diff --git a/src/components/__tests__/SubmitInvoiceForm.test.tsx b/src/components/__tests__/SubmitInvoiceForm.test.tsx
index 2fe1172..c0a892c 100644
--- a/src/components/__tests__/SubmitInvoiceForm.test.tsx
+++ b/src/components/__tests__/SubmitInvoiceForm.test.tsx
@@ -12,7 +12,7 @@
*/
import React from "react";
-import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
+import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { beforeEach, describe, expect, it, vi } from "vitest";
import SubmitInvoiceForm from "../SubmitInvoiceForm";
@@ -135,6 +135,21 @@ describe("SubmitInvoiceForm", () => {
).toBeInTheDocument();
});
+ it("rejects a payer address matching the connected freelancer wallet", async () => {
+ connectWallet(VALID_STELLAR_PAYER);
+ render();
+
+ fireEvent.change(screen.getByPlaceholderText("G..."), {
+ target: { value: VALID_STELLAR_PAYER },
+ });
+ fireEvent.click(screen.getByRole("button", { name: /submit invoice/i }));
+
+ expect(
+ await screen.findByText(/payer address must be different from your wallet address/i),
+ ).toBeInTheDocument();
+ expect(submitInvoiceTransaction).not.toHaveBeenCalled();
+ });
+
it("rejects a non-numeric invoice amount", async () => {
connectWallet();
render();
@@ -243,8 +258,9 @@ describe("SubmitInvoiceForm", () => {
it("submits a fully valid invoice and displays the returned invoice ID and tx hash", async () => {
connectWallet();
submitInvoiceTransaction.mockResolvedValue({ invoiceId: 99n, txHash: "deadbeef" });
+ const onSubmitted = vi.fn();
- render();
+ render();
fireEvent.change(screen.getByPlaceholderText("G..."), {
target: { value: VALID_STELLAR_PAYER },
@@ -270,6 +286,7 @@ describe("SubmitInvoiceForm", () => {
expect(await screen.findByText("Returned invoice ID")).toBeInTheDocument();
expect(screen.getByText("#99")).toBeInTheDocument();
expect(screen.getByText(/Transaction hash: deadbeef/)).toBeInTheDocument();
+ expect(onSubmitted).toHaveBeenCalledWith("99");
});
it("disables the submit button while the transaction is in-flight", async () => {
@@ -326,7 +343,7 @@ describe("SubmitInvoiceForm", () => {
await waitFor(() => expect(updateToast).toHaveBeenCalled());
expect(addToast).toHaveBeenCalledWith(
- expect.objectContaining({ type: "pending", title: /submitting invoice/i }),
+ expect.objectContaining({ type: "pending", title: expect.stringMatching(/submitting invoice/i) }),
);
expect(updateToast).toHaveBeenCalledWith(
"toast-id-1",
diff --git a/src/utils/federation.ts b/src/utils/federation.ts
index 11922eb..b1d734b 100644
--- a/src/utils/federation.ts
+++ b/src/utils/federation.ts
@@ -11,7 +11,8 @@ export async function resolveFederatedAddress(address: string): Promise
try {
const account = await horizonServer.getAccount(address);
- const homeDomain = account.home_domain ?? (account as any).homeDomain;
+ const accountWithHomeDomain = account as { home_domain?: string; homeDomain?: string };
+ const homeDomain = accountWithHomeDomain.home_domain ?? accountWithHomeDomain.homeDomain;
if (!homeDomain) return address;
const stellarTomlResponse = await fetch(`https://${homeDomain}/.well-known/stellar.toml`);
diff --git a/src/utils/invoiceSubmission.ts b/src/utils/invoiceSubmission.ts
index 098d2a1..9881e14 100644
--- a/src/utils/invoiceSubmission.ts
+++ b/src/utils/invoiceSubmission.ts
@@ -9,6 +9,7 @@ export interface InvoiceFormValues {
dueDate: string;
discountRate: string;
tokenId: string;
+ referralCode: string;
}
export interface YieldPreview {
@@ -122,6 +123,7 @@ export function validateInvoiceForm(
decimals = 7,
tokenSymbol = "USDC",
nowInSeconds = Math.floor(Date.now() / 1000),
+ freelancerAddress?: string | null,
): Partial> {
const errors: Partial> = {};
@@ -133,6 +135,8 @@ export function validateInvoiceForm(
errors.payer = "Payer Stellar address is required.";
} else if (!isValidStellarAccount(values.payer)) {
errors.payer = "Enter a valid Stellar public key for the payer.";
+ } else if (freelancerAddress && values.payer.trim() === freelancerAddress) {
+ errors.payer = "Payer address must be different from your wallet address.";
}
const amountUnits = parseAmountToUnits(values.amount, decimals);