Skip to content

Commit 54a8aa1

Browse files
Merge pull request #115 from Web3Auth/feat/wc-adapters
Add Rainbow & coinbase wallet adapters
2 parents 62bd27a + f8f2923 commit 54a8aa1

File tree

16 files changed

+293
-63
lines changed

16 files changed

+293
-63
lines changed

examples/vue-app/package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/vue-app/src/chains/ethereum.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,12 @@ export default Vue.extend({
113113
try {
114114
this.parseConfig();
115115
this.loading = true;
116-
this.web3auth = new Web3Auth({ chainConfig: ethChainConfig, clientId: config.clientId, authMode: "DAPP", enableLogging: true });
116+
this.web3auth = new Web3Auth({
117+
chainConfig: ethChainConfig,
118+
clientId: config.clientId,
119+
authMode: "DAPP",
120+
enableLogging: true,
121+
});
117122
const openloginAdapter = new OpenloginAdapter({
118123
adapterSettings: {
119124
network: this.openloginNetwork as OPENLOGIN_NETWORK_TYPE,

examples/vue-app/vue.config.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,13 @@ module.exports = {
2929
},
3030
chainWebpack: (config) => {
3131
if (process.env.NODE_ENV !== "production") {
32-
config.module.rule("sourcemap").test(/\.js$/).enforce("pre").use("source-map-loader").loader("source-map-loader").end();
32+
config.module
33+
.rule("sourcemap")
34+
.test(/\.${js,ts}$/)
35+
.enforce("pre")
36+
.use("source-map-loader")
37+
.loader("source-map-loader")
38+
.end();
3339
}
3440
const svgRule = config.module.rule("svg");
3541

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { CHAIN_NAMESPACES, IWalletConnectExtensionAdapter } from "@web3auth/base";
2+
3+
export const WALLET_CONNECT_EXTENSION_ADAPTERS: IWalletConnectExtensionAdapter[] = [
4+
{
5+
name: "Rainbow",
6+
chains: [CHAIN_NAMESPACES.EIP155],
7+
logo: "https://images.web3auth.io/login-rainbow.svg",
8+
mobile: {
9+
native: "rainbow:",
10+
universal: "https://rnbwapp.com",
11+
},
12+
desktop: {
13+
native: "",
14+
universal: "",
15+
},
16+
},
17+
];

packages/adapters/wallet-connect-v1-adapter/src/walletConnectV1adapter.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
CONNECTED_EVENT_DATA,
1414
CustomChainConfig,
1515
getChainConfig,
16-
isHexStrict,
1716
log,
1817
SafeEventEmitterProvider,
1918
UserInfo,
@@ -25,6 +24,7 @@ import {
2524
} from "@web3auth/base";
2625
import { WalletConnectProvider } from "@web3auth/ethereum-provider";
2726

27+
import { WALLET_CONNECT_EXTENSION_ADAPTERS } from "./config";
2828
import { WalletConnectV1AdapterOptions } from "./interface";
2929

3030
class WalletConnectV1Adapter extends BaseAdapter<void> {
@@ -42,6 +42,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
4242

4343
public adapterData: WalletConnectV1Data = {
4444
uri: "",
45+
extensionAdapters: WALLET_CONNECT_EXTENSION_ADAPTERS,
4546
};
4647

4748
public connector: WalletConnect | null = null;
@@ -75,13 +76,13 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
7576
}
7677
// Create a connector
7778
this.connector = this.getWalletConnectInstance();
78-
this.wcProvider = new WalletConnectProvider({ config: { chainConfig: this.chainConfig as CustomChainConfig } });
79+
this.wcProvider = new WalletConnectProvider({ config: { chainConfig: this.chainConfig as CustomChainConfig }, connector: this.connector });
7980

8081
this.emit(ADAPTER_EVENTS.READY, WALLET_ADAPTERS.WALLET_CONNECT_V1);
8182
this.status = ADAPTER_STATUS.READY;
8283
if (this.connector.connected) {
8384
this.rehydrated = true;
84-
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId.toString() });
85+
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId });
8586
}
8687
}
8788

@@ -90,7 +91,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
9091
if (!this.connector) throw WalletInitializationError.notReady("Wallet adapter is not ready yet");
9192

9293
if (this.connected) {
93-
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId.toString() });
94+
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId });
9495
return this.provider;
9596
}
9697

