Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/1381 notifications fiat #1430

Merged
merged 19 commits into from
Sep 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
509d33b
refactor(currencyconvert): use localstorage for rate to #1020
escapedcat Aug 29, 2022
6a1781c
refactor(currencyconvert): update types, remove old code #1021
escapedcat Aug 29, 2022
433f8db
refactor(currencyconvert): remove isLatestRate and update tests #1021
escapedcat Aug 29, 2022
283760c
test(cache): getCurrencyRateFromCache #1021
escapedcat Aug 30, 2022
0f6bd52
refactor(currencyrate): move functionality into background-action #1021
escapedcat Sep 6, 2022
b4d9378
chore: cleanup and types #1021
escapedcat Sep 6, 2022
72ef196
chore: cleanup #1021
escapedcat Sep 8, 2022
33fa559
test(cache): update test to match action #1021
escapedcat Sep 8, 2022
b6fb4c4
test(currencyConvert): update test to match action #1021
escapedcat Sep 8, 2022
f5b1fc5
fix(cache): check if currency changed #1021
escapedcat Sep 13, 2022
1a08332
feat(notifications): show fiat value for payment #1381
escapedcat Sep 13, 2022
70048fd
test(notifications): skip tests for now because spys will not work an…
escapedcat Sep 13, 2022
b4f8bfd
feat(notifications): check showFiat #1381
escapedcat Sep 13, 2022
2219e28
chore: types #1381
escapedcat Sep 22, 2022
24387b6
refactor(notifications): use function from common #1381
escapedcat Sep 22, 2022
817704d
refactor(getcurrencyrate): switch btc-rate directly to sats-rate #1381
escapedcat Sep 22, 2022
235ee4c
test(notifications): with recepient with and without fiat #1381
escapedcat Sep 22, 2022
6a16d31
feat(notifications): update message to support long values #1381
escapedcat Sep 25, 2022
2c5e175
chore(currencyconvert): use explicit var-naming #1381
escapedcat Sep 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/utils/__tests__/currencyConvert.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe("Currency coversion utils", () => {
test("getFiatValue", async () => {
const result = await getFiatValue({
amount: 123456789,
rate: 29991.836,
rate: 0.00029991836,
currency: CURRENCIES["USD"],
});

Expand Down
13 changes: 3 additions & 10 deletions src/common/utils/currencyConvert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@
*/
import type { CURRENCIES } from "../constants";

const numSatsInBtc = 100_000_000;

const bitcoinToFiat = async (amountInBtc: number | string, rate: number) => {
return Number(amountInBtc) * Number(rate);
};

const satoshisToBitcoin = (amountInSatoshis: number | string) => {
return Number(amountInSatoshis) / numSatsInBtc;
const satsToFiat = async (amount: number | string, rate: number) => {
return Number(amount) * rate;
};

const satoshisToFiat = async ({
Expand All @@ -20,8 +14,7 @@ const satoshisToFiat = async ({
amountInSats: number | string;
rate: number;
}) => {
const btc = satoshisToBitcoin(amountInSats);
const fiat = await bitcoinToFiat(btc, rate);
const fiat = await satsToFiat(amountInSats, rate);
return fiat;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("currencyRate", () => {
test("storing rate for the first time", async () => {
(chrome.storage.local.get as jest.Mock).mockResolvedValue({});
const result = await getCurrencyRate(message);
expect(result.data.rate).toBe(29991.836);
expect(result.data.rate).toBe(0.00029991836);
});

test("storing rate if it is outdated", async () => {
Expand All @@ -46,9 +46,9 @@ describe("currencyRate", () => {
const result = await getCurrencyRate(message);
expect(chrome.storage.local.set).toHaveBeenCalledWith({
currencyRate:
'{"currency":"USD","rate":29991.836,"timestamp":1577836800000}',
'{"currency":"USD","rate":0.00029991836,"timestamp":1577836800000}',
});
expect(result.data.rate).toBe(29991.836);
expect(result.data.rate).toBe(0.00029991836);
});

test("returning rate if still valid", async () => {
Expand All @@ -75,7 +75,7 @@ describe("currencyRate", () => {
await getCurrencyRate(message);
expect(chrome.storage.local.set).toHaveBeenCalledWith({
currencyRate:
'{"currency":"USD","rate":29991.836,"timestamp":1577836800000}',
'{"currency":"USD","rate":0.00029991836,"timestamp":1577836800000}',
});
});
});
14 changes: 8 additions & 6 deletions src/extension/background-script/actions/cache/getCurrencyRate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ interface CurrencyRate {
timestamp?: number;
}

export const numSatsInBtc = 100_000_000;

const storeCurrencyRate = async ({ rate, currency }: CurrencyRate) => {
const currencyRate: CurrencyRate = {
currency,
Expand All @@ -37,25 +39,26 @@ const getFiatBtcRate = async (currency: CURRENCIES): Promise<number> => {
`https://api.yadio.io/exrates/${currency.toLowerCase()}`
);
const data = await response?.data;
return data.BTC;
return data.BTC / numSatsInBtc;
}

if (exchange === "coindesk") {
response = await axios.get(
`https://api.coindesk.com/v1/bpi/currentprice/${currency.toLowerCase()}.json`
);
const data = await response?.data;
return data.bpi[currency].rate_float;
return data.bpi[currency].rate_float / numSatsInBtc;
}

response = await axios.get(
`https://getalby.com/api/rates/${currency.toLowerCase()}.json`
);
const data = await response?.data;
return data[currency].rate_float;

return data[currency].rate_float / numSatsInBtc;
};

const getCurrencyRateFromCache = async (currency: CURRENCIES) => {
export const getCurrencyRateFromCache = async (currency: CURRENCIES) => {
let currencyRateCache: CurrencyRate = {};
const result = await browser.storage.local.get(["currencyRate"]);

Expand All @@ -67,13 +70,12 @@ const getCurrencyRateFromCache = async (currency: CURRENCIES) => {
dayjs(currencyRateCache?.timestamp).add(10, "minute")
);

if (isRateNewEnough) {
if (isRateNewEnough && currencyRateCache.rate) {
return currencyRateCache.rate;
}

const rate = await getFiatBtcRate(currency);
await storeCurrencyRate({ rate, currency });
// switch rate to be SATS not BTC
return rate;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,42 @@
import { CURRENCIES } from "~/common/constants";
import utils from "~/common/lib/utils";
import type { PaymentNotificationData, AuthNotificationData } from "~/types";
import state from "~/extension/background-script/state";
import type {
PaymentNotificationData,
AuthNotificationData,
SettingsStorage,
} from "~/types";

import {
paymentSuccessNotification,
lnurlAuthSuccessNotification,
} from "../notifications";

jest.mock("~/extension/background-script/actions/cache/getCurrencyRate", () => {
return {
getCurrencyRateFromCache: jest.fn(() => Promise.resolve(0.00019233)),
};
});

const settings: SettingsStorage = {
browserNotifications: true,
currency: CURRENCIES["USD"],
debug: false,
exchange: "coindesk",
isUsingLegacyLnurlAuthKey: false,
legacyLnurlAuth: false,
locale: "",
showFiat: true,
theme: "",
userEmail: "",
userName: "",
websiteEnhancements: true,
};

const mockState = {
settings,
};

describe("Payment notifications", () => {
afterEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -91,24 +122,61 @@ describe("Payment notifications", () => {
};

test("success via lnaddress from popup", async () => {
state.getState = jest.fn().mockReturnValue(mockState);
const notifySpy = jest.spyOn(utils, "notify");
await paymentSuccessNotification("ln.sendPayment.success", data);

expect(notifySpy).toHaveBeenCalledWith({
message: "Amount: 1 sat ($0.00)\nFee: 0 sats",
title: "✅ Successfully paid to »escapedcat@getalby.com«",
});
});

test("success via lnaddress from popup without fiat-conversion turned off", async () => {
const mockStateNoFiat = {
settings: { ...settings, showFiat: false },
};

state.getState = jest.fn().mockReturnValue(mockStateNoFiat);

const notifySpy = jest.spyOn(utils, "notify");
await paymentSuccessNotification("ln.sendPayment.success", data);

expect(notifySpy).toHaveBeenCalledWith({
message: "Amount: 1 sat\nFee: 0 sats",
title: "✅ Successfully paid to »escapedcat@getalby.com«",
});
});

test("success via lnaddress from popup without fiat-conversion turned on", async () => {
const mockStateNoFiat = {
settings: { ...settings },
};

state.getState = jest.fn().mockReturnValue(mockStateNoFiat);

const notifySpy = jest.spyOn(utils, "notify");
paymentSuccessNotification("ln.sendPayment.success", data);
await paymentSuccessNotification("ln.sendPayment.success", data);

expect(notifySpy).toHaveBeenCalledWith({
title: "✅ Successfully paid 1 sat to »escapedcat@getalby.com«",
message: "Fee: 0 sats",
message: "Amount: 1 sat ($0.00)\nFee: 0 sats",
title: "✅ Successfully paid to »escapedcat@getalby.com«",
});
});

test("success without origin skips receiver", async () => {
state.getState = jest.fn().mockReturnValue(mockState);
const notifySpy = jest.spyOn(utils, "notify");
const dataWitouthOrigin = { ...data };
delete dataWitouthOrigin.origin;
paymentSuccessNotification("ln.sendPayment.success", dataWitouthOrigin);
await paymentSuccessNotification(
"ln.sendPayment.success",
dataWitouthOrigin
);

expect(notifySpy).toHaveBeenCalledWith({
title: "✅ Successfully paid 1 sat",
message: "Fee: 0 sats",
message: "Amount: 1 sat ($0.00)\nFee: 0 sats",
title: "✅ Successfully paid",
});
});
});
Expand Down
31 changes: 28 additions & 3 deletions src/extension/background-script/events/notifications.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import utils from "~/common/lib/utils";
import { getFiatValue } from "~/common/utils/currencyConvert";
import { getCurrencyRateFromCache } from "~/extension/background-script/actions/cache/getCurrencyRate";
import state from "~/extension/background-script/state";
import type { PaymentNotificationData, AuthNotificationData } from "~/types";

const paymentSuccessNotification = (
const paymentSuccessNotification = async (
message: "ln.sendPayment.success",
data: PaymentNotificationData
) => {
Expand All @@ -11,6 +14,7 @@ const paymentSuccessNotification = (

const recipient = data?.origin?.name;
const paymentResponseData = data.response;
let paymentAmountFiatLocale;

if ("error" in paymentResponseData) {
return;
Expand All @@ -20,13 +24,34 @@ const paymentSuccessNotification = (
const { total_amt, total_fees } = route;
const paymentAmount = total_amt - total_fees;

let notificationTitle = `✅ Successfully paid ${formatAmount(paymentAmount)}`;
const { settings } = state.getState();
const { showFiat, currency } = settings;

if (showFiat) {
const rate = await getCurrencyRateFromCache(currency);

paymentAmountFiatLocale = await getFiatValue({
amount: paymentAmount,
rate,
currency,
});
}

let notificationTitle = "✅ Successfully paid";

if (recipient) {
notificationTitle = `${notificationTitle} to »${recipient}«`;
}

const notificationMessage = `Fee: ${formatAmount(total_fees)}`;
let notificationMessage = `Amount: ${formatAmount(paymentAmount)}`;

if (showFiat) {
notificationMessage = `${notificationMessage} (${paymentAmountFiatLocale})`;
}

notificationMessage = `${notificationMessage}\nFee: ${formatAmount(
total_fees
)}`;

return utils.notify({
title: notificationTitle,
Expand Down