Skip to content

Commit eccd826

Browse files
authored
feat: allow customize wallets (#97)
* feat: allow adding external/custom wallets * add external enkrypt wallet * fix reject wallet connection behaviour * add error log
1 parent 970541c commit eccd826

File tree

9 files changed

+118
-65
lines changed

9 files changed

+118
-65
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3+
4+
name: Demo Build Tests
5+
6+
on:
7+
push:
8+
workflow_dispatch:
9+
merge_group:
10+
11+
jobs:
12+
unit-tests:
13+
runs-on: ubuntu-latest
14+
15+
strategy:
16+
matrix:
17+
node-version: [18.x]
18+
19+
steps:
20+
- uses: actions/checkout@v3
21+
- name: Use Node.js ${{ matrix.node-version }}
22+
uses: actions/setup-node@v3
23+
with:
24+
node-version: ${{ matrix.node-version }}
25+
cache: 'yarn'
26+
- run: yarn install --immutable
27+
- run: yarn workspace demo build
28+
- run: yarn workspace demo-subconnect build

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ const { connectedAccount, signer } = ... // from subconnect or talisman-connect
194194
- `useWatchContractQuery`: Similar to `useContractQuery` with ability to watch for changes
195195
- `useDeployer`: Create & manage `ContractDeployer` instance given its unique id from the registered contract deployments
196196
- `useDeployerTx`: Similar to `useContractTx`, this hook provides functionality to sign and send transactions to deploy a smart contract, and tracks the progress of the transaction.
197-
- `useWallets`: Access available/installed extension wallets, helpful when building a wallet connector
198197
- `useWatchContractEvent`: Help watch for a specific contract event and perform a specific action
199198
- `usePSP22Balance`: Fetch balance of an address from a PSP22 contract with ability to watch for balance changing
200199

@@ -215,6 +214,7 @@ const {
215214
connectedWallet, // connected wallet
216215
connectWallet, // func to connect to a wallet given its id
217216
disconnect, // func to sign out and disconnect from the wallet
217+
wallets, // available wallets
218218
...
219219
} = useTypink();
220220
// ...

examples/demo/src/components/dialog/WalletSelection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
useDisclosure,
1313
} from '@chakra-ui/react';
1414
import { ThemingProps } from '@chakra-ui/system';
15-
import { useTypink, useWallets, Wallet } from 'typink';
15+
import { useTypink, Wallet } from 'typink';
1616