@@ -114,7 +115,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
114115
});
115116
try {
116117
// Subscribe to session connection
117-
this.connector.on("connect", async (error: Error | null, payload: { params: { accounts: string[]; chainId: string }[] }) => {
118+
this.connector.on("connect", async (error: Error | null, payload: { params: { accounts: string[]; chainId: number }[] }) => {
118119
if (error) {
119120
this.emit(ADAPTER_EVENTS.ERRORED, error);
120121
}
@@ -178,7 +179,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
178179
return reject(err);
179180
}
180181
const uri = payload.params[0];
181-
this.updateAdapterData({ uri } as WalletConnectV1Data);
182+
this.updateAdapterData({ uri, extensionAdapters: WALLET_CONNECT_EXTENSION_ADAPTERS } as WalletConnectV1Data);
182183

183184
this.connector?.off("display_uri");
184185
return resolve();
@@ -192,25 +193,34 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
192193
});
193194
}
194195

195-
private async onConnectHandler(params: { accounts: string[]; chainId: string }) {
196+
private async onConnectHandler(params: { accounts: string[]; chainId: number }) {
196197
if (!this.connector || !this.wcProvider) throw WalletInitializationError.notReady("Wallet adapter is not ready yet");
197198
if (!this.chainConfig) throw WalletInitializationError.invalidParams("Chain config is not set");
198199

199200
const { chainId } = params;
200-
log.debug("connected chainId", chainId);
201-
const connectedChainId = parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
202-
if (connectedChainId !== parseInt(this.chainConfig.chainId, 16)) {
203-
// we need to create a new session since old session is already used and
204-
// user needs to login again with correct chain with new qr code.
205-
await this.createNewSession({ forceNewSession: true });
206-
this.emit(
207-
ADAPTER_EVENTS.ERRORED,
208-
WalletInitializationError.fromCode(
209-
5000,
210-
`Not connected to correct chainId. Expected: ${this.chainConfig.chainId}, Current: ${connectedChainId}, Please switch to correct chain from wallet`
211-
)
212-
);
213-
return;
201+
log.debug("connected chainId in hex", chainId);
202+
if (chainId !== parseInt(this.chainConfig.chainId, 16)) {
203+
try {
204+
await this.wcProvider.switchChain({ chainId: this.chainConfig.chainId, lookup: false });
205+
} catch (error) {
206+
log.error(error);
207+
// we need to create a new session since old session is already used and
208+
// user needs to login again with correct chain with new qr code.
209+
await this.createNewSession({ forceNewSession: true });
210+
const connectedChainConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, chainId);
211+
this.emit(
212+
ADAPTER_EVENTS.ERRORED,
213+
WalletInitializationError.fromCode(
214+
5000,
215+
`Not connected to correct network. Expected: ${this.chainConfig.displayName}, Current: ${
216+
connectedChainConfig?.displayName || chainId
217+
}, Please switch to correct network from wallet`
218+
)
219+
);
220+
this.status = ADAPTER_STATUS.READY;
221+
this.rehydrated = true;
222+
return;
223+
}
214224
}
215225
await this.wcProvider.setupProvider(this.connector);
216226
this.subscribeEvents(this.connector);

packages/base/src/adapter/IAdapter.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,23 @@ export type LoginMethodConfig = Record<
199199
}
200200
>;
201201

202+
export interface IWalletConnectExtensionAdapter {
203+
name: string;
204+
chains: ChainNamespaceType[];
205+
logo: string;
206+
mobile: {
207+
native: string;
208+
universal: string;
209+
};
210+
desktop: {
211+
native: string;
212+
universal: string;
213+
};
214+
}
215+
202216
export interface WalletConnectV1Data {
203217
uri: string;
218+
extensionAdapters: IWalletConnectExtensionAdapter[];
204219
}
205220

206221
export interface IAdapterDataEvent {

packages/modal/src/modalManager.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ export interface Web3AuthOptions extends Web3AuthCoreOptions {
5959
* Config for configuring modal ui display properties
6060
*/
6161
uiConfig?: UIConfig;
62+
63+
/**
64+
* Whether to show errors on Web3Auth modal.
65+
*
66+
* @defaultValue `true`
67+
*/
68+
displayErrorsOnModal?: boolean;
6269
}
6370
export class Web3Auth extends Web3AuthCore {
6471
public loginModal: LoginModal;
@@ -95,6 +102,7 @@ export class Web3Auth extends Web3AuthCore {
95102
appLogo: this.options.uiConfig?.appLogo || "",
96103
version: "",
97104
adapterListener: this,
105+
displayErrorsOnModal: this.options.displayErrorsOnModal,
98106
});
99107
this.subscribeToLoginModalEvents();
100108
}

packages/providers/ethereum-provider/src/providers/injectedProviders/WalletConnectProvider.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { providerFromEngine } from "@toruslabs/base-controllers";
22
import { JRPCEngine } from "@toruslabs/openlogin-jrpc";
33
import type { IConnector } from "@walletconnect/types";
4-
import { CHAIN_NAMESPACES, CustomChainConfig, isHexStrict, WalletInitializationError, WalletLoginError } from "@web3auth/base";
4+
import { CHAIN_NAMESPACES, CustomChainConfig, getChainConfig, isHexStrict, log, WalletInitializationError, WalletLoginError } from "@web3auth/base";
55
import { BaseProvider, BaseProviderConfig, BaseProviderState } from "@web3auth/base-provider";
66
import { ethErrors } from "eth-rpc-errors";
77

@@ -49,23 +49,39 @@ export class WalletConnectProvider extends BaseProvider<BaseProviderConfig, Wall
4949
await this.setupEngine(connector);
5050
}
5151

52-
public async switchChain({ chainId }: { chainId: string }): Promise<void> {
52+
public async switchChain({ chainId, lookup = true }: { chainId: string; lookup?: boolean }): Promise<void> {
53+
if (!this.connector)
54+
throw ethErrors.provider.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 });
5355
const currentChainConfig = this.getChainConfig(chainId);
54-
const { ticker, tickerName, rpcTarget } = currentChainConfig;
56+
const { rpcTarget, displayName } = currentChainConfig;
5557
this.update({
5658
chainId: "loading",
5759
});
58-
await this.connector.updateChain({
59-
chainId: Number.parseInt(chainId, 16),
60-
nativeCurrency: {
61-
name: tickerName,
62-
symbol: ticker,
63-
},
64-
networkId: Number.parseInt(chainId, 10),
65-
rpcUrl: rpcTarget,
66-
});
60+
try {
61+
await this.connector.sendCustomRequest({
62+
method: "wallet_addEthereumChain",
63+
params: [{ chainId, chainName: displayName, rpcUrls: [rpcTarget] }],
64+
});
65+
} catch (error) {
66+
log.error(error);
67+
}
68+
69+
try {
70+
await this.connector.sendCustomRequest({
71+
method: "wallet_switchEthereumChain",
72+
params: [{ chainId }],
73+
});
74+
} catch (error) {
75+
log.error(error);
76+
// ignore this error because metamask & others return provider.result as null
77+
// wallet connect thinks this is wrong
78+
if (error.message !== "JSON RPC response format is invalid") {
79+
throw error;
80+
}
81+
}
82+
6783
this.configure({ chainConfig: currentChainConfig });
68-
await this.lookupNetwork(this.connector);
84+
if (lookup) await this.lookupNetwork(this.connector);
6985
}
7086

