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

Create a loan backup file when confirming #226

Merged
merged 6 commits into from
Aug 12, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
14 changes: 14 additions & 0 deletions e2e_tests/src/borrow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ describe("borrow test", () => {
debug("Signing loan");
let signLoanButton = await getElementById(driver, "//button[@data-cy='data-cy-sign-loan-button']", 20_000);
await signLoanButton.click();
debug("Download loan backup");
let downloadLoanButton = await getElementById(
driver,
"//button[@data-cy='data-cy-download-loan-button']",
20_000,
);
await downloadLoanButton.click();
debug("Confirming the backup");
let confirmLoanButton = await getElementById(
driver,
"//button[@data-cy='data-cy-confirm-loan-button']",
20_000,
);
await confirmLoanButton.click();

await switchToWindow(driver, webAppTitle);

Expand Down
2 changes: 2 additions & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,15 @@
"@types/react": "^16.9.0",
"@types/react-dom": "^16.9.0",
"@types/uuid": "^8.3.1",
"chakra-ui-steps": "^1.3.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

noice! I also found that one and wanted to use it at some point :D

"debug": "^4.3.1",
"framer-motion": "^4.0.0",
"moment": "^2.29.1",
"react": "^17.0.2",
"react-async": "^10.0.1",
"react-browser-extension-scripts": "4.0.10",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-qr-code": "^1.1.1",
"react-scripts": "4.0.1",
"type-fest": "^2.0.0",
Expand Down
4 changes: 2 additions & 2 deletions extension/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { browser } from "webextension-polyfill-ts";
import { getBalances, getLoanToSign, getOpenLoans, getSwapToSign, getWalletStatus } from "./background-proxy";
import AddressQr from "./components/AddressQr";
import WalletBalances from "./components/Balances";
import ConfirmLoan from "./components/ConfirmLoan";
import ConfirmLoanWizard from "./components/ConfirmLoanWizard";
import ConfirmSwap from "./components/ConfirmSwap";
import CreateWallet from "./components/CreateWallet";
import OpenLoans from "./components/OpenLoans";
Expand Down Expand Up @@ -74,7 +74,7 @@ const App = () => {
swapToSign={swapToSign!}
/>}
{signLoan
&& <ConfirmLoan
&& <ConfirmLoanWizard
onCancel={refreshAll}
onSuccess={refreshAll}
loanToSign={loanToSign!}
Expand Down
28 changes: 26 additions & 2 deletions extension/src/background-proxy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { browser } from "webextension-polyfill-ts";
import { Address, BalanceUpdate, LoanDetails, LoanToSign, SwapToSign, Txid, WalletStatus } from "./models";
import {
Address,
BackupDetails,
BalanceUpdate,
LoanDetails,
LoanToSign,
SwapToSign,
Txid,
WalletStatus,
} from "./models";

const proxy = browser.extension.getBackgroundPage();

Expand All @@ -13,11 +22,26 @@ export async function signAndSendSwap(txHex: string): Promise<void> {
return proxy.signAndSendSwap(txHex);
}

export async function signLoan(): Promise<void> {
export async function signLoan(): Promise<string> {
// @ts-ignore
return proxy.signLoan();
}

export async function confirmLoan(payload: string): Promise<void> {
// @ts-ignore
return proxy.confirmLoan(payload);
}

export async function createLoanBackup(loanTx: string): Promise<string> {
// @ts-ignore
return proxy.createLoanBackup(loanTx);
}

export async function loadLoanBackup(backupDetails: BackupDetails): Promise<void> {
// @ts-ignore
return proxy.loadLoanBackup(backupDetails);
}

export async function getLoanToSign(): Promise<LoanToSign | undefined> {
// @ts-ignore
return proxy.getLoanToSign();
Expand Down
33 changes: 28 additions & 5 deletions extension/src/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import Debug from "debug";
import { browser } from "webextension-polyfill-ts";
import WavesProvider from "../in-page";
import { LoanDetails, LoanToSign, SwapToSign, Txid } from "../models";
import { BackupDetails, LoanDetails, LoanToSign, SwapToSign, Txid } from "../models";
import {
bip39SeedWords,
createLoanBackup,
createNewBip39Wallet,
extractLoan,
extractTrade,
Expand All @@ -12,6 +13,7 @@ import {
getBlockHeight,
getOpenLoans,
getPastTransactions,
loadLoanBackup,
makeBuyCreateSwapPayload,
makeLoanRequestPayload,
makeSellCreateSwapPayload,
Expand Down Expand Up @@ -171,11 +173,32 @@ window.signLoan = async () => {
// on the pop-up matches what is stored in the extension's
// storage. It would be better to send around the swap ID to check
// that the wallet is signing the same transaction the user has authorised
signLoan(walletName)
.then(resolveLoanSignRequest)
.catch(rejectLoanSignRequest)
.then(cleanupPendingLoan);

// if we receive an error, we respond directly, else we return the details
return await signLoan(walletName).catch(rejectLoanSignRequest);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return await is unnecessary, you can just remove the await and return the promise directly.

};

// @ts-ignore
window.confirmLoan = async (payload: string) => {
if (!resolveLoanSignRequest || !rejectLoanSignRequest) {
throw new Error("No pending promise functions for loan sign request");
}
// once sent to the page, we assume the business is done.
// TODO: a feedback loop is required where the wallet gets told if bobtimus successfully published the transaction
resolveLoanSignRequest(payload);
await cleanupPendingLoan();
};

// @ts-ignore
window.createLoanBackup = async (loanTx: string) => {
return createLoanBackup(walletName, loanTx);
};

// @ts-ignore
window.loadLoanBackup = async (backupDetails: BackupDetails) => {
return loadLoanBackup(backupDetails);
};

// @ts-ignore
window.rejectLoan = () => {
if (!resolveLoanSignRequest || !rejectLoanSignRequest) {
Expand Down
9 changes: 5 additions & 4 deletions extension/src/components/ConfirmLoan.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ const debug = Debug("confirmloan:error");

interface ConfirmLoanProps {
onCancel: () => void;
onSuccess: () => void;
onSigned: (signedTransaction: string) => void;
loanToSign: LoanToSign;
}

export default function ConfirmLoan(
{ onCancel, onSuccess, loanToSign }: ConfirmLoanProps,
{ onCancel, onSigned, loanToSign }: ConfirmLoanProps,
) {
let { isPending, run } = useAsync({
deferFn: async () => {
await signLoan();
onSuccess();
let signedTransaction = await signLoan();
debug("Signed loan: %s", signedTransaction);
onSigned(signedTransaction);
},
});

Expand Down
122 changes: 122 additions & 0 deletions extension/src/components/ConfirmLoanWizard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button, Flex, VStack } from "@chakra-ui/react";
import { Step, Steps, useSteps } from "chakra-ui-steps";
import * as React from "react";
import { useState } from "react";
import { FiCheck, FiClipboard, FiExternalLink } from "react-icons/all";
import { confirmLoan, createLoanBackup } from "../background-proxy";
import { LoanToSign } from "../models";
import ConfirmLoan from "./ConfirmLoan";

interface ConfirmLoanWizardProps {
onCancel: () => void;
onSuccess: () => void;
loanToSign: LoanToSign;
}

const ConfirmLoanWizard = ({ onCancel, onSuccess, loanToSign }: ConfirmLoanWizardProps) => {
const { nextStep, activeStep } = useSteps({
initialStep: 0,
});
let [signedTransaction, setSignedTransaction] = useState("");

const onSigned = (tx: string) => {
setSignedTransaction(tx);
nextStep();
};

const downloadLoanBackup = async () => {
const loanBackup = await createLoanBackup(signedTransaction);
const file = new Blob([JSON.stringify(loanBackup)], { type: "text/json" });
const url = URL.createObjectURL(file);
// Note: it would be nicer to open a dialog to download the file. However, we would lose focus
// of the window. We don't want that, hence we just open a new tab with the content
window.open(url, "_blank");
nextStep();
};

const onPublish = async () => {
await confirmLoan(signedTransaction);
onSuccess();
};

return (
<VStack width="100%">
<Steps activeStep={activeStep}>
<Step label={"Sign"} key={"sign"} icon={FiClipboard}>
<Flex py={4}>
<ConfirmLoan
loanToSign={loanToSign}
onCancel={onCancel}
onSigned={onSigned}
/>
</Flex>
</Step>
<Step label={"Backup"} key={"backup"} icon={FiExternalLink}>
<Flex py={4}>
<VStack>
<Alert
status="warning"
variant="subtle"
flexDirection="column"
alignItems="center"
justifyContent="center"
textAlign="center"
height="250px"
>
<AlertIcon boxSize="40px" mr={0} />
<AlertTitle mt={4} mb={1} fontSize="lg">
Create a backup!
</AlertTitle>
<AlertDescription>
Click below to download a backup of the loan details. Together with your seed words
you can recover your loan details in case your browser storage gets purged. Keep it
safe! You can restore your backup through the settings.
</AlertDescription>
</Alert>
<Button
onClick={async () => {
await downloadLoanBackup();
}}
data-cy="data-cy-download-loan-button"
>
Download backup
</Button>
</VStack>
</Flex>
</Step>

<Step label={"Confirm"} key={"confirm"} icon={FiCheck}>
<Flex py={4}>
<VStack>
<Alert
status="info"
variant="subtle"
flexDirection="column"
alignItems="center"
justifyContent="center"
textAlign="center"
height="200px"
>
<AlertIcon boxSize="40px" mr={0} />
<AlertTitle mt={4} mb={1} fontSize="lg">
Backup saved?
</AlertTitle>
<AlertDescription>
The lender will publish the transaction once signed.
</AlertDescription>
</Alert>
<Button
onClick={() => onPublish()}
data-cy="data-cy-confirm-loan-button"
>
Confirm
</Button>
</VStack>
</Flex>
</Step>
</Steps>
</VStack>
);
};

export default ConfirmLoanWizard;
5 changes: 5 additions & 0 deletions extension/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,8 @@ export interface OutPoint {
txid: string;
vout: number;
}

export interface BackupDetails {
loan_details: LoanDetails;
borrower: any; // we don't really care about the type here
}
2 changes: 2 additions & 0 deletions extension/src/theme.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { extendTheme } from "@chakra-ui/react";
import { StepsStyleConfig as Steps } from "chakra-ui-steps";

const theme = extendTheme({
textStyles: {
Expand Down Expand Up @@ -35,6 +36,7 @@ const theme = extendTheme({
},
},
},
Steps,
},
});

Expand Down
16 changes: 16 additions & 0 deletions extension/src/wasmProxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Debug from "debug";
import {
Address,
BackupDetails,
BalanceUpdate,
CreateSwapPayload,
LoanDetails,
Expand Down Expand Up @@ -108,6 +109,21 @@ export async function signLoan(name: string): Promise<string> {
return (await sign_loan(name)).inner;
}

export async function createLoanBackup(name: string, loanTx: string): Promise<BackupDetails> {
const { create_loan_backup } = await import("./wallet");

debug("createLoanBackup");
const tx = { inner: loanTx };
return create_loan_backup(name, tx);
}

export async function loadLoanBackup(backupDetails: BackupDetails): Promise<void> {
const { load_loan_backup } = await import("./wallet");

debug("loadLoanBackup");
return load_loan_backup(backupDetails);
}

export async function withdrawAll(name: string, address: string): Promise<Txid> {
const { withdraw_everything_to } = await import("./wallet");

Expand Down
Loading