Skip to content

Commit

Permalink
feat(browser-wallets): migrate to react for browser-tests (#91)
Browse files Browse the repository at this point in the history
feat(browser-wallets): migrate to react for test
  • Loading branch information
Nanosync authored Dec 6, 2023
1 parent d990818 commit 2e584ed
Show file tree
Hide file tree
Showing 12 changed files with 1,458 additions and 272 deletions.
5 changes: 5 additions & 0 deletions apps/browser-tests/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
const path = require("path");

module.exports = {
root: true,
extends: ["./node_modules/@ordzaar/standard-web-linter"],
parserOptions: { project: [path.join(__dirname, "tsconfig.json")] },
rules: {
// Allow better IDE import path resolution
"import/prefer-default-export": "off",
"no-console": "off",
"jsx-a11y/label-has-associated-control": "off",
},
};
24 changes: 2 additions & 22 deletions apps/browser-tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,7 @@
<title>Ordit SDK Playground</title>
</head>
<body>
<h1>Connect to Wallet Provider</h1>
<div>
<button id="unisat-connect" type="button">Connect to Unisat</button>
<button id="xverse-connect" type="button">Connect to Xverse</button>
</div>
<h1>Unisat Transaction Tests</h1>
<div>
<button id="unisat-create-psbt" type="button">
Create and Prepare PSBT
</button>
<button id="unisat-sign-psbt" type="button">Sign PSBT</button>
<button id="unisat-sign-message" type="button">Sign Message</button>
</div>
<h1>Xverse Transaction Tests</h1>
<div>
<button id="xverse-create-psbt" type="button">
Create and Prepare PSBT
</button>
<button id="xverse-sign-psbt" type="button">Sign PSBT</button>
<button id="xverse-sign-message" type="button">Sign Message</button>
</div>
<script type="module" src="/src/main.ts"></script>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
12 changes: 10 additions & 2 deletions apps/browser-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@
"lint": "eslint src --ext ts --report-unused-disable-directives"
},
"dependencies": {
"@ordzaar/ordit-sdk": "workspace:*"
"@ordzaar/ordit-sdk": "workspace:*",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@ordzaar/standard-typescript": "^0.4.3",
"@ordzaar/standard-web": "^0.4.6",
"@ordzaar/standard-web-linter": "^0.4.6",
"@types/react": "^18.2.39",
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.4",
"vite-plugin-node-polyfills": "^0.16.0"
}
}
300 changes: 300 additions & 0 deletions apps/browser-tests/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
import { useCallback, useMemo, useState } from "react";
import { Address, PSBTBuilder, PSBTBuilderOptions } from "@ordzaar/ordit-sdk";
import * as unisat from "@ordzaar/ordit-sdk/unisat";
import * as xverse from "@ordzaar/ordit-sdk/xverse";

import { RadioInput } from "./components/RadioInput";
import { Select } from "./components/Select";

type WalletProvider = "unisat" | "xverse";

async function createAndPreparePsbt(psbtParams: PSBTBuilderOptions) {
const psbt = new PSBTBuilder(psbtParams);
console.log("Initial Psbt: ", psbt);

// Duplicate the PSBT so that console log shows a different instance.
const clonedPSBT = new PSBTBuilder(psbtParams);
await clonedPSBT.prepare();
console.log("Prepared Psbt: ", clonedPSBT);
return clonedPSBT;
}