7187
protected async lookupNetwork(connector: IConnector): Promise<string> {
@@ -104,7 +120,7 @@ export class WalletConnectProvider extends BaseProvider<BaseProviderConfig, Wall
104120
this.provider.emit("error", error);
105121
return;
106122
}
107-
const { accounts, chainId: connectedChainId, rpcUrl } = payload;
123+
const { accounts, chainId: connectedChainId, rpcUrl }: { accounts?: string[]; chainId?: number; rpcUrl?: string } = payload.params[0];
108124
// Check if accounts changed and trigger event
109125
if (accounts?.length && this.state.accounts[0] !== accounts[0]) {
110126
this.update({
@@ -113,12 +129,13 @@ export class WalletConnectProvider extends BaseProvider<BaseProviderConfig, Wall
113129
// await this.setupEngine(connector);
114130
this.provider.emit("accountsChanged", accounts);
115131
}
116-
const connectedHexChainId = isHexStrict(connectedChainId) ? connectedChainId : `0x${connectedChainId.toString(16)}`;
132+
const connectedHexChainId = `0x${connectedChainId.toString(16)}`;
117133
// Check if chainId changed and trigger event
118134
if (connectedChainId && this.state.chainId !== connectedHexChainId) {
135+
const maybeConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, connectedChainId) || {};
119136
// Handle rpcUrl update
120137
this.configure({
121-
chainConfig: { ...this.config.chainConfig, chainId: connectedHexChainId, rpcTarget: rpcUrl },
138+
chainConfig: { ...maybeConfig, chainId: connectedHexChainId, rpcTarget: rpcUrl, chainNamespace: CHAIN_NAMESPACES.EIP155 },
122139
});
123140
await this.setupEngine(connector);
124141
}

packages/ui/css/web3auth.css

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,21 +503,48 @@
503503
}
504504

