Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[react, wallets] Add imToken Wallet #2497

Merged
merged 11 commits into from Mar 20, 2024
7 changes: 7 additions & 0 deletions .changeset/tall-moose-add-imToken.md
@@ -0,0 +1,7 @@
---
"@thirdweb-dev/wallets": patch
"@thirdweb-dev/react": patch
"@thirdweb-dev/react-core": patch
---

Add imToken Wallet
4 changes: 3 additions & 1 deletion packages/react-core/src/core/hooks/wallet-hooks.ts
Expand Up @@ -8,6 +8,7 @@ import type {
CryptoDefiWallet,
EmbeddedWallet,
FrameWallet,
ImTokenWallet,
LocalWallet,
MagicEdenWallet,
MagicLink,
Expand Down Expand Up @@ -41,6 +42,7 @@ export type WalletIdToWalletTypeMap = {
rainbowWallet: RainbowWallet;
blocto: BloctoWallet;
frame: FrameWallet;
imToken: ImTokenWallet;
localWallet: LocalWallet;
magicLink: MagicLink;
paper: PaperWallet;
Expand All @@ -57,7 +59,7 @@ export type WalletIdToWalletTypeMap = {
cryptoDefiWallet: CryptoDefiWallet;
rabby: RabbyWallet;
xdefi: XDEFIWallet;
magicEden: MagicEdenWallet
magicEden: MagicEdenWallet;
};

/**
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/evm/index.ts
Expand Up @@ -111,4 +111,5 @@ export {
CryptoDefiWallet,
RabbyWallet,
Coin98Wallet,
ImTokenWallet,
} from "@thirdweb-dev/wallets";
1 change: 1 addition & 0 deletions packages/react/src/evm/locales/en.ts
Expand Up @@ -171,6 +171,7 @@ export function enDefault(): ThirdwebLocale {
xdefiWallet: extensionAndQRScanScreens("XDEFI"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
imTokenWallet: extensionAndQRScanScreens("imToken"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
coreWallet: extensionAndQRScanScreens("Core"),
oneKeyWallet: extensionAndQRScanScreens("OneKey"),
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/evm/locales/es.ts
Expand Up @@ -176,6 +176,7 @@ export function esDefault(): ThirdwebLocale {
xdefiWallet: extensionAndQRScanScreens("XDEFI"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
imTokenWallet: extensionAndQRScanScreens("imToken"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
oneKeyWallet: extensionAndQRScanScreens("OneKey"),
cryptoDefiWallet: extensionAndQRScanScreens("Crypto Defi"),
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/evm/locales/ja.ts
Expand Up @@ -176,6 +176,7 @@ export function jaDefault(): ThirdwebLocale {
xdefiWallet: extensionAndQRScanScreens("XDEFI"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
imTokenWallet: extensionAndQRScanScreens("imToken"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
oneKeyWallet: extensionAndQRScanScreens("OneKey"),
cryptoDefiWallet: extensionAndQRScanScreens("Crypto Defi"),
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/evm/locales/tl.ts
Expand Up @@ -176,6 +176,7 @@ export function tlDefault(): ThirdwebLocale {
xdefiWallet: extensionAndQRScanScreens("XDEFI"),
rainbowWallet: extensionAndQRScanScreens("Rainbow"),
trustWallet: extensionAndQRScanScreens("Trust"),
imTokenWallet: extensionAndQRScanScreens("imToken"),
zerionWallet: extensionAndQRScanScreens("Zerion"),
coreWallet: extensionAndQRScanScreens("Core"),
oneKeyWallet: extensionAndQRScanScreens("OneKey"),
Expand Down
3 changes: 2 additions & 1 deletion packages/react/src/evm/locales/types.ts
Expand Up @@ -183,6 +183,7 @@ export type ThirdwebLocale = {
getStartedScreen: { instruction: string };
scanScreen: { instruction: string };
};
trustWallet: ExtensionAndQRScreensLocale;
localWallet: {
confirmPasswordLabel: string;
createScreen: {
Expand Down Expand Up @@ -302,7 +303,7 @@ export type ThirdwebLocale = {
title: string;
};
};
trustWallet: ExtensionAndQRScreensLocale;
imTokenWallet: ExtensionAndQRScreensLocale;
walletConnect: { scanInstruction: string };
zerionWallet: ExtensionAndQRScreensLocale;
};
Expand Down
5 changes: 5 additions & 0 deletions packages/react/src/index.ts
Expand Up @@ -107,6 +107,11 @@ export {
type Coin98WalletConfigOptions,
} from "./wallet/wallets/coin98/coin98Wallet";

export {
imTokenWallet,
type ImTokenWalletConfigOptions,
} from "./wallet/wallets/imtoken/imTokenWallet";

// theming
export { darkTheme, lightTheme } from "./design-system/index";
export type { Theme, ThemeOverrides } from "./design-system/index";
Expand Down
3 changes: 3 additions & 0 deletions packages/react/src/wallet/hooks/useInstalledWallets.ts
Expand Up @@ -12,19 +12,22 @@ export function useInstalledWallets() {
let isCoinbaseWalletInstalled = false;
let isZerionWalletInstalled = false;
let isTrustWalletInstalled = false;
let isImTokenInstalled = false;

const window_: Window | undefined = globalThis?.window;
if (assertWindowEthereum(window_)) {
isMetamaskInstalled = !!getInjectedMetamaskProvider();
isCoinbaseWalletInstalled = !!getInjectedCoinbaseProvider();
isZerionWalletInstalled = !!window_.ethereum?.isZerion;
isTrustWalletInstalled = !!window_.ethereum?.isTrust;
isImTokenInstalled = !!window_.ethereum?.isToken;
}

return {
metamask: isMetamaskInstalled,
coinbaseWallet: isCoinbaseWalletInstalled,
trustWallet: isTrustWalletInstalled,
zerionWallet: isZerionWalletInstalled,
imtokenWallet: isImTokenInstalled,
};
}
73 changes: 73 additions & 0 deletions packages/react/src/wallet/wallets/imtoken/imTokenWallet.tsx
@@ -0,0 +1,73 @@
import type {
WalletOptions,
WalletConfig,
ConnectUIProps,
} from "@thirdweb-dev/react-core";
import { ImTokenWallet, assertWindowEthereum } from "@thirdweb-dev/wallets";
import { useTWLocale } from "../../../evm/providers/locale-provider";
import { ExtensionOrWCConnectionUI } from "../_common/ExtensionORWCConnectionUI";

const imTokenWalletUris = {
ios: "https://itunes.apple.com/us/app/imtoken2/id1384798940",
android: "https://play.google.com/store/apps/details?id=im.token.app",
};

export type ImTokenWalletConfigOptions = {
projectId?: string;
recommended?: boolean;
};

export const imTokenWallet = (
options?: ImTokenWalletConfigOptions,
): WalletConfig<ImTokenWallet> => {
return {
id: ImTokenWallet.id,
recommended: options?.recommended,
meta: {
...ImTokenWallet.meta,
iconURL:
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAgMCA4MCA4MCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzIyMl80NzgzKSI+CjxtYXNrIGlkPSJtYXNrMF8yMjJfNDc4MyIgc3R5bGU9Im1hc2stdHlwZTpsdW1pbmFuY2UiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjAiIHk9IjAiIHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCI+CjxwYXRoIGQ9Ik03OS44OTQ4IDBIMC4wNTA3ODEyVjgwSDc5Ljg5NDhWMFoiIGZpbGw9IndoaXRlIi8+CjwvbWFzaz4KPGcgbWFzaz0idXJsKCNtYXNrMF8yMjJfNDc4MykiPgo8cGF0aCBkPSJNNjIuMDI3NSAwSDE4LjA1MDlDOC4xNDYzOSAwIDAuMTE3MTg4IDguMDQ0ODggMC4xMTcxODggMTcuOTY4OFY2Mi4wMzEyQzAuMTE3MTg4IDcxLjk1NTEgOC4xNDYzOSA4MCAxOC4wNTA5IDgwSDYyLjAyNzVDNzEuOTMyIDgwIDc5Ljk2MTIgNzEuOTU1MSA3OS45NjEyIDYyLjAzMTJWMTcuOTY4OEM3OS45NjEyIDguMDQ0ODggNzEuOTMyIDAgNjIuMDI3NSAwWiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzIyMl80NzgzKSIvPgo8cGF0aCBkPSJNNjUuMDk4MSAyNC43MzNDNjYuNzU4NiA0Ny4yNjY3IDUyLjMwMjEgNTcuOTE3MiAzOS4zNDIzIDU5LjA1M0MyNy4yOTM1IDYwLjEwODcgMTUuOTUyIDUyLjY5MDggMTQuOTU3MSA0MS4yOTM2QzE0LjEzNjMgMzEuODc3NiAxOS45NDQ1IDI3Ljg2ODkgMjQuNTA4IDI3LjQ2OTRDMjkuMjAxNSAyNy4wNTcgMzMuMTQ1OCAzMC4zMDAxIDMzLjQ4NzkgMzQuMjI2OUMzMy44MTc1IDM4LjAwMiAzMS40NjY0IDM5LjcyMDUgMjkuODMxMyAzOS44NjM0QzI4LjUzODIgMzkuOTc3IDI2LjkxMTQgMzkuMTkwNSAyNi43NjQ1IDM3LjUwMTZDMjYuNjM4NSAzNi4wNTAzIDI3LjE4ODUgMzUuODUyNiAyNy4wNTQxIDM0LjMxMDlDMjYuODE0OSAzMS41NjYyIDI0LjQyNjEgMzEuMjQ2NiAyMy4xMTgzIDMxLjM2MDFDMjEuNTM1NyAzMS40OTkxIDE4LjY2NDEgMzMuMzQ5OCAxOS4wNjcgMzcuOTZDMTkuNDcyMiA0Mi42MTAxIDIzLjkyMjIgNDYuMjg0NSAyOS43NTU3IDQ1Ljc3MzRDMzYuMDUwOSA0NS4yMjIzIDQwLjQzMzcgNDAuMzExNCA0MC43NjM0IDMzLjQyMzRDNDAuNzYwMyAzMy4wNTg2IDQwLjgzNyAzMi42OTc1IDQwLjk4OCAzMi4zNjU1TDQwLjk5IDMyLjM1NzJDNDEuMDU3OCAzMi4yMTI4IDQxLjEzNzIgMzIuMDc0MiA0MS4yMjcyIDMxLjk0MjhDNDEuMzYxNiAzMS43NDA5IDQxLjUzMzggMzEuNTE4IDQxLjc1NjIgMzEuMjczOUM0MS43NTgzIDMxLjI2NzYgNDEuNzU4MyAzMS4yNjc2IDQxLjc2MjYgMzEuMjY3NkM0MS45MjQxIDMxLjA4NDcgNDIuMTE5NCAzMC44ODcgNDIuMzM5NyAzMC42NzQ1QzQ1LjA4OTcgMjguMDc1IDU0Ljk5MzEgMjEuOTQ0MiA2NC4zNTkyIDIzLjg4NTVDNjQuNTU3MyAyMy45MjggNjQuNzM2MSAyNC4wMzM0IDY0Ljg2OTMgMjQuMTg2MkM2NS4wMDI1IDI0LjMzOTEgNjUuMDgyNiAyNC41MzA4IDY1LjA5ODEgMjQuNzMzWiIgZmlsbD0id2hpdGUiLz4KPC9nPgo8L2c+CjxkZWZzPgo8bGluZWFyR3JhZGllbnQgaWQ9InBhaW50MF9saW5lYXJfMjIyXzQ3ODMiIHgxPSI3My41MDA5IiB5MT0iNS4zMTI1IiB4Mj0iMi44NzI0MSIgeTI9Ijc3LjY3NDciIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzExQzREMSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMwMDYyQUQiLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF8yMjJfNDc4MyI+CjxyZWN0IHdpZHRoPSI4MCIgaGVpZ2h0PSI4MCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4=",
},
create: (walletOptions: WalletOptions) => {
const wallet = new ImTokenWallet({
...walletOptions,
projectId: options?.projectId,
qrcode: false,
});

return wallet;
},
connectUI: ConnectUI,
isInstalled: isInstalled,
};
};

function isInstalled() {
if (assertWindowEthereum(globalThis.window)) {
return !!globalThis.window.ethereum.isImToken;
}
return false;
}

function ConnectUI(props: ConnectUIProps<ImTokenWallet>) {
const locale = useTWLocale();
return (
<ExtensionOrWCConnectionUI
connect={props.connect}
connected={props.connected}
createWalletInstance={props.createWalletInstance}
goBack={props.goBack}
meta={props.walletConfig.meta}
setConnectedWallet={(w) => props.setConnectedWallet(w as ImTokenWallet)}
setConnectionStatus={props.setConnectionStatus}
supportedWallets={props.supportedWallets}
walletConnectUris={{
ios: imTokenWalletUris.ios,
android: imTokenWalletUris.android,
other: imTokenWalletUris.android,
}}
walletLocale={locale.wallets.imTokenWallet}
isInstalled={isInstalled}
/>
);
}
7 changes: 7 additions & 0 deletions packages/wallets/evm/connectors/imtoken/package.json
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-connectors-imtoken.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js": "./dist/thirdweb-dev-wallets-evm-connectors-imtoken.browser.esm.js"
}
}
7 changes: 7 additions & 0 deletions packages/wallets/evm/wallets/imtoken/package.json
@@ -0,0 +1,7 @@
{
"main": "dist/thirdweb-dev-wallets-evm-wallets-imtoken.cjs.js",
"module": "dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js",
"browser": {
"./dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js": "./dist/thirdweb-dev-wallets-evm-wallets-imtoken.browser.esm.js"
}
}
14 changes: 14 additions & 0 deletions packages/wallets/package.json
Expand Up @@ -147,6 +147,13 @@
},
"default": "./evm/wallets/gcp-kms/dist/thirdweb-dev-wallets-evm-wallets-gcp-kms.cjs.js"
},
"./evm/wallets/imtoken": {
"module": {
"browser": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.browser.esm.js",
"default": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.esm.js"
},
"default": "./evm/wallets/imtoken/dist/thirdweb-dev-wallets-evm-wallets-imtoken.cjs.js"
},
"./evm/wallets/phantom": {
"module": {
"browser": "./evm/wallets/phantom/dist/thirdweb-dev-wallets-evm-wallets-phantom.browser.esm.js",
Expand Down Expand Up @@ -336,6 +343,13 @@
},
"default": "./evm/wallets/embedded-wallet/dist/thirdweb-dev-wallets-evm-wallets-embedded-wallet.cjs.js"
},
"./evm/connectors/imtoken": {
"module": {
"browser": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.browser.esm.js",
"default": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.esm.js"
},
"default": "./evm/connectors/imtoken/dist/thirdweb-dev-wallets-evm-connectors-imtoken.cjs.js"
},
"./evm/connectors/phantom": {
"module": {
"browser": "./evm/connectors/phantom/dist/thirdweb-dev-wallets-evm-connectors-phantom.browser.esm.js",
Expand Down
48 changes: 48 additions & 0 deletions packages/wallets/src/evm/connectors/imtoken/index.ts
@@ -0,0 +1,48 @@
import { InjectedConnector, InjectedConnectorOptions } from "../injected";
import { assertWindowEthereum } from "../../utils/assertWindowEthereum";
import { Ethereum } from "../injected/types";
import type { Chain } from "@thirdweb-dev/chains";
import { AsyncStorage } from "../../../core/AsyncStorage";

type ImTokenConnectorConstructorArg = {
chains?: Chain[];
connectorStorage: AsyncStorage;
options?: InjectedConnectorOptions;
};

export class ImTokenConnector extends InjectedConnector {
constructor(arg: ImTokenConnectorConstructorArg) {
const defaultOptions = {
name: "imToken",
getProvider() {
function getReady(ethereum?: Ethereum) {
const isToken = !!ethereum?.isToken;
if (!isToken) {
return;
}
return ethereum;
}

if (typeof window === "undefined") {
return;
}
if (assertWindowEthereum(globalThis.window)) {
if (globalThis.window.ethereum?.providers) {
return globalThis.window.ethereum.providers.find(getReady);
}

return getReady(globalThis.window.ethereum);
}
},
};
const options = {
...defaultOptions,
...arg.options,
};
super({
chains: arg.chains,
options,
connectorStorage: arg.connectorStorage,
});
}
}
2 changes: 2 additions & 0 deletions packages/wallets/src/evm/connectors/injected/types.ts
Expand Up @@ -56,6 +56,8 @@ type InjectedProviderFlags = {
isDefiWallet?: true;
isRabbyWallet?: true;
isCoin98Wallet?: true;
// imToken
isToken?: true;
};
type InjectedProviders = InjectedProviderFlags & {
isMetaMask: true;
Expand Down
1 change: 1 addition & 0 deletions packages/wallets/src/evm/constants/walletIds.ts
Expand Up @@ -5,6 +5,7 @@ export const walletIds = {
coreWallet: "coreWallet",
cryptoDefiWallet: "cryptoDefiWallet",
frame: "frame",
imtoken: "imToken",
localWallet: "localWallet",
magicLink: "magicLink",
metamask: "metamask",
Expand Down
1 change: 1 addition & 0 deletions packages/wallets/src/evm/index.ts
Expand Up @@ -32,6 +32,7 @@ export * from "./wallets/coinbase-wallet";
export * from "./wallets/embedded-wallet";
export * from "./wallets/ethers";
export * from "./wallets/frame";
export * from "./wallets/imtoken";
export * from "./wallets/injected";
export * from "./wallets/local-wallet";
export * from "./wallets/signer";
Expand Down