1717
interface WalletButtonProps {
1818
walletInfo: Wallet;
@@ -63,7 +63,7 @@ export default function WalletSelection({
6363
buttonProps,
6464
}: WalletSelectionProps) {
6565
const { isOpen, onOpen, onClose } = useDisclosure();
66-
const { wallets } = useWallets();
66+
const { wallets } = useTypink();
6767

6868
return (
6969
<>

examples/demo/src/main.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,31 @@ import 'react-toastify/dist/ReactToastify.css';
55
import App from '@/App';
66
import { theme } from '@/theme';
77
import { deployments } from '@/contracts/deployments';
8-
import { alephZeroTestnet, development, popTestnet, TypinkProvider } from 'typink';
8+
import {
9+
alephZeroTestnet,
10+
development,
11+
ExtensionWallet,
12+
polkadotjs,
13+
popTestnet,
14+
subwallet,
15+
talisman,
16+
TypinkProvider,
17+
} from 'typink';
918

1019
const DEFAULT_CALLER = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; // Alice
1120
const SUPPORTED_NETWORK = [popTestnet, alephZeroTestnet];
1221
if (process.env.NODE_ENV === 'development') {
1322
SUPPORTED_NETWORK.push(development);
1423
}
1524

25+
const enkrypt = new ExtensionWallet({
26+
name: 'Enkrypt',
27+
id: 'enkrypt',
28+
logo: 'https://raw.githubusercontent.com/enkryptcom/enKrypt/refs/heads/main/packages/extension/public/assets/img/icons/icon192.png',
29+
installUrl: 'https://www.enkrypt.com',
30+
websiteUrl: 'https://www.enkrypt.com',
31+
});
32+
1633
function Root() {
1734
return (
1835
<ChakraProvider theme={theme}>
@@ -21,7 +38,8 @@ function Root() {
2138
defaultCaller={DEFAULT_CALLER}
2239
supportedNetworks={SUPPORTED_NETWORK}
2340
defaultNetworkId={popTestnet.id}
24-
cacheMetadata={true}>
41+
cacheMetadata={true}
42+
wallets={[subwallet, talisman, polkadotjs, enkrypt]}>
2543
<App />
2644
<ToastContainer
2745
position='top-right'

packages/typink/src/hooks/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,5 @@ export * from './useContractTx.js';
88
export * from './useDeployer.js';
99
export * from './useDeployerTx.js';
1010
export * from './useRawContract.js';
11-
export * from './useWallets.js';
1211
export * from './useWatchContractEvent.js';
1312
export * from './useTypink.js';

packages/typink/src/hooks/useWallets.ts

Lines changed: 0 additions & 40 deletions
This file was deleted.

packages/typink/src/providers/TypinkProvider.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ export function TypinkProvider({
6565
supportedNetworks,
6666
signer,
6767
connectedAccount,
68+
wallets,
6869
}: TypinkProviderProps) {
6970
return (
70-
<WalletSetupProvider signer={signer} connectedAccount={connectedAccount}>
71+
<WalletSetupProvider signer={signer} connectedAccount={connectedAccount} wallets={wallets}>
7172
<ClientProvider
7273
defaultNetworkId={defaultNetworkId}
7374
cacheMetadata={cacheMetadata}

packages/typink/src/providers/WalletSetupProvider.tsx

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
22
import { useLocalStorage } from 'react-use';
3-
import { useIsFirstRender, useWallets } from '../hooks/index.js';
3+
import { useDeepDeps, useIsFirstRender } from '../hooks/index.js';
44
import { InjectedAccount, InjectedSigner } from '../types.js';
5-
import { Wallet } from '../wallets/index.js';
5+
import { polkadotjs, subwallet, talisman, Wallet } from '../wallets/index.js';
66
import { assert } from 'dedot/utils';
77
import { noop } from '../utils/index.js';
88
import { WalletProvider, WalletProviderProps } from './WalletProvider.js';
@@ -14,6 +14,7 @@ export interface WalletSetupContextProps {
1414
disconnect: () => void;
1515
connectedWalletId?: string;
1616
connectedWallet?: Wallet;
17+
wallets: Wallet[]; // custom available wallets
1718

1819
setConnectedAccount: (account: InjectedAccount) => void;
1920
accounts: InjectedAccount[];
@@ -24,13 +25,18 @@ export const WalletSetupContext = createContext<WalletSetupContextProps>({
2425
connectWallet: noop,
2526
disconnect: noop,
2627
setConnectedAccount: noop,
28+
wallets: [],
2729
});
2830

2931
export const useWalletSetup = () => {
3032
return useContext(WalletSetupContext);
3133
};
3234

33-
export interface WalletSetupProviderProps extends WalletProviderProps {}
35+
export interface WalletSetupProviderProps extends WalletProviderProps {
36+
wallets?: Wallet[];
37+
}
38+
39+
const DEFAULT_WALLETS: Wallet[] = [subwallet, talisman, polkadotjs];
3440

3541
/**
3642
* WalletSetupProvider is a component that manages wallet setup and connection state.
@@ -45,8 +51,9 @@ export function WalletSetupProvider({
4551
children,
4652
signer: initialSigner,
4753
connectedAccount: initialConnectedAccount,
54+
wallets: initialWallets,
4855
}: WalletSetupProviderProps) {
49-
const { wallets } = useWallets();
56+
const wallets = useMemo(() => initialWallets || DEFAULT_WALLETS, useDeepDeps([initialWallets]));
5057
const [accounts, setAccounts] = useState<InjectedAccount[]>([]);
5158

5259
const [connectedWalletId, setConnectedWalletId, removeConnectedWalletId] =
@@ -81,28 +88,41 @@ export function WalletSetupProvider({
8188
let unsub: () => void;
8289

8390
(async () => {
84-
const targetWallet: Wallet = getWallet(connectedWalletId);
91+
try {
92+
const targetWallet: Wallet = getWallet(connectedWalletId);
8593

86-
assert(targetWallet, `Wallet Id Not Found ${connectedWalletId}`);
94+
assert(targetWallet, `Wallet Id Not Found ${connectedWalletId}`);
8795

88-
await targetWallet.waitUntilReady();
89-
const injectedProvider = targetWallet.injectedProvider;
96+
await targetWallet.waitUntilReady();
97+
const injectedProvider = targetWallet.injectedProvider;
9098

91-
assert(injectedProvider?.enable, `Invalid Wallet: ${targetWallet.id}`);
99+
assert(injectedProvider?.enable, `Invalid Wallet: ${targetWallet.id}`);
92100

93-
const injected = await injectedProvider.enable('Sample Dapp');
101+
// TODO customize dapp name?
102+
const injected = await injectedProvider.enable('Sample Dapp');
103+
const initialConnectedAccounts = await injected.accounts.get();
94104

95-
// reset accounts on wallet changing
96-
setAccounts([]);
105+
// TODO keep track of wallet decision?
106+
if (initialConnectedAccounts.length === 0) {
107+
removeConnectedWalletId();
108+
return;
109+
}
97110

98-
// only remove the connected account if we're switching to a different wallet
99-
if (!isFirstRender) {
100-
removeConnectedAccount();
101-
}
111+
// reset accounts on wallet changing
112+
setAccounts([]);
102113

103-
unsub = injected.accounts.subscribe(setAccounts);
114+
// only remove the connected account if we're switching to a different wallet
115+
if (!isFirstRender) {
116+
removeConnectedAccount();
117+
}
104118

105-
setSigner(injected.signer as any);
119+
unsub = injected.accounts.subscribe(setAccounts);
120+
121+
setSigner(injected.signer as any);
122+
} catch (e) {
123+
console.error('Error while connecting wallet:', e);
124+
removeConnectedWalletId();
125+
}
106126
})();
107127

108128
return () => unsub && unsub();
@@ -128,6 +148,7 @@ export function WalletSetupProvider({
128148
connectedWalletId,
129149
connectedWallet,
130150
setConnectedAccount,
151+
wallets,
131152
}}>
132153
<WalletProvider signer={signer} connectedAccount={connectedAccount}>
133154
{children}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,28 @@
1+
import { ExtensionWallet } from './ExtensionWallet.js';
2+
13
export * from './Wallet.js';
24
export * from './ExtensionWallet.js';
5+
6+
export const subwallet = new ExtensionWallet({
7+
name: 'SubWallet',
8+
id: 'subwallet-js',
9+
logo: 'https://raw.githubusercontent.com/dedotdev/typink/refs/heads/main/assets/wallets/subwallet-logo.svg',
10+
installUrl: 'https://www.subwallet.app/download.html',
11+
websiteUrl: 'https://www.subwallet.app',
12+
});
13+
14+
export const talisman = new ExtensionWallet({
15+
name: 'Talisman',
16+
id: 'talisman',
17+
logo: 'https://raw.githubusercontent.com/dedotdev/typink/refs/heads/main/assets/wallets/talisman-logo.svg',
18+
installUrl: 'https://talisman.xyz/download',
19+
websiteUrl: 'https://talisman.xyz',
20+
});
21+
22+
export const polkadotjs = new ExtensionWallet({
23+
name: 'Polkadot{.js}',
24+
id: 'polkadot-js',
25+
logo: 'https://raw.githubusercontent.com/dedotdev/typink/refs/heads/main/assets/wallets/polkadot-js-logo.svg',
26+
installUrl: 'https://polkadot.js.org/extension',
27+
websiteUrl: 'https://polkadot.js.org',
28+
});

0 commit comments

Comments
 (0)