function Transactions({
provider,
connectedAddresses,
}: {
provider: WalletProvider;
connectedAddresses: Address[];
}) {
const [inputAddressInfo, setInputAddress] = useState(connectedAddresses[0]);
const inputAddressesSelectOptions = useMemo(
() =>
connectedAddresses.map((addr) => ({
name: addr.address,
value: addr.address,
})),
[connectedAddresses],
);

const [outputAddress, setOutputAddress] = useState(
"tb1qatkgzm0hsk83ysqja5nq8ecdmtwl73zwurawww",
);
const [feeRate, setFeeRate] = useState(1);
const [amount, setAmount] = useState(600);
const [error, setError] = useState<string | undefined>();

const psbtParams = useMemo(
() => ({
address: inputAddressInfo.address,
feeRate,
publicKey: inputAddressInfo.publicKey,
outputs: [
{
address: outputAddress,
value: amount,
},
],
network: "testnet" as const,
}),
[
amount,
feeRate,
inputAddressInfo.address,
inputAddressInfo.publicKey,
outputAddress,
],
);

const handleCreateAndPreparePsbt = useCallback(async () => {
setError(undefined);

try {
const psbt = await createAndPreparePsbt(psbtParams);
return psbt;
} catch (e) {
setError((e as Error).message);
throw e;
}
}, [psbtParams]);

const handleSignPsbt = useCallback(async () => {
setError(undefined);

try {
const psbt = await createAndPreparePsbt(psbtParams);
let signPsbtResponse;
if (provider === "unisat") {
signPsbtResponse = await unisat.signPsbt(psbt.toPSBT());
} else if (provider === "xverse") {
signPsbtResponse = await xverse.signPsbt(psbt.toPSBT(), {
network: "testnet",
inputsToSign: [
{
address: inputAddressInfo.address,
signingIndexes: [0],
},
],
});
} else {
throw new Error("Unknown provider");
}
console.log("Sign PSBT Response", signPsbtResponse);
} catch (e) {
setError((e as Error).message);
throw e;
}
}, [psbtParams, provider, inputAddressInfo.address]);

const handleSignMessage = useCallback(async () => {
setError(undefined);

try {
let signMessageResponse;
const message =
"This is a test message and will not be sent to the network.";
if (provider === "unisat") {
signMessageResponse = await unisat.signMessage(message);
} else if (provider === "xverse") {
signMessageResponse = await xverse.signMessage(
message,
inputAddressInfo.address,
"testnet",
);
} else {
throw new Error("Unknown provider");
}
console.log("Sign Message Response", signMessageResponse);
} catch (e) {
setError((e as Error).message);
throw e;
}
}, [inputAddressInfo.address, provider]);

return (
<div
className="flex flex-col"
style={{ maxWidth: "600px", gap: "4px", width: "100%" }}
>
<h1>Transactions</h1>
<label className="flex" htmlFor="inputAddress">
Input Address
</label>
<Select
options={inputAddressesSelectOptions}
name="inputAddress"
id="inputAddress"
defaultValue={inputAddressesSelectOptions[0].value}
onChange={(newInputAddress) =>
setInputAddress(
connectedAddresses.find(
(connectedAddr) => connectedAddr.address === newInputAddress,
)!,
)
}
/>
<label className="flex" htmlFor="feeRate">
Fee Rate (sats/vB)
</label>
<input
type="text"
id="feeRate"
name="feeRate"
value={feeRate}
onChange={(e) => setFeeRate(Number(e.target.value))}
/>
<label className="flex" htmlFor="amount">
Transfer Amount (sats) (Excluding network fee)
</label>
<input
type="text"
id="amount"
name="amount"
value={amount}
onChange={(e) => setAmount(Number(e.target.value))}
/>
<label className="flex" htmlFor="outputAddress">
Output Address
</label>
<input
type="text"
id="outputAddress"
name="outputAddress"
value={outputAddress}
onChange={(e) => setOutputAddress(e.target.value)}
/>
<div className="flex" style={{ marginTop: "12px", gap: "4px" }}>
<button
type="button"
onClick={handleCreateAndPreparePsbt}
disabled={!connectedAddresses || !outputAddress}
>
Create and Prepare PSBT
</button>
<button
type="button"
onClick={handleSignPsbt}
disabled={!connectedAddresses || !outputAddress}
>
Sign PSBT
</button>
<button
type="button"
onClick={handleSignMessage}
disabled={!connectedAddresses || !outputAddress}
>
Sign Message
</button>
</div>
<p>{error ? `Error: ${error}` : null}</p>
</div>
);
}

function App() {
const [provider, setProvider] = useState<WalletProvider>("unisat");
const [connectedAddresses, setConnectedAddresses] = useState<
Address[] | undefined
>();

const handleConnect = useCallback(async () => {
if (provider === "unisat") {
const addresses = await unisat.getAddresses("testnet");
console.log("Unisat Connected: ", addresses);
setConnectedAddresses(addresses);
} else if (provider === "xverse") {
const addresses = await xverse.getAddresses("testnet");
setConnectedAddresses(addresses);
console.log("Xverse Connected: ", addresses);
} else {
console.log("Unknown provider", provider);
}
}, [provider]);

const handleDisconnect = useCallback(async () => {
setConnectedAddresses(undefined);
}, []);

return (
<div>
<p>Connect wallet to run tests.</p>
<h1>Select Provider</h1>
<RadioInput
name="provider"
onChange={(option) =>
provider !== option.value
? setProvider(option.value as WalletProvider)
: undefined
}
options={[
{ name: "Unisat", value: "unisat" },
{ name: "Xverse", value: "xverse" },
]}
value={provider}
disabled={!!connectedAddresses}
/>
<p>Network: Testnet</p>
<button
type="button"
style={{ marginTop: "12px" }}
onClick={handleConnect}
disabled={!!connectedAddresses}
>
Connect
</button>
<button
type="button"
style={{ marginTop: "12px" }}
onClick={handleDisconnect}
disabled={!connectedAddresses}
>
Disconnect
</button>
{connectedAddresses && connectedAddresses.length > 0 ? (
<>
<div style={{ marginTop: "12px", maxWidth: "800px" }}>
<h1>Connected Wallet Info</h1>
{connectedAddresses.map((addressInfo) => (
<div
key={addressInfo.address}
style={{
padding: "8px",
border: "1px solid black",
}}
>
<p>Address: {addressInfo.address}</p>
<p>Format: {addressInfo.format}</p>
<p>Public Key: {addressInfo.publicKey}</p>
</div>
))}
</div>
<Transactions
provider={provider}
connectedAddresses={connectedAddresses}
/>
</>
) : null}
</div>
);
}

export default App;
Loading

0 comments on commit 2e584ed

Please sign in to comment.