Skip to content

Commit

Permalink
Merge pull request #115 from Web3Auth/feat/wc-adapters
Browse files Browse the repository at this point in the history
Add Rainbow & coinbase wallet adapters
  • Loading branch information
chaitanyapotti authored Mar 30, 2022
2 parents 62bd27a + f8f2923 commit 54a8aa1
Show file tree
Hide file tree
Showing 16 changed files with 293 additions and 63 deletions.
2 changes: 2 additions & 0 deletions examples/vue-app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion examples/vue-app/src/chains/ethereum.vue
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ export default Vue.extend({
try {
this.parseConfig();
this.loading = true;
this.web3auth = new Web3Auth({ chainConfig: ethChainConfig, clientId: config.clientId, authMode: "DAPP", enableLogging: true });
this.web3auth = new Web3Auth({
chainConfig: ethChainConfig,
clientId: config.clientId,
authMode: "DAPP",
enableLogging: true,
});
const openloginAdapter = new OpenloginAdapter({
adapterSettings: {
network: this.openloginNetwork as OPENLOGIN_NETWORK_TYPE,
Expand Down
8 changes: 7 additions & 1 deletion examples/vue-app/vue.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,13 @@ module.exports = {
},
chainWebpack: (config) => {
if (process.env.NODE_ENV !== "production") {
config.module.rule("sourcemap").test(/\.js$/).enforce("pre").use("source-map-loader").loader("source-map-loader").end();
config.module
.rule("sourcemap")
.test(/\.${js,ts}$/)
.enforce("pre")
.use("source-map-loader")
.loader("source-map-loader")
.end();
}
const svgRule = config.module.rule("svg");

Expand Down
17 changes: 17 additions & 0 deletions packages/adapters/wallet-connect-v1-adapter/src/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { CHAIN_NAMESPACES, IWalletConnectExtensionAdapter } from "@web3auth/base";

export const WALLET_CONNECT_EXTENSION_ADAPTERS: IWalletConnectExtensionAdapter[] = [
{
name: "Rainbow",
chains: [CHAIN_NAMESPACES.EIP155],
logo: "https://images.web3auth.io/login-rainbow.svg",
mobile: {
native: "rainbow:",
universal: "https://rnbwapp.com",
},
desktop: {
native: "",
universal: "",
},
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
CONNECTED_EVENT_DATA,
CustomChainConfig,
getChainConfig,
isHexStrict,
log,
SafeEventEmitterProvider,
UserInfo,
Expand All @@ -25,6 +24,7 @@ import {
} from "@web3auth/base";
import { WalletConnectProvider } from "@web3auth/ethereum-provider";

import { WALLET_CONNECT_EXTENSION_ADAPTERS } from "./config";
import { WalletConnectV1AdapterOptions } from "./interface";

class WalletConnectV1Adapter extends BaseAdapter<void> {
Expand All @@ -42,6 +42,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {

public adapterData: WalletConnectV1Data = {
uri: "",
extensionAdapters: WALLET_CONNECT_EXTENSION_ADAPTERS,
};

public connector: WalletConnect | null = null;
Expand Down Expand Up @@ -75,13 +76,13 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
}
// Create a connector
this.connector = this.getWalletConnectInstance();
this.wcProvider = new WalletConnectProvider({ config: { chainConfig: this.chainConfig as CustomChainConfig } });
this.wcProvider = new WalletConnectProvider({ config: { chainConfig: this.chainConfig as CustomChainConfig }, connector: this.connector });

this.emit(ADAPTER_EVENTS.READY, WALLET_ADAPTERS.WALLET_CONNECT_V1);
this.status = ADAPTER_STATUS.READY;
if (this.connector.connected) {
this.rehydrated = true;
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId.toString() });
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId });
}
}

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

if (this.connected) {
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId.toString() });
await this.onConnectHandler({ accounts: this.connector.accounts, chainId: this.connector.chainId });
return this.provider;
}

Expand All @@ -114,7 +115,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
});
try {
// Subscribe to session connection
this.connector.on("connect", async (error: Error | null, payload: { params: { accounts: string[]; chainId: string }[] }) => {
this.connector.on("connect", async (error: Error | null, payload: { params: { accounts: string[]; chainId: number }[] }) => {
if (error) {
this.emit(ADAPTER_EVENTS.ERRORED, error);
}
Expand Down Expand Up @@ -178,7 +179,7 @@ class WalletConnectV1Adapter extends BaseAdapter<void> {
return reject(err);
}
const uri = payload.params[0];
this.updateAdapterData({ uri } as WalletConnectV1Data);
this.updateAdapterData({ uri, extensionAdapters: WALLET_CONNECT_EXTENSION_ADAPTERS } as WalletConnectV1Data);

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

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

const { chainId } = params;
log.debug("connected chainId", chainId);
const connectedChainId = parseInt(chainId, isHexStrict(chainId) ? 16 : 10);
if (connectedChainId !== parseInt(this.chainConfig.chainId, 16)) {
// we need to create a new session since old session is already used and
// user needs to login again with correct chain with new qr code.
await this.createNewSession({ forceNewSession: true });
this.emit(
ADAPTER_EVENTS.ERRORED,
WalletInitializationError.fromCode(
5000,
`Not connected to correct chainId. Expected: ${this.chainConfig.chainId}, Current: ${connectedChainId}, Please switch to correct chain from wallet`
)
);
return;
log.debug("connected chainId in hex", chainId);
if (chainId !== parseInt(this.chainConfig.chainId, 16)) {
try {
await this.wcProvider.switchChain({ chainId: this.chainConfig.chainId, lookup: false });
} catch (error) {
log.error(error);
// we need to create a new session since old session is already used and
// user needs to login again with correct chain with new qr code.
await this.createNewSession({ forceNewSession: true });
const connectedChainConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, chainId);
this.emit(
ADAPTER_EVENTS.ERRORED,
WalletInitializationError.fromCode(
5000,
`Not connected to correct network. Expected: ${this.chainConfig.displayName}, Current: ${
connectedChainConfig?.displayName || chainId
}, Please switch to correct network from wallet`
)
);
this.status = ADAPTER_STATUS.READY;
this.rehydrated = true;
return;
}
}
await this.wcProvider.setupProvider(this.connector);
this.subscribeEvents(this.connector);
Expand Down
15 changes: 15 additions & 0 deletions packages/base/src/adapter/IAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,23 @@ export type LoginMethodConfig = Record<
}
>;

export interface IWalletConnectExtensionAdapter {
name: string;
chains: ChainNamespaceType[];
logo: string;
mobile: {
native: string;
universal: string;
};
desktop: {
native: string;
universal: string;
};
}

export interface WalletConnectV1Data {
uri: string;
extensionAdapters: IWalletConnectExtensionAdapter[];
}

export interface IAdapterDataEvent {
Expand Down
8 changes: 8 additions & 0 deletions packages/modal/src/modalManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ export interface Web3AuthOptions extends Web3AuthCoreOptions {
* Config for configuring modal ui display properties
*/
uiConfig?: UIConfig;

/**
* Whether to show errors on Web3Auth modal.
*
* @defaultValue `true`
*/
displayErrorsOnModal?: boolean;
}
export class Web3Auth extends Web3AuthCore {
public loginModal: LoginModal;
Expand Down Expand Up @@ -95,6 +102,7 @@ export class Web3Auth extends Web3AuthCore {
appLogo: this.options.uiConfig?.appLogo || "",
version: "",
adapterListener: this,
displayErrorsOnModal: this.options.displayErrorsOnModal,
});
this.subscribeToLoginModalEvents();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { providerFromEngine } from "@toruslabs/base-controllers";
import { JRPCEngine } from "@toruslabs/openlogin-jrpc";
import type { IConnector } from "@walletconnect/types";
import { CHAIN_NAMESPACES, CustomChainConfig, isHexStrict, WalletInitializationError, WalletLoginError } from "@web3auth/base";
import { CHAIN_NAMESPACES, CustomChainConfig, getChainConfig, isHexStrict, log, WalletInitializationError, WalletLoginError } from "@web3auth/base";
import { BaseProvider, BaseProviderConfig, BaseProviderState } from "@web3auth/base-provider";
import { ethErrors } from "eth-rpc-errors";

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

public async switchChain({ chainId }: { chainId: string }): Promise<void> {
public async switchChain({ chainId, lookup = true }: { chainId: string; lookup?: boolean }): Promise<void> {
if (!this.connector)
throw ethErrors.provider.custom({ message: "Connector is not initialized, pass wallet connect connector in constructor", code: 4902 });
const currentChainConfig = this.getChainConfig(chainId);
const { ticker, tickerName, rpcTarget } = currentChainConfig;
const { rpcTarget, displayName } = currentChainConfig;
this.update({
chainId: "loading",
});
await this.connector.updateChain({
chainId: Number.parseInt(chainId, 16),
nativeCurrency: {
name: tickerName,
symbol: ticker,
},
networkId: Number.parseInt(chainId, 10),
rpcUrl: rpcTarget,
});
try {
await this.connector.sendCustomRequest({
method: "wallet_addEthereumChain",
params: [{ chainId, chainName: displayName, rpcUrls: [rpcTarget] }],
});
} catch (error) {
log.error(error);
}

try {
await this.connector.sendCustomRequest({
method: "wallet_switchEthereumChain",
params: [{ chainId }],
});
} catch (error) {
log.error(error);
// ignore this error because metamask & others return provider.result as null
// wallet connect thinks this is wrong
if (error.message !== "JSON RPC response format is invalid") {
throw error;
}
}

this.configure({ chainConfig: currentChainConfig });
await this.lookupNetwork(this.connector);
if (lookup) await this.lookupNetwork(this.connector);
}

protected async lookupNetwork(connector: IConnector): Promise<string> {
Expand Down Expand Up @@ -104,7 +120,7 @@ export class WalletConnectProvider extends BaseProvider<BaseProviderConfig, Wall
this.provider.emit("error", error);
return;
}
const { accounts, chainId: connectedChainId, rpcUrl } = payload;
const { accounts, chainId: connectedChainId, rpcUrl }: { accounts?: string[]; chainId?: number; rpcUrl?: string } = payload.params[0];
// Check if accounts changed and trigger event
if (accounts?.length && this.state.accounts[0] !== accounts[0]) {
this.update({
Expand All @@ -113,12 +129,13 @@ export class WalletConnectProvider extends BaseProvider<BaseProviderConfig, Wall
// await this.setupEngine(connector);
this.provider.emit("accountsChanged", accounts);
}
const connectedHexChainId = isHexStrict(connectedChainId) ? connectedChainId : `0x${connectedChainId.toString(16)}`;
const connectedHexChainId = `0x${connectedChainId.toString(16)}`;
// Check if chainId changed and trigger event
if (connectedChainId && this.state.chainId !== connectedHexChainId) {
const maybeConfig = getChainConfig(CHAIN_NAMESPACES.EIP155, connectedChainId) || {};
// Handle rpcUrl update
this.configure({
chainConfig: { ...this.config.chainConfig, chainId: connectedHexChainId, rpcTarget: rpcUrl },
chainConfig: { ...maybeConfig, chainId: connectedHexChainId, rpcTarget: rpcUrl, chainNamespace: CHAIN_NAMESPACES.EIP155 },
});
await this.setupEngine(connector);
}
Expand Down
33 changes: 30 additions & 3 deletions packages/ui/css/web3auth.css
Original file line number Diff line number Diff line change
Expand Up @@ -503,21 +503,48 @@
}

#w3a-modal .w3a-wallet-connect__container {
padding: 10px;
background: #ffffff;
border-radius: 10px;
color: var(--text-color1);
font-size: 10px;
width: fit-content;
margin: auto;
min-width: 250px;
padding: 16px 12px;
}

.w3a-wallet-connect-qr {
width: 200px;
#w3a-modal .w3a-wallet-connect__container-desktop,
#w3a-modal .w3a-wallet-connect__container-android {
margin: auto;
}

#w3a-modal .w3a-wallet-connect__container-ios {
display: flex;
grid-gap: 30px 20px;
padding: 0 0 28px;
box-sizing: border-box;
flex-wrap: wrap;
}

#w3a-modal .w3a-wallet-connect-qr {
margin: 16px 16px;
padding: inherit;
}

#w3a-modal .w3a-wallet-connect__container-android a {
text-decoration: none;
}

#w3a-modal .w3a-wallet-connect__container-android .w3a-button {
background-color: rgb(64, 153, 255) !important;
color: #ffffff !important;
height: auto;
font-size: 14px;
padding: 8px 16px;
width: auto;
margin: auto;
}

#w3a-modal .w3a-wallet-connect__logo > img {
text-align: center;
width: 115px;
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
"lodash.merge": "^4.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-qr-code": "^2.0.3"
"react-qr-code": "^2.0.3",
"bowser": "^2.11.0"
},
"lint-staged": {
"!(*d).ts": [
Expand Down
Loading

0 comments on commit 54a8aa1

Please sign in to comment.