Skip to content

Commit 6307bab

Browse files
committed
feat: multiple wallet selection options
1 parent ea640bf commit 6307bab

13 files changed

+314
-107
lines changed

playwright.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ export default defineConfig({
2222
ignoreHTTPSErrors: true,
2323

2424
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
25-
headless: true,
2625
trace: "on-first-retry",
26+
27+
headless: true,
2728
},
2829

2930
/* Configure projects for major browsers */

src/components/ConnectMetamask.tsx

+145-41
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,167 @@
1-
import { Show, createEffect, createSignal } from "solid-js";
1+
import log from "loglevel";
2+
import { IoClose } from "solid-icons/io";
3+
import {
4+
Accessor,
5+
For,
6+
Setter,
7+
Show,
8+
createEffect,
9+
createMemo,
10+
createSignal,
11+
} from "solid-js";
212

313
import { useCreateContext } from "../context/Create";
414
import { useGlobalContext } from "../context/Global";
5-
import { useWeb3Signer } from "../context/Web3";
15+
import { EIP6963ProviderInfo, useWeb3Signer } from "../context/Web3";
16+
import "../style/web3.scss";
17+
import { formatError } from "../utils/errors";
618

7-
const ConnectMetamask = ({ showAddress }) => {
8-
const [address, setAddress] = createSignal<string | undefined>();
9-
const [buttonText, setButtonText] = createSignal<string | undefined>();
19+
const connect = async (
20+
notify: (type: string, message: string) => void,
21+
connectProvider: (rdns: string) => Promise<void>,
22+
provider: EIP6963ProviderInfo,
23+
) => {
24+
try {
25+
await connectProvider(provider.rdns);
26+
} catch (e) {
27+
log.error(
28+
`Provider connect to ${provider.rdns} failed: ${formatError(e)}`,
29+
);
30+
notify("error", `Wallet connection failed: ${formatError(e)}`);
31+
}
32+
};
1033

11-
const { t } = useGlobalContext();
12-
const { setAddressValid, setOnchainAddress } = useCreateContext();
34+
const Modal = ({
35+
show,
36+
setShow,
37+
}: {
38+
show: Accessor<boolean>;
39+
setShow: Setter<boolean>;
40+
}) => {
41+
const { t, notify } = useGlobalContext();
42+
const { providers, connectProvider } = useWeb3Signer();
1343

14-
const setButtonTextAddress = () => {
15-
setButtonText(address() || t("connect_to_address"));
44+
const Provider = ({ provider }: { provider: EIP6963ProviderInfo }) => {
45+
return (
46+
<div
47+
class="provider-modal-entry-wrapper"
48+
onClick={() => connect(notify, connectProvider, provider)}>
49+
<hr />
50+
<div class="provider-modal-entry">
51+
<img
52+
class="provider-modal-icon"
53+
src={provider.icon}
54+
alt={`${provider.name} icon`}
55+
/>
56+
<h4>{provider.name}</h4>
57+
</div>
58+
</div>
59+
);
1660
};
1761

18-
createEffect(() => {
19-
setButtonTextAddress();
20-
});
62+
return (
63+
<div
64+
id="settings-menu"
65+
class="frame assets-select"
66+
onClick={() => setShow(false)}
67+
style={show() ? "display: block;" : "display: none;"}>
68+
<div onClick={(e) => e.stopPropagation()}>
69+
<h2>{t("select_wallet")}</h2>
70+
<span class="close" onClick={() => setShow(false)}>
71+
<IoClose />
72+
</span>
73+
<hr class="spacer" />
74+
<For
75+
each={Object.values(providers()).sort((a, b) =>
76+
a.info.name
77+
.toLowerCase()
78+
.localeCompare(b.info.name.toLowerCase()),
79+
)}>
80+
{(item) => <Provider provider={item.info} />}
81+
</For>
82+
</div>
83+
</div>
84+
);
85+
};
86+
87+
const ConnectModal = () => {
88+
const { t, notify } = useGlobalContext();
89+
const { providers, connectProvider } = useWeb3Signer();
90+
91+
const [show, setShow] = createSignal<boolean>(false);
92+
93+
return (
94+
<>
95+
<button
96+
id="metamask"
97+
class="btn"
98+
onClick={() => {
99+
if (Object.keys(providers()).length > 1) {
100+
setShow(true);
101+
} else {
102+
// Do not show the modal when there is only one option to select
103+
connect(
104+
notify,
105+
connectProvider,
106+
Object.values(providers())[0].info,
107+
).then();
108+
}
109+
}}>
110+
{t("connect_metamask")}
111+
</button>
112+
<Modal show={show} setShow={setShow} />
113+
</>
114+
);
115+
};
116+
117+
const ShowAddress = ({
118+
address,
119+
}: {
120+
address: Accessor<string | undefined>;
121+
}) => {
122+
const { t } = useGlobalContext();
123+
const { clearSigner } = useWeb3Signer();
124+
125+
const [text, setText] = createSignal<string>(address());
126+
127+
return (
128+
<button
129+
onClick={() => clearSigner()}
130+
onMouseEnter={() => setText(t("disconnect_address"))}
131+
onMouseLeave={() => setText(address())}
132+
class="btn btn-light">
133+
{text()}
134+
</button>
135+
);
136+
};
137+
138+
const ConnectMetamask = () => {
139+
const { t } = useGlobalContext();
140+
const { providers, signer } = useWeb3Signer();
141+
const { setAddressValid, setOnchainAddress } = useCreateContext();
142+
143+
const address = createMemo(() => signer()?.address);
21144

22145
createEffect(() => {
23146
const addr = address();
24147
setAddressValid(addr !== undefined);
25148
setOnchainAddress(addr || "");
26149
});
27150

28-
const { getSigner, hasMetamask } = useWeb3Signer();
29-
30151
return (
31152
<>
32-
<Show when={hasMetamask()}>
33-
<Show when={address() === undefined}>
34-
<button
35-
id="metamask"
36-
class="btn"
37-
onClick={async () =>
38-
setAddress(await (await getSigner()).getAddress())
39-
}>
40-
{t("connect_metamask")}
153+
<Show
154+
when={Object.keys(providers()).length > 0}
155+
fallback={
156+
<button class="btn" disabled>
157+
{t("no_metamask")}
41158
</button>
159+
}>
160+
<Show
161+
when={address() !== undefined}
162+
fallback={<ConnectModal />}>
163+
<ShowAddress address={address} />
42164
</Show>
43-
<Show when={address() !== undefined}>
44-
<Show when={showAddress}>
45-
<button
46-
onClick={() => setAddress(undefined)}
47-
onMouseEnter={() =>
48-
setButtonText(t("disconnect_address"))
49-
}
50-
onMouseLeave={() => setButtonTextAddress()}
51-
class="btn btn-light">
52-
{buttonText()}
53-
</button>
54-
</Show>
55-
</Show>
56-
</Show>
57-
<Show when={!hasMetamask()}>
58-
<button class="btn" disabled>
59-
{t("no_metamask")}
60-
</button>
61165
</Show>
62166
</>
63167
);

src/components/CreateButton.tsx

+6-5
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export const CreateButton = () => {
5959
invoiceValid,
6060
invoiceError,
6161
} = useCreateContext();
62-
const { getEtherSwap, getSigner } = useWeb3Signer();
62+
const { getEtherSwap, signer } = useWeb3Signer();
6363

6464
const [buttonDisable, setButtonDisable] = createSignal(false);
6565
const [buttonClass, setButtonClass] = createSignal("btn");
@@ -165,18 +165,19 @@ export const CreateButton = () => {
165165
let claimAddress = onchainAddress();
166166

167167
if (assetReceive() === RBTC) {
168-
const signer = await getSigner();
169168
const [balance, gasPrice] = await Promise.all([
170-
signer.provider.getBalance(await signer.getAddress()),
171-
signer.provider.getFeeData().then((data) => data.gasPrice),
169+
signer().provider.getBalance(await signer().getAddress()),
170+
signer()
171+
.provider.getFeeData()
172+
.then((data) => data.gasPrice),
172173
]);
173174
log.debug("RSK balance", balance);
174175

175176
const balanceNeeded = gasPrice * GasNeededToClaim;
176177
log.debug("RSK balance needed", balanceNeeded);
177178

178179
if (balance <= balanceNeeded) {
179-
claimAddress = (await getSmartWalletAddress(signer)).address;
180+
claimAddress = (await getSmartWalletAddress(signer())).address;
180181
log.info("Using RIF smart wallet as claim address");
181182
}
182183
}

src/components/LockupEvm.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,16 @@ const LockupEvm = ({
3131
timeoutBlockHeight: number;
3232
amount: number;
3333
}) => {
34-
const { getEtherSwap, getSigner } = useWeb3Signer();
34+
const { getEtherSwap, signer } = useWeb3Signer();
3535
const { t, getSwap, setSwapStorage } = useGlobalContext();
3636

3737
const value = () => satoshiToWei(amount);
3838

3939
const getSignerBalance = async () => {
40-
const signer = await getSigner();
41-
return signer.provider.getBalance(await signer.getAddress());
40+
return signer().provider.getBalance(await signer().getAddress());
4241
};
4342

44-
const [signerBalance] = createResource(getSigner, getSignerBalance);
43+
const [signerBalance] = createResource(signer(), getSignerBalance);
4544

4645
return (
4746
<Show

src/components/RefundButton.tsx

+3-4
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,22 @@ const RefundEvm = ({
3737
timeoutBlockHeight: number;
3838
}) => {
3939
const { setSwap } = usePayContext();
40-
const { getEtherSwap, getSigner } = useWeb3Signer();
40+
const { getEtherSwap, signer } = useWeb3Signer();
4141
const { setSwapStorage, getSwap, t } = useGlobalContext();
4242

4343
return (
4444
<ContractTransaction
4545
onClick={async () => {
46-
const [contract, signer, currentSwap] = await Promise.all([
46+
const [contract, currentSwap] = await Promise.all([
4747
getEtherSwap(),
48-
getSigner(),
4948
getSwap(swapId),
5049
]);
5150

5251
let tx: TransactionResponse;
5352

5453
if (
5554
timeoutBlockHeight <
56-
(await signer.provider.getBlockNumber())
55+
(await signer().provider.getBlockNumber())
5756
) {
5857
tx = await contract.refund(
5958
prefix0x(preimageHash),

0 commit comments

Comments
 (0)