505505
#w3a-modal .w3a-wallet-connect__container {
506-
padding: 10px;
507506
background: #ffffff;
508507
border-radius: 10px;
509508
color: var(--text-color1);
510509
font-size: 10px;
511510
width: fit-content;
512511
margin: auto;
512+
min-width: 250px;
513+
padding: 16px 12px;
513514
}
514515

515-
.w3a-wallet-connect-qr {
516-
width: 200px;
516+
#w3a-modal .w3a-wallet-connect__container-desktop,
517+
#w3a-modal .w3a-wallet-connect__container-android {
518+
margin: auto;
519+
}
520+
521+
#w3a-modal .w3a-wallet-connect__container-ios {
522+
display: flex;
523+
grid-gap: 30px 20px;
524+
padding: 0 0 28px;
525+
box-sizing: border-box;
526+
flex-wrap: wrap;
527+
}
528+
529+
#w3a-modal .w3a-wallet-connect-qr {
517530
margin: 16px 16px;
518531
padding: inherit;
519532
}
520533

534+
#w3a-modal .w3a-wallet-connect__container-android a {
535+
text-decoration: none;
536+
}
537+
538+
#w3a-modal .w3a-wallet-connect__container-android .w3a-button {
539+
background-color: rgb(64, 153, 255) !important;
540+
color: #ffffff !important;
541+
height: auto;
542+
font-size: 14px;
543+
padding: 8px 16px;
544+
width: auto;
545+
margin: auto;
546+
}
547+
521548
#w3a-modal .w3a-wallet-connect__logo > img {
522549
text-align: center;
523550
width: 115px;

packages/ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@
6666
"lodash.merge": "^4.6.2",
6767
"react": "^17.0.2",
6868
"react-dom": "^17.0.2",
69-
"react-qr-code": "^2.0.3"
69+
"react-qr-code": "^2.0.3",
70+
"bowser": "^2.11.0"
7071
},
7172
"lint-staged": {
7273
"!(*d).ts": [

0 commit comments

Comments
 (0)