From 4842f7f184a865a20c091f0d61f9bfde90ab6b34 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Thu, 8 Jun 2023 20:29:56 -0700 Subject: [PATCH 01/11] -Changed wallets to be interfaces where users could self implement the methods or add custom wallets -Improved Wallet connection styles, added ability to tint the UI -Added hook to open and close the connect modal -Removed unnecessary wallet assets -Fixed various typescript exports --- packages/core/src/lib/wallet/types.ts | 7 + packages/react/src/lib/assets/coin98.png | Bin 3550 -> 0 bytes packages/react/src/lib/assets/default.svg | 3 - packages/react/src/lib/assets/falcon.png | Bin 15423 -> 0 bytes packages/react/src/lib/assets/keplr.png | Bin 13328 -> 0 bytes packages/react/src/lib/assets/leap.png | Bin 28169 -> 0 bytes .../WalletConnectButton.tsx | 184 +++++++------- .../components/WalletConnectButton/styles.css | 38 +++ .../components/WalletConnectButton/styles.ts | 25 -- .../components/WalletConnectButton/types.ts | 17 +- .../WalletSelectModal/WalletSelectModal.tsx | 208 ++++++++++------ .../components/WalletSelectModal/styles.css | 235 ++++++++++++++++++ .../components/WalletSelectModal/styles.ts | 53 ---- .../lib/components/WalletSelectModal/types.ts | 12 +- .../react/src/lib/config/supportedWallets.ts | 72 ++++++ .../src/lib/hooks/useCosmWasmClient/index.ts | 6 +- .../src/lib/hooks/useQueryClient/index.ts | 7 +- .../src/lib/hooks/useSelectWallet/index.ts | 2 + .../hooks/useSelectWallet/useSelectWallet.ts | 14 ++ .../src/lib/hooks/useSigningClient/index.ts | 6 +- .../hooks/useSigningCosmWasmClient/index.ts | 6 +- .../src/lib/hooks/useStargateClient/index.ts | 6 +- .../src/lib/provider/SeiWalletProvider.tsx | 180 ++++++-------- packages/react/src/lib/provider/types.ts | 53 ++-- packages/react/src/lib/utils/css.ts | 6 + packages/react/src/lib/utils/index.ts | 1 + 26 files changed, 722 insertions(+), 419 deletions(-) delete mode 100644 packages/react/src/lib/assets/coin98.png delete mode 100644 packages/react/src/lib/assets/default.svg delete mode 100644 packages/react/src/lib/assets/falcon.png delete mode 100644 packages/react/src/lib/assets/keplr.png delete mode 100644 packages/react/src/lib/assets/leap.png create mode 100644 packages/react/src/lib/components/WalletConnectButton/styles.css delete mode 100644 packages/react/src/lib/components/WalletConnectButton/styles.ts create mode 100644 packages/react/src/lib/components/WalletSelectModal/styles.css delete mode 100644 packages/react/src/lib/components/WalletSelectModal/styles.ts create mode 100644 packages/react/src/lib/config/supportedWallets.ts create mode 100644 packages/react/src/lib/hooks/useSelectWallet/index.ts create mode 100644 packages/react/src/lib/hooks/useSelectWallet/useSelectWallet.ts create mode 100644 packages/react/src/lib/utils/css.ts diff --git a/packages/core/src/lib/wallet/types.ts b/packages/core/src/lib/wallet/types.ts index f4fe71eb..a8deeaf3 100644 --- a/packages/core/src/lib/wallet/types.ts +++ b/packages/core/src/lib/wallet/types.ts @@ -33,9 +33,16 @@ export type SupportedWallet = { windowKey: WalletWindowKey; }; +type GasPriceStep = { + low: number; + average: number; + high: number; +}; + export type ChainInfo = { chainName?: string; chainId?: string; restUrl?: string; rpcUrl?: string; + gasPriceStep?: GasPriceStep; }; diff --git a/packages/react/src/lib/assets/coin98.png b/packages/react/src/lib/assets/coin98.png deleted file mode 100644 index 7db2b3e2092e64ef13a41635be51c61968469283..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3550 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 Y0oJeI=)(FJr2qf`07*qoM6N<$f|_EFtN;K2 diff --git a/packages/react/src/lib/assets/default.svg b/packages/react/src/lib/assets/default.svg deleted file mode 100644 index 8e01c3bf..00000000 --- a/packages/react/src/lib/assets/default.svg +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/packages/react/src/lib/assets/falcon.png b/packages/react/src/lib/assets/falcon.png deleted file mode 100644 index ef255f43331976ef33f9e1c6f7ea59b8396dbdff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15423 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 diff --git a/packages/react/src/lib/assets/keplr.png b/packages/react/src/lib/assets/keplr.png deleted file mode 100644 index 07434574e3d61c8e6f3d4e1d3a64bd8d3b49fd20..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13328 zcmV+rH1EraP)cguY0Y%&;A6TfO7tRJ%>GRon*dMc6hpq+zQ1x5k0SvR*^q0pOuc; zcS-fO&*{4?l~wz;q0aKA()YKv^RL>DaW$@^_GdY`jbj6;PzQB97J~W=$Jb-|JLK|G z=koIL3g-Ej_lN1yTfST#z$c&xK2Bvd`ZUd7FFSd@LT_DqKIgql8dBoN&33tNHu?$m zHsf>GR2PUqG62};b^@^xqLEeu`BxoONXC^^WpsRk<0z^pfo~mquHPrf1M_@%?bSzi z_g#LDT*1eooEZR%;m=R|`DIH-FPvrdax}$O51A&`tu!N%$I|Kk^9i0ESwn`5k^yF) z*RPvmo71*JfUx~~Fi`e(dX4)YkF*O5OzU=hXl%4Fkk9G$fS}i76uJ1eFPFP<%5o|IY+f(= zQ_Jgu8+>4h;W1PrbjFg-BXhqDl#}}A?~(o`oEPpBgV;_7i|3V^#gogV3lNf5av$~k ze1b70g#e0}jbSJ>$#sB~`o26}ljpqU6l^fvXvL@9f9-a`;dhtmU!Y_cqj*-_ARR*$ zp3aqtX;CsMo%>Lw{xtT@=bUQSHZ!-Sj#jT7_Nl{{8CvRR8nt=@2xLm38TqK6b$LgO95J~-yD7*WN9vCMjgpvUv zNXaTQn11V9pR>CMCzKNb@U&mNcDwFzFH@-|PxbL1g&Bf^lffVDb1}HXVz?JDl7p4* zpUL_|R>+=-5`!JgDNJ~m+JELgN1h5$n6mcQ0}H6mni~jC4m0(;^|dg;9dCP%+=oFK z0$|Jb5A*%Y2Xq@-wP&jb&%#q5Qrno4xIgb7Q3loiJImLNgK>SIW1a)G zlVnU8=)(64ST|XBXdnpoQSbWP(&k1(# zQJ)(|NLXH`Db>rM*&1nUAaxw2l{p$r98>_VV`#0i1aBRHATRyQ$RJRNra97F zgDz_yZhU$ddSwv!xJh4=>aCyuONWa{C9ui)U--yE`TKXmvN53F>{_eY;yK80L3eK<4A$T0+ABUOYQTBPTOY< za%wtnuetU)Y4T>PKj?$mlh1AA%~5{#?#viGR`&j;kf`5o&U3KGx#@trK*jsoydSIg zV+d#Vs1?TMtxEN?2b)*s&vA4~-%qGNVjaav4&99vlCe&R(vif`VY`2ma39ipy`Ipk zjmhUVjv=G=gYUwk4(i%<2nQS0%}g~P5R5rJio@lg~-(dEJy_1~MjCIa##kH6uCg*qrCD(c9~# zOzq~-EbNTH#!mALY-DUz+XY6U&yy?{@@Nd4zgN%6r(ApI^6{K`yDcYzC#Lrnd;6_n z48C`tixZ@aHx!Rg${PlKgyW!b>Yoky>>1RhQsoevCD;SQIIlE+HLq6h!-V0nCT9^1 z(t3aOTgVkiuJw=9c3fZjE<5k9>~Q6#hvk+_cgU5(0K2QVukpb=O$qTFCxhqAl)YOf zPRQq_(2XpSLJTtnlb8B(LE&EN#+iH17wXGa6C6u1M|r}Ez`Y&AnB04Llg*gHm!F9p zr5e`s1!59@-mCR?$e!-{0O z&_7t#8glA0xn+O;-m4cjgi!}EULiCz5PQ5_y=V}FgC@{suqHp;hu4?J z3JU5!$LhKWyv*B zGRc!=_K>V_O?6VyGn0v#}>)14Rrao33VSXJ}}S zkipr;ZMGGeZ{5!-7Xn>Z%P`VSqHS|)#fIpqcqs_1Q72kGSrdIE;b~IF zOeW{ZvqQ)TeJONs#+4?0n<{1tJ#+8HRtJN2*Za;u2|%cfXu!8Q77uUjSNFxN-54do zD{W_Oz&D8(`#pWXRC~NBUa|oZW0?uqVoU={Gwh%-?Q`diAwAKfn`?8tm%0hH89#h4mGw1n|DR;oemBFM(I%w=6UA?T7Q4+>`0)&BPTs00NS;^4Ze)fTZYv3^j z@n=*fa-w8}63|o=b5uFHLLhR_t_C$v_W4u)++GI8&55F%0D#ghxuv#U+;us)U;dmA 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 a=kn*E_|JH>&9?pk0000o diff --git a/packages/react/src/lib/assets/leap.png b/packages/react/src/lib/assets/leap.png deleted file mode 100644 index 33dca9db07c726eab1a8285b7cf48230c9441279..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28169 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 diff --git a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx index 9e1a508e..d453ec75 100644 --- a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx +++ b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx @@ -1,113 +1,105 @@ -import React, { useContext, useState } from 'react'; -import { styles } from './styles'; +import React, { useContext, useEffect, useState } from 'react'; import OutsideClickHandler from 'react-outside-click-handler'; +import { IconContext } from 'react-icons'; +import { IoCopyOutline, IoLogOutOutline, IoWalletOutline } from 'react-icons/all'; import { SeiWalletContext } from '../../provider'; import { WalletSelectModal } from '../WalletSelectModal'; import { WalletConnectButtonProps } from './types'; +import './styles.css'; +import { isValidCSSColor } from '../../utils'; -export const truncateAddress = (address: string) => - `${address.slice(0, 3)}....${address.slice(address.length - 5)}`; +export const truncateAddress = (address: string) => `${address.slice(0, 3)}....${address.slice(address.length - 5)}`; -const WalletConnectButton = ({ - buttonStyles, - buttonClassName, - wallets, -}: WalletConnectButtonProps) => { - const [showMenu, setShowMenu] = useState(false); - const [recentlyCopied, setRecentlyCopied] = useState(false); - const [showConnectModal, setShowConnectModal] = useState(false); +const WalletConnectButton = ({ buttonClassName, wallets: inputWallets, primaryColor, secondaryColor, backgroundColor }: WalletConnectButtonProps) => { + const [showMenu, setShowMenu] = useState(false); + const [recentlyCopied, setRecentlyCopied] = useState(false); - const { connectedWallet, accounts, setInputWallet } = - useContext(SeiWalletContext); + const { connectedWallet, accounts, setTargetWallet, wallets, showConnectModal, setShowConnectModal } = useContext(SeiWalletContext); - const changeWallet = () => { - if (setInputWallet) setInputWallet(undefined); - setShowConnectModal(true); - setShowMenu(false); - }; + useEffect(() => { + const color = primaryColor && isValidCSSColor(primaryColor) ? primaryColor : '#121212'; + document.documentElement.style.setProperty('--wallet-primary-color', color); + document.documentElement.style.setProperty('--wallet-primary-color-11', `${color}11`); + document.documentElement.style.setProperty('--wallet-primary-color-22', `${color}22`); + document.documentElement.style.setProperty('--wallet-primary-color-33', `${color}33`); + document.documentElement.style.setProperty('--wallet-primary-color-44', `${color}44`); + }, [primaryColor]); - const copyAddress = async () => { - setRecentlyCopied(true); - await navigator.clipboard.writeText(accounts?.[0]?.address); - setTimeout(() => { - setRecentlyCopied(false); - }, 600); - }; + useEffect(() => { + const color = secondaryColor && isValidCSSColor(secondaryColor) ? secondaryColor : '#8C8C8C'; + document.documentElement.style.setProperty('--wallet-secondary-color', color); + }, [secondaryColor]); - const disconnect = () => { - if (setInputWallet) setInputWallet(undefined); - }; + useEffect(() => { + const color = backgroundColor && isValidCSSColor(backgroundColor) ? backgroundColor : '#F1F1F1'; + document.documentElement.style.setProperty('--wallet-background-color', color); + }, [backgroundColor]); - const renderButton = () => { - if (!connectedWallet) { - return ( - - ); - } + const changeWallet = () => { + setShowConnectModal(true); + setShowMenu(false); + }; - const accountLabel = - accounts?.[0] === undefined - ? 'connecting...' - : truncateAddress(accounts[0].address); + const copyAddress = async () => { + setRecentlyCopied(true); + await navigator.clipboard.writeText(accounts?.[0]?.address); + setTimeout(() => { + setRecentlyCopied(false); + }, 1500); + }; - return ( -
- - {showMenu && ( - setShowMenu(false)}> -
- {accounts && ( -

- - {recentlyCopied ? 'copied' : 'copy address'} - -

- )} -

- change wallet -

-

- disconnect -

-
-
- )} -
- ); - }; + const disconnect = () => { + if (setTargetWallet) setTargetWallet(undefined); + }; - return ( - <> - {renderButton()} - {showConnectModal && ( - - )} - - ); + const renderButton = () => { + if (!connectedWallet) { + return ( + + ); + } + + const accountLabel = accounts?.[0] === undefined ? 'connecting...' : truncateAddress(accounts[0].address); + + return ( +
+ + {showMenu && ( + setShowMenu(false)}> +
+ {accounts && ( +
+ + {recentlyCopied ? 'copied' : 'copy address'} +
+ )} +
+ + change wallet +
+
+ + disconnect +
+
+
+ )} +
+ ); + }; + + return ( + <> + + {renderButton()} + {showConnectModal && } + + + ); }; export default WalletConnectButton; diff --git a/packages/react/src/lib/components/WalletConnectButton/styles.css b/packages/react/src/lib/components/WalletConnectButton/styles.css new file mode 100644 index 00000000..77ce4a1c --- /dev/null +++ b/packages/react/src/lib/components/WalletConnectButton/styles.css @@ -0,0 +1,38 @@ +.connect_wrapper { + position: relative; +} + +.wallet__menu { + position: absolute; + display: flex; + flex-direction: column; + top: 46px; + right: 0; + padding: 12px; + gap: 12px; + background-color: var(--wallet-background-color); + border-radius: 4px; +} + +.wallet__menu--item { + display: flex; + align-items: center; + gap: 12px; + font-weight: 500; + font-size: 1.15rem; + white-space: nowrap; + color: var(--wallet-primary-color); + cursor: pointer; + border-radius: 6px; + padding: 9px; +} + +.wallet__menu--item:hover { + background-color: var(--wallet-primary-color-44); +} + +.wallet__menu--item-icon { + width: 20px; + height: 20px; + color: var(--wallet-primary-color); +} diff --git a/packages/react/src/lib/components/WalletConnectButton/styles.ts b/packages/react/src/lib/components/WalletConnectButton/styles.ts deleted file mode 100644 index 91d3c704..00000000 --- a/packages/react/src/lib/components/WalletConnectButton/styles.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { CSSProperties } from 'react'; - -export const styles: { [key: string]: CSSProperties } = { - wrapper: { - position: 'relative', - }, - menu: { - position: 'absolute', - display: 'flex', - flexDirection: 'column', - top: 46, - right: 0, - padding: 18, - gap: 12, - backgroundColor: '#222222', - borderRadius: 4, - }, - menuItem: { - fontWeight: 'bold', - fontSize: '1.15rem', - whiteSpace: 'nowrap', - color: '#d1d1d1', - cursor: 'pointer', - }, -}; diff --git a/packages/react/src/lib/components/WalletConnectButton/types.ts b/packages/react/src/lib/components/WalletConnectButton/types.ts index 3cff3a7c..b5e7e2df 100644 --- a/packages/react/src/lib/components/WalletConnectButton/types.ts +++ b/packages/react/src/lib/components/WalletConnectButton/types.ts @@ -1,14 +1,9 @@ -import { CSSProperties } from 'react'; -import { WalletSelectStyles } from '../WalletSelectModal/types'; -import { WalletWindowKey } from '@sei-js/core'; +import { SeiWallet } from '../../provider'; export type WalletConnectButtonProps = { - buttonClassName?: string; - wallets?: WalletWindowKey[]; - buttonStyles?: { - button?: CSSProperties; - menu?: CSSProperties; - menuItem?: CSSProperties; - walletSelectStyles?: WalletSelectStyles; - }; + buttonClassName?: string; + wallets?: SeiWallet[]; + primaryColor?: string; + secondaryColor?: string; + backgroundColor?: string; }; diff --git a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx index c972efbe..a43cd447 100644 --- a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx +++ b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx @@ -1,91 +1,137 @@ -import React, { useContext } from 'react'; -import OutsideClickHandler from 'react-outside-click-handler'; -import { WalletWindowKey } from '@sei-js/core'; +import React, { useContext, useEffect, useState } from 'react'; import { WalletSelectModalProps } from './types'; -import { styles } from './styles'; -import { SeiWalletContext } from '../../provider'; +import { SeiWallet, SeiWalletContext } from '../../provider'; +import './styles.css'; +import { AiFillCloseCircle } from 'react-icons/ai'; +import { BiErrorAlt, FaCheckCircle } from 'react-icons/all'; +import { BiError } from 'react-icons/bi'; +import { Link } from 'react-router-dom'; -// TODO: Refactor this to a separate assets repo -// Wallet logos -import coin98Logo from '../../assets/coin98.png'; -import falconLogo from '../../assets/falcon.png'; -import keplrLogo from '../../assets/keplr.png'; -import leapLogo from '../../assets/leap.png'; -import defaultIcon from '../../assets/default.svg'; -const getWalletIcon = (wallet: WalletWindowKey) => { - if (wallet === 'coin98') { - return coin98Logo; - } else if (wallet === 'falcon') { - return falconLogo; - } else if (wallet === 'keplr') { - return keplrLogo; - } else if (wallet === 'leap') { - return leapLogo; - } else { - return defaultIcon; - } -}; +const WalletSelectModal = ({ setShowConnectModal, wallets }: WalletSelectModalProps) => { + const { connectedWallet, setTargetWallet, connectionError, targetWallet, setConnectionError } = useContext(SeiWalletContext); + + const [isConnecting, setIsConnecting] = useState(false); + + const closeModal = () => { + setConnectionError(undefined); + setShowConnectModal(false); + }; + + const renderWallet = (wallet: SeiWallet) => { + const isConnectedWallet = connectedWallet?.walletInfo.name === wallet.walletInfo.name; + + const renderConnection = () => { + if (isConnectedWallet) return ; + return null; + }; + + const selectWallet = async () => { + if (wallet.walletInfo.name === targetWallet?.walletInfo.name) return; + if (setTargetWallet) setTargetWallet(wallet); + setIsConnecting(true); + setConnectionError(undefined); + }; + + useEffect(() => { + if (connectedWallet || connectionError) { + setIsConnecting(false); + } + }, [connectedWallet, connectionError]); + + return ( +
+
+ {wallet.walletInfo.name} +

{wallet.walletInfo.name}

+
+ {renderConnection()} +
+ ); + }; + + const renderRightSide = () => { + if (isConnecting) { + return ( +
+ {targetWallet?.walletInfo.icon} +

Connecting to {targetWallet?.walletInfo.name}...

+
+ ); + } + + const isWalletNotInstalled = targetWallet && !window[targetWallet.walletInfo.windowKey]; -const WalletSelectModal = ({ - setShowConnectModal, - inputWallets, - walletSelectStyles, -}: WalletSelectModalProps) => { - const { - installedWallets, - connectedWallet, - setInputWallet, - supportedWallets, - } = useContext(SeiWalletContext); + if (isWalletNotInstalled) { + return ( +
+ +

{targetWallet?.walletInfo?.name || 'Wallet'} not installed

+ {targetWallet?.walletInfo.website && ( + + Download {targetWallet?.walletInfo.name} + + )} +
+ ); + } - const wallets = inputWallets || supportedWallets; + if (connectionError) { + return ( +
+ +

We couldn't connect to {targetWallet?.walletInfo?.name || 'your wallet'}

+
+

How to resolve this issue?

+

A pending action or a locked wallet can cause issues. Please open the extension manually and try again.

+
+
+ ); + } - const renderWallet = (wallet: WalletWindowKey) => { - const renderConnection = () => { - if (connectedWallet === wallet) return

connected

; - if (installedWallets.includes(wallet)) return

detected

; - return null; - }; + if (connectedWallet) { + return ( +
+ {targetWallet?.walletInfo.icon} +

Connected to {targetWallet?.walletInfo.name}

+
+ ); + } - const selectWallet = () => { - if (setInputWallet) setInputWallet(wallet); - setShowConnectModal(false); - }; + return ( +
+

New to using a wallet?

+
+

A Secure Hub for Digital Transactions

+

Wallets provide a secure environment for signing and sending transactions involving your tokens and NFTs.

+
+
+

A modern way to log in

+

Rather than generating new accounts and passwords for each website, simply link your wallet.

+
+
+ ); + }; - return ( -
-
- {wallet} -

- {wallet} -

-
- {renderConnection()} -
- ); - }; - return ( -
- { - e.stopPropagation(); - setShowConnectModal(false); - }} - > -
-

Connect your wallet

-
- {wallets.map(renderWallet)} -
-
-
- ); + return ( +
+
e.stopPropagation()} className='modal__card'> +
+

Connect a wallet

+ +
+
+
{wallets?.map(renderWallet)}
+
+ {renderRightSide()} +
+
+
+ ); }; export default WalletSelectModal; diff --git a/packages/react/src/lib/components/WalletSelectModal/styles.css b/packages/react/src/lib/components/WalletSelectModal/styles.css new file mode 100644 index 00000000..859dd661 --- /dev/null +++ b/packages/react/src/lib/components/WalletSelectModal/styles.css @@ -0,0 +1,235 @@ +@keyframes fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +:root { + --wallet-primary-color: #121212; + --wallet-primary-color-11: #12121211; + --wallet-primary-color-22: #12121222; + --wallet-primary-color-33: #12121233; + --wallet-primary-color-44: #12121244; + --wallet-secondary-color: #8C8C8C; + --wallet-background-color: #F1F1F1; +} + +.modal__background { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + backdrop-filter: blur(3px); + background-color: #f1f1f133; + z-index: 2; + padding: 24px; +} + +.modal__card { + display: flex; + flex-direction: column; + background-color: var(--wallet-background-color); + border-radius: 12px; + z-index: 10; + padding: 24px; + max-height: 100%; + width: 100%; + max-width: 720px; + min-height: 440px; + box-shadow: rgba(14, 15, 16, 0.1) 3px 9px 24px; + overflow-x: hidden; + gap: 48px; + animation: fadeIn 150ms linear; +} + +.card__header { + display: flex; + min-height: 26px; + height: 26px; + width: 100%; + justify-content: space-between; +} + +.card__header--title { + color: var(--wallet-primary-color); +} + +.card__header--close { + transition: 70ms all ease-out; + color: var(--wallet-primary-color); + width: 26px; + height: 26px; + cursor: pointer; +} + +.card__header--close:hover { + transform: scale(1.2); +} + +.card__header--close:active { + opacity: 0.75; +} + +.card__content { + display: flex; + flex-direction: row; + flex: 1; + gap: 24px; + margin-bottom: 26px; +} + +.card__content--wallets { + display: flex; + flex-direction: column; + gap: 12px; + width: 280px; + max-width: 280px; + min-width: 280px; +} + +.card__content--separator { + height: 100%; + width: 2px; + border-radius: 2px; + background-color: var(--wallet-primary-color-22); +} + +.card__right { + display: flex; + flex-direction: column; + gap: 24px; + align-items: center; + margin-bottom: 48px; + width: 100%; + padding: 0 36px 0 24px; +} + +.card__right-centered { + display: flex; + flex-direction: column; + justify-content: center; + gap: 24px; + align-items: center; + width: 100%; + padding: 0 36px 0 24px; + opacity: 0; + animation: fadeIn 0.15s 0.3s forwards; +} + +.card__right--item { + display: flex; + flex-direction: column; + gap: 3px; + max-width: 300px; +} + +.card__right--item-title { + color: var(--wallet-primary-color); + font-weight: 600; +} + +.card__right--item-description { + color: var(--wallet-secondary-color); +} + +.card__right--icon{ + color: var(--wallet-primary-color); + width: 72px; + height: 72px; +} + +.card__right--title { + color: var(--wallet-primary-color); + text-align: center; + font-weight: 600; + font-size: 1.25rem; + margin-bottom: 24px; +} + +.card__right--download { + padding: 0.5rem 1rem; + border-radius: 30px; + background-color: var(--wallet-primary-color); + color: var(--wallet-background-color); + font-weight: 700; + transition: 50ms all ease-out; +} + +.card__right--download:hover { + opacity: 0.8; +} + +.card__right--download:active { + opacity: 1; +} + +.card__right--connecting-icon { + width: 48px; + height: 48px; + max-width: 48px; + max-height: 48px; + min-width: 48px; + min-height: 48px; + border-radius: 12px; +} + +.wallet__item { + padding: 0 12px; + justify-content: space-between; + align-items: center; + border-radius: 6px; + border: transparent solid 2px; + transition: 50ms all ease-out; + user-select: none; +} + +.wallet__item:active { + opacity: 0.75; +} + +.wallet__item:hover { + background-color: var(--wallet-primary-color-11); + cursor: pointer; +} + +.wallet__item-connected { + background-color: var(--wallet-primary-color-11); +} + +.wallet__item-targeted { + border: var(--wallet-primary-color-33) solid 2px !important; +} + +.wallet__item--info { + display: flex; + flex-direction: row; + align-items: center; + gap: 12px; + padding: 0.5rem; +} + +.wallet__item--info-icon { + border-radius: 50%; + width: 24px; + height: 24px; +} + +.wallet__item--info-name { + color: var(--wallet-primary-color); + text-transform: uppercase; + font-size: 1.25rem; +} + +.wallet__item--info-icon { + color: var(--wallet-primary-color-44); + height: 18px; + width: 18px; +} diff --git a/packages/react/src/lib/components/WalletSelectModal/styles.ts b/packages/react/src/lib/components/WalletSelectModal/styles.ts deleted file mode 100644 index a10b8ed4..00000000 --- a/packages/react/src/lib/components/WalletSelectModal/styles.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { CSSProperties } from 'react'; - -export const styles: { [key: string]: CSSProperties } = { - background: { - position: 'absolute', - top: 0, - right: 0, - bottom: 0, - left: 0, - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - backdropFilter: 'blur(3px)', - backgroundColor: '#f1f1f133', - zIndex: 2, - padding: 24, - }, - card: { - flexDirection: 'column', - backgroundColor: 'black', - borderRadius: 12, - zIndex: 10, - padding: 24, - maxHeight: '100%', - width: '100%', - maxWidth: 360, - boxShadow: 'rgba(14, 15, 16, 0.1) 3px 9px 24px', - overflowX: 'hidden', - gap: 12, - animation: 'fadeIn 150ms linear', - }, - row: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - gap: 60, - cursor: 'pointer', - padding: 12, - borderRadius: 3, - }, - info: { - flexDirection: 'row', - alignItems: 'center', - gap: 12, - }, - icon: { - borderRadius: '50%', - }, - name: { - textTransform: 'uppercase', - fontSize: '18pt', - }, -}; diff --git a/packages/react/src/lib/components/WalletSelectModal/types.ts b/packages/react/src/lib/components/WalletSelectModal/types.ts index d05f52e8..1f069c1e 100644 --- a/packages/react/src/lib/components/WalletSelectModal/types.ts +++ b/packages/react/src/lib/components/WalletSelectModal/types.ts @@ -1,14 +1,6 @@ -import { WalletWindowKey } from '@sei-js/core'; -import { CSSProperties } from 'react'; - -export type WalletSelectStyles = { - background?: CSSProperties; - card?: CSSProperties; - name?: CSSProperties; -}; +import { SeiWallet } from '../../provider'; export type WalletSelectModalProps = { setShowConnectModal: (show: boolean) => void; - inputWallets?: WalletWindowKey[]; - walletSelectStyles?: WalletSelectStyles; + wallets?: SeiWallet[]; }; diff --git a/packages/react/src/lib/config/supportedWallets.ts b/packages/react/src/lib/config/supportedWallets.ts new file mode 100644 index 00000000..38dc4c97 --- /dev/null +++ b/packages/react/src/lib/config/supportedWallets.ts @@ -0,0 +1,72 @@ +import { SeiWallet } from '../provider'; + +export const FIN_WALLET: SeiWallet = { + getAccounts: async (chainId) => await window?.['fin']?.getOfflineSigner(chainId).getAccounts(), + connect: async (chainId) => await window?.['fin']?.enable(chainId), + disconnect: async (chainId) => await window?.['fin']?.disable(chainId), + getOfflineSigner: async (chainId) => window?.['fin']?.getOfflineSigner(chainId), + signArbitrary: window?.['fin']?.signArbitrary, + walletInfo: { + windowKey: 'fin', + name: 'Fin', + website: 'https://chrome.google.com/webstore/detail/fin-wallet-for-sei/dbgnhckhnppddckangcjbkjnlddbjkna', + icon: 'https://sei-js-assets.s3.us-west-2.amazonaws.com/fin.png' + } +}; + +export const COMPASS_WALLET: SeiWallet = { + getAccounts: async (chainId) => await window['compass'].getOfflineSigner(chainId).getAccounts(), + connect: async (chainId) => await window['compass'].enable(chainId), + disconnect: async (chainId) => await window['compass'].disable(chainId), + getOfflineSigner: async (chainId) => window['compass'].getOfflineSigner(chainId), + signArbitrary: window['compass'].signArbitrary, + walletInfo: { + windowKey: 'compass', + name: 'Compass', + website: 'https://chrome.google.com/webstore/detail/compass-wallet/anokgmphncpekkhclmingpimjmcooifb', + icon: 'https://sei-js-assets.s3.us-west-2.amazonaws.com/compass.png' + } +}; + +export const KEPLR_WALLET: SeiWallet = { + getAccounts: async (chainId) => await window?.['keplr']?.getOfflineSigner(chainId).getAccounts(), + connect: async (chainId) => await window?.['keplr']?.enable(chainId), + disconnect: async (chainId) => await window?.['keplr']?.disable(chainId), + getOfflineSigner: async (chainId) => window?.['keplr']?.getOfflineSigner(chainId), + signArbitrary: window['keplr'].signArbitrary, + walletInfo: { + windowKey: 'keplr', + name: 'Keplr', + website: 'https://www.keplr.app/download', + icon: 'https://sei-js-assets.s3.us-west-2.amazonaws.com/keplr.png' + } +}; + +export const LEAP_WALLET: SeiWallet = { + getAccounts: async (chainId) => await window?.['leap']?.getOfflineSigner(chainId).getAccounts(), + connect: async (chainId) => await window?.['leap']?.enable(chainId), + disconnect: async (chainId) => await window?.['leap']?.disable(chainId), + getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSigner(chainId), + signArbitrary: window['leap'].signArbitrary, + walletInfo: { + windowKey: 'leap', + name: 'Leap', + website: 'https://www.leapwallet.io/download', + icon: 'https://sei-js-assets.s3.us-west-2.amazonaws.com/leap.png' + } +}; + +export const findWalletByWindowKey = (windowKey: string): SeiWallet | undefined => { + switch (windowKey) { + case 'compass': + return COMPASS_WALLET; + case 'leap': + return LEAP_WALLET; + case 'keplr': + return KEPLR_WALLET; + case 'fin': + return FIN_WALLET; + default: + return undefined; + } +}; diff --git a/packages/react/src/lib/hooks/useCosmWasmClient/index.ts b/packages/react/src/lib/hooks/useCosmWasmClient/index.ts index ad9817d0..c37104b7 100644 --- a/packages/react/src/lib/hooks/useCosmWasmClient/index.ts +++ b/packages/react/src/lib/hooks/useCosmWasmClient/index.ts @@ -1,4 +1,2 @@ -export { - default as useCosmWasmClient, - UseCosmWasmClient, -} from './useCosmWasmClient'; +export { default as useCosmWasmClient } from './useCosmWasmClient'; +export type { UseCosmWasmClient } from './useCosmWasmClient'; diff --git a/packages/react/src/lib/hooks/useQueryClient/index.ts b/packages/react/src/lib/hooks/useQueryClient/index.ts index 70473568..9684a60f 100644 --- a/packages/react/src/lib/hooks/useQueryClient/index.ts +++ b/packages/react/src/lib/hooks/useQueryClient/index.ts @@ -1,5 +1,2 @@ -export { - default as useQueryClient, - QueryClient, - UseQueryClient, -} from './useQueryClient'; +export type { QueryClient, UseQueryClient } from './useQueryClient'; +export { default as useQueryClient } from './useQueryClient'; diff --git a/packages/react/src/lib/hooks/useSelectWallet/index.ts b/packages/react/src/lib/hooks/useSelectWallet/index.ts new file mode 100644 index 00000000..5c8a702c --- /dev/null +++ b/packages/react/src/lib/hooks/useSelectWallet/index.ts @@ -0,0 +1,2 @@ +export { default as useSelectWallet } from './useSelectWallet'; +export type { UseSelectWallet } from './useSelectWallet'; diff --git a/packages/react/src/lib/hooks/useSelectWallet/useSelectWallet.ts b/packages/react/src/lib/hooks/useSelectWallet/useSelectWallet.ts new file mode 100644 index 00000000..bea55af6 --- /dev/null +++ b/packages/react/src/lib/hooks/useSelectWallet/useSelectWallet.ts @@ -0,0 +1,14 @@ +import { useContext } from 'react'; +import { SeiWalletContext } from '../../provider'; + +const useSelectWallet = () => { + const { setShowConnectModal } = useContext(SeiWalletContext); + + const openModal = () => setShowConnectModal(true); + + const closeModal = () => setShowConnectModal(false); + + return { openModal, closeModal }; +}; + +export default useSelectWallet; diff --git a/packages/react/src/lib/hooks/useSigningClient/index.ts b/packages/react/src/lib/hooks/useSigningClient/index.ts index 6e0cd8a3..cd150781 100644 --- a/packages/react/src/lib/hooks/useSigningClient/index.ts +++ b/packages/react/src/lib/hooks/useSigningClient/index.ts @@ -1,4 +1,2 @@ -export { - default as useSigningClient, - UseSigningClient, -} from './useSigningClient'; +export { default as useSigningClient } from './useSigningClient'; +export type { UseSigningClient } from './useSigningClient'; diff --git a/packages/react/src/lib/hooks/useSigningCosmWasmClient/index.ts b/packages/react/src/lib/hooks/useSigningCosmWasmClient/index.ts index abb81a49..32ec7e89 100644 --- a/packages/react/src/lib/hooks/useSigningCosmWasmClient/index.ts +++ b/packages/react/src/lib/hooks/useSigningCosmWasmClient/index.ts @@ -1,4 +1,2 @@ -export { - default as useSigningCosmWasmClient, - UseSigningCosmWasmClient, -} from './useSigningCosmWasmClient'; +export { default as useSigningCosmWasmClient } from './useSigningCosmWasmClient'; +export type { UseSigningCosmWasmClient } from './useSigningCosmWasmClient'; diff --git a/packages/react/src/lib/hooks/useStargateClient/index.ts b/packages/react/src/lib/hooks/useStargateClient/index.ts index fc9a18c2..e6783f1f 100644 --- a/packages/react/src/lib/hooks/useStargateClient/index.ts +++ b/packages/react/src/lib/hooks/useStargateClient/index.ts @@ -1,4 +1,2 @@ -export { - default as useStargateClient, - UseStargateClient, -} from './useStargateClient'; +export { default as useStargateClient } from './useStargateClient'; +export type { UseStargateClient } from './useStargateClient'; diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index 8659cb53..901c2185 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -1,120 +1,92 @@ -import React, { - createContext, - useCallback, - useEffect, - useMemo, - useState, -} from 'react'; -import { SeiWalletProviderProps, WalletProvider } from './types'; -import { - WalletWindowKey, - connect as connectWallet, - SUPPORTED_WALLETS, - suggestChain, -} from '@sei-js/core'; +import React, { createContext, useEffect, useState } from 'react'; import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; - -const supportedWallets = SUPPORTED_WALLETS.map((wallet) => wallet.windowKey); +import { SeiWallet, SeiWalletProviderProps, WalletProvider } from './types'; +import { findWalletByWindowKey } from '../config/supportedWallets'; export const SeiWalletContext = createContext({ - supportedWallets, - accounts: [], - installedWallets: [], - connect: () => undefined, - disconnect: () => undefined, + accounts: [], + connect: () => undefined, + disconnect: () => undefined, + setConnectionError: () => undefined, + setTargetWallet: () => undefined, + setShowConnectModal: () => undefined, + wallets: [] }); -const SeiWalletProvider = ({ - children, - chainConfiguration, -}: SeiWalletProviderProps) => { - const [inputWallet, setInputWallet] = useState(); - const [offlineSigner, setOfflineSigner] = useState(); - const [accounts, setAccounts] = useState([]); - const [connectedWallet, setConnectedWallet] = useState< - WalletWindowKey | undefined - >(); +const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect }: SeiWalletProviderProps) => { + const autoConnectSeiWallet = typeof autoConnect === 'string' ? findWalletByWindowKey(autoConnect) : autoConnect; + + const [targetWallet, setTargetWallet] = useState(autoConnectSeiWallet); + const [offlineSigner, setOfflineSigner] = useState(); + const [connectionError, setConnectionError] = useState(); + const [accounts, setAccounts] = useState([]); + const [showConnectModal, setShowConnectModal] = useState(false); + const [connectedWallet, setConnectedWallet] = useState(); + + const filteredWallets = wallets.reduce((acc: SeiWallet[], wallet) => { + let seiWallet = typeof wallet === 'string' ? findWalletByWindowKey(wallet) : wallet; + if (seiWallet !== undefined) acc.push(seiWallet); + return acc; + }, []); - const suggestAndConnect = useCallback(async () => { - if (!inputWallet) return; - if (inputWallet === 'keplr' || inputWallet === 'leap') { - await suggestChain(inputWallet, { - chainName: `Sei ${chainConfiguration.chainId}`, - chainId: chainConfiguration.chainId, - rpcUrl: chainConfiguration.rpcUrl, - restUrl: chainConfiguration.restUrl, - }); - } + const connectToChain = async () => { + if (!targetWallet) return; - const connectedWallet = await connectWallet( - inputWallet, - chainConfiguration.chainId - ); - if (!connectedWallet) return; - setOfflineSigner(connectedWallet.offlineSigner); - setAccounts(connectedWallet.accounts); - if (connectedWallet.accounts.length > 0) { - setConnectedWallet(inputWallet); - } - }, [ - inputWallet, - chainConfiguration.chainId, - chainConfiguration.restUrl, - chainConfiguration.rpcUrl, - ]); + try { + if (!window[targetWallet.walletInfo.windowKey]) { + setConnectionError(targetWallet.walletInfo.windowKey); + return; + } - useEffect(() => { - if (inputWallet) { - suggestAndConnect().then(() => setConnectedWallet(inputWallet)); - } else { - setConnectedWallet(undefined); - setAccounts([]); - setOfflineSigner(undefined); - } - }, [ - inputWallet, - chainConfiguration.chainId, - chainConfiguration.restUrl, - chainConfiguration.rpcUrl, - ]); + const fetchedOfflineSigner = await targetWallet.getOfflineSigner(chainConfiguration.chainId); + const fetchedAccounts = await targetWallet.getAccounts(chainConfiguration.chainId); - const installedWallets = useMemo( - () => - window - ? SUPPORTED_WALLETS.filter((wallet) => window?.[wallet.windowKey]).map( - (wallet) => wallet.windowKey - ) - : [], - [window] - ); + if (fetchedAccounts.length > 0 && fetchedOfflineSigner) { + setShowConnectModal(false); + setOfflineSigner(fetchedOfflineSigner); + setAccounts(fetchedAccounts); + setConnectedWallet(targetWallet); + } + } catch (e) { + console.log('Error connecting to wallet', e); + setConnectionError(targetWallet.walletInfo.windowKey); + return; + } + }; - const connect = (walletKey: WalletWindowKey) => { - setInputWallet(walletKey); - }; + useEffect(() => { + if (targetWallet) { + connectToChain().then(); + } else { + setOfflineSigner(undefined); + setAccounts([]); + setConnectedWallet(undefined); + } + }, [targetWallet, chainConfiguration.chainId]); - const disconnect = () => { - setInputWallet(undefined); - }; + const disconnect = () => { + setTargetWallet(undefined); + }; - const contextValue: WalletProvider = { - chainId: chainConfiguration.chainId, - restUrl: chainConfiguration.restUrl, - rpcUrl: chainConfiguration.rpcUrl, - supportedWallets, - accounts, - offlineSigner, - connectedWallet, - installedWallets, - setInputWallet, - connect, - disconnect, - }; + const contextValue: WalletProvider = { + chainId: chainConfiguration.chainId, + restUrl: chainConfiguration.restUrl, + rpcUrl: chainConfiguration.rpcUrl, + wallets: filteredWallets, + connect: setTargetWallet, + disconnect, + accounts, + offlineSigner, + connectedWallet, + targetWallet, + setTargetWallet, + showConnectModal, + setShowConnectModal, + connectionError, + setConnectionError + }; - return ( - - {children} - - ); + return {children}; }; export default SeiWalletProvider; diff --git a/packages/react/src/lib/provider/types.ts b/packages/react/src/lib/provider/types.ts index 5d0b119b..cf9c221c 100644 --- a/packages/react/src/lib/provider/types.ts +++ b/packages/react/src/lib/provider/types.ts @@ -1,23 +1,46 @@ -import { WalletWindowKey } from '@sei-js/core'; import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; -import { ReactNode } from 'react'; +import { Dispatch, ReactNode, SetStateAction } from 'react'; import { ChainConfiguration } from '../types'; +import { StdSignature } from '@cosmjs/amino'; export type WalletProvider = { - chainId?: string; - restUrl?: string; - rpcUrl?: string; - supportedWallets: WalletWindowKey[]; - accounts: readonly AccountData[]; - offlineSigner?: OfflineSigner; - connectedWallet?: WalletWindowKey; - installedWallets: WalletWindowKey[]; - setInputWallet?: (inputWallet: WalletWindowKey | undefined) => void; - connect: (walletKey: WalletWindowKey) => void; - disconnect: () => void; + chainId?: string; + restUrl?: string; + rpcUrl?: string; + connectionError?: string; + setConnectionError: Dispatch>; + accounts: readonly AccountData[]; + offlineSigner?: OfflineSigner; + connectedWallet?: SeiWallet; + targetWallet?: SeiWallet; + setTargetWallet: Dispatch>; + showConnectModal?: boolean; + setShowConnectModal: Dispatch>; + connect: (walletKey: SeiWallet) => void; + wallets: SeiWallet[]; + disconnect: () => void; }; +export type SupportedWalletInput = 'compass' | 'leap' | 'fin' | 'keplr' | SeiWallet; + +export interface SeiWallet { + walletInfo: { + windowKey: string; + name: string; + icon: string; + website: string; + }; + getOfflineSigner: (chainId: string) => Promise; + getAccounts: (chainId: string) => Promise; + connect: (chainId: string) => Promise; + disconnect: (chainId: string) => Promise; + suggestChain?: (chainId: string) => void; + signArbitrary: (chainId: string, signer: string, message: string) => Promise; +} + export type SeiWalletProviderProps = { - children: ReactNode; - chainConfiguration: ChainConfiguration; + children: ReactNode; + chainConfiguration: ChainConfiguration; + wallets: SupportedWalletInput[]; + autoConnect: SupportedWalletInput; }; diff --git a/packages/react/src/lib/utils/css.ts b/packages/react/src/lib/utils/css.ts new file mode 100644 index 00000000..6c75b270 --- /dev/null +++ b/packages/react/src/lib/utils/css.ts @@ -0,0 +1,6 @@ +export const isValidCSSColor = (color) => { + //validate hex + const s = new Option().style; + s.color = color; + return s.color !== '' && color.startsWith('#'); +}; diff --git a/packages/react/src/lib/utils/index.ts b/packages/react/src/lib/utils/index.ts index d61b7aad..8359c082 100644 --- a/packages/react/src/lib/utils/index.ts +++ b/packages/react/src/lib/utils/index.ts @@ -1 +1,2 @@ +export * from './css'; export * from './shouldUseTm34Client'; From 9e3d4563d35db68cb853da7157fbff3edf36aff8 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Thu, 8 Jun 2023 21:50:57 -0700 Subject: [PATCH 02/11] -Updated README -Removed falcon and coin98 -Fixed typescript errors -Fixed styles on basic apps without global styles --- packages/core/src/lib/wallet/connect.ts | 6 +- packages/core/src/lib/wallet/types.ts | 1 + packages/react/README.md | 69 ++++++++++++++++++- .../WalletConnectButton.tsx | 8 +-- .../WalletSelectModal/WalletSelectModal.tsx | 33 +++++---- .../components/WalletSelectModal/styles.css | 14 ++++ .../lib/components/WalletSelectModal/types.ts | 1 - .../react/src/lib/config/supportedWallets.ts | 33 +++++---- .../src/lib/provider/SeiWalletProvider.tsx | 6 +- packages/react/src/lib/provider/types.ts | 8 +-- 10 files changed, 136 insertions(+), 43 deletions(-) diff --git a/packages/core/src/lib/wallet/connect.ts b/packages/core/src/lib/wallet/connect.ts index ee677eec..21ca0ff5 100644 --- a/packages/core/src/lib/wallet/connect.ts +++ b/packages/core/src/lib/wallet/connect.ts @@ -3,10 +3,8 @@ import { WalletConnect, WalletWindowInterface, WalletWindowKey } from './types'; declare global { interface Window { - coin98?: { - keplr: KeplrWindow; - }; - falcon?: WalletWindowInterface; + compass?: WalletWindowInterface; + fin?: WalletWindowInterface; keplr?: KeplrWindow; leap?: WalletWindowInterface; } diff --git a/packages/core/src/lib/wallet/types.ts b/packages/core/src/lib/wallet/types.ts index a8deeaf3..7864d15d 100644 --- a/packages/core/src/lib/wallet/types.ts +++ b/packages/core/src/lib/wallet/types.ts @@ -3,6 +3,7 @@ import { OfflineSigner, AccountData } from '@cosmjs/proto-signing'; export type WalletWindowInterface = { enable: (chainId: string) => Promise; + disable: (chainId: string) => Promise; getOfflineSigner: (chainId: string) => Promise; // Will return a signer that only supports Amino if the account is a Ledger-based account, // and returns a signer that is compatible for both Amino and Protobuf otherwise diff --git a/packages/react/README.md b/packages/react/README.md index 874b7523..9affc303 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -96,10 +96,77 @@ const { cosmWasmClient } = useSeiCosmWasmClient(); |----------------|-----------------|-----------------------------------------| | cosmWasmClient | CosmWasmClient? | A cosm wasm client for smart contracts. | +# UI Components +This package contains two helpful UI components for connecting to a wallet provider. + +## WalletConnectButton +This component renders a button that will open a modal to connect to a wallet provider. + +```javascript +import React from "react"; +import {useWallet, WalletConnectButton} from "../lib"; + +const Component = () => { + const { connectedWallet } = useWallet() + + return
+ +

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

+
+}; + +export default Component; + +``` + + +| Property | Type | Description | +|-----------------|--------------|-----------------------------------------------------------------------------| +| wallets | SeiWallet[]? | A stargate signing client. | +| buttonClassName | string | A css class name for styling the button | +| primaryColor | string | A hex value of the color you want to tint the text and icons with | +| secondaryColor | string | A secondary hex value of the color you want to tint the text and icons with | +| backgroundColor | string | A hex value of the color you want to use as a background | + +*If your page has a on the page it can be opened programmatically by calling the hook "useSelectWallet"* + + +## WalletSelectModal +This component renders a modal that will allow you to select a wallet provider and has a hook "useSelectWallet" that will allow you to open and close the modal programmatically. +```javascript +import React from "react"; +import {useWallet, WalletSelectModal} from "../lib"; + +const Component = () => { + const { connectedWallet } = useWallet() + const { openModal } = useSelectWallet() + + return
+ +

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

+ + { /** You must include the with the 'useSelectWallet' hook, unless you already have a rendered somewhere else in your app **/ } + +
+}; + +export default Component; + +``` + + +| Property | Type | Description | +|-----------------|--------------|-----------------------------------------------------------------------------| +| wallets | SeiWallet[]? | A stargate signing client. | +| buttonClassName | string | A css class name for styling the button | +| primaryColor | string | A hex value of the color you want to tint the text and icons with | +| secondaryColor | string | A secondary hex value of the color you want to tint the text and icons with | +| backgroundColor | string | A hex value of the color you want to use as a background | + ### Other helpful packages - [@sei-js/core](https://www.npmjs.com/package/@sei-js/core) - TypeScript library containing helper functions for wallet connection, transaction sig ning, and RPC querying. -- [@sei-js/proto](https://www.npmjs.com/package/@sei-js/proto) - TypeScript library for Sei protobufs generated using Telescope \ No newline at end of file +- [@sei-js/proto](https://www.npmjs.com/package/@sei-js/proto) - TypeScript library for Sei protobufs generated using Telescope diff --git a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx index d453ec75..af61e20a 100644 --- a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx +++ b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx @@ -1,12 +1,12 @@ -import React, { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import OutsideClickHandler from 'react-outside-click-handler'; import { IconContext } from 'react-icons'; -import { IoCopyOutline, IoLogOutOutline, IoWalletOutline } from 'react-icons/all'; import { SeiWalletContext } from '../../provider'; import { WalletSelectModal } from '../WalletSelectModal'; import { WalletConnectButtonProps } from './types'; import './styles.css'; import { isValidCSSColor } from '../../utils'; +import {IoCopyOutline, IoLogOutOutline, IoWalletOutline} from "react-icons/io5"; export const truncateAddress = (address: string) => `${address.slice(0, 3)}....${address.slice(address.length - 5)}`; @@ -14,7 +14,7 @@ const WalletConnectButton = ({ buttonClassName, wallets: inputWallets, primaryCo const [showMenu, setShowMenu] = useState(false); const [recentlyCopied, setRecentlyCopied] = useState(false); - const { connectedWallet, accounts, setTargetWallet, wallets, showConnectModal, setShowConnectModal } = useContext(SeiWalletContext); + const { connectedWallet, accounts, setTargetWallet, wallets, setShowConnectModal } = useContext(SeiWalletContext); useEffect(() => { const color = primaryColor && isValidCSSColor(primaryColor) ? primaryColor : '#121212'; @@ -96,7 +96,7 @@ const WalletConnectButton = ({ buttonClassName, wallets: inputWallets, primaryCo <> {renderButton()} - {showConnectModal && } + ); diff --git a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx index a43cd447..cc5491d0 100644 --- a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx +++ b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx @@ -1,17 +1,25 @@ -import React, { useContext, useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { WalletSelectModalProps } from './types'; import { SeiWallet, SeiWalletContext } from '../../provider'; import './styles.css'; import { AiFillCloseCircle } from 'react-icons/ai'; -import { BiErrorAlt, FaCheckCircle } from 'react-icons/all'; -import { BiError } from 'react-icons/bi'; +import {BiError, BiErrorAlt} from 'react-icons/bi'; import { Link } from 'react-router-dom'; +import {FaCheckCircle} from "react-icons/fa"; -const WalletSelectModal = ({ setShowConnectModal, wallets }: WalletSelectModalProps) => { - const { connectedWallet, setTargetWallet, connectionError, targetWallet, setConnectionError } = useContext(SeiWalletContext); +const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) => { + const { connectedWallet, setTargetWallet, wallets, connectionError, targetWallet, setConnectionError, showConnectModal, setShowConnectModal } = useContext(SeiWalletContext); + + const visibleWallets = inputWallets || wallets || []; const [isConnecting, setIsConnecting] = useState(false); + useEffect(() => { + if (connectedWallet || connectionError) { + setIsConnecting(false); + } + }, [connectedWallet, connectionError]); + const closeModal = () => { setConnectionError(undefined); setShowConnectModal(false); @@ -19,7 +27,7 @@ const WalletSelectModal = ({ setShowConnectModal, wallets }: WalletSelectModalPr const renderWallet = (wallet: SeiWallet) => { const isConnectedWallet = connectedWallet?.walletInfo.name === wallet.walletInfo.name; - + const renderConnection = () => { if (isConnectedWallet) return ; return null; @@ -32,12 +40,6 @@ const WalletSelectModal = ({ setShowConnectModal, wallets }: WalletSelectModalPr setConnectionError(undefined); }; - useEffect(() => { - if (connectedWallet || connectionError) { - setIsConnecting(false); - } - }, [connectedWallet, connectionError]); - return (
e.stopPropagation()} className='modal__card'> @@ -125,7 +130,7 @@ const WalletSelectModal = ({ setShowConnectModal, wallets }: WalletSelectModalPr
-
{wallets?.map(renderWallet)}
+
{visibleWallets.map(renderWallet)}
{renderRightSide()}
diff --git a/packages/react/src/lib/components/WalletSelectModal/styles.css b/packages/react/src/lib/components/WalletSelectModal/styles.css index 859dd661..1fbccb95 100644 --- a/packages/react/src/lib/components/WalletSelectModal/styles.css +++ b/packages/react/src/lib/components/WalletSelectModal/styles.css @@ -33,6 +33,20 @@ padding: 24px; } +.modal__background * { + display: flex; + flex-direction: row; + margin: 0; + font-family: Roboto, Helvetica, sans-serif; + box-sizing: border-box; + line-height: 1.5; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + .modal__card { display: flex; flex-direction: column; diff --git a/packages/react/src/lib/components/WalletSelectModal/types.ts b/packages/react/src/lib/components/WalletSelectModal/types.ts index 1f069c1e..77806a20 100644 --- a/packages/react/src/lib/components/WalletSelectModal/types.ts +++ b/packages/react/src/lib/components/WalletSelectModal/types.ts @@ -1,6 +1,5 @@ import { SeiWallet } from '../../provider'; export type WalletSelectModalProps = { - setShowConnectModal: (show: boolean) => void; wallets?: SeiWallet[]; }; diff --git a/packages/react/src/lib/config/supportedWallets.ts b/packages/react/src/lib/config/supportedWallets.ts index 38dc4c97..07462e8f 100644 --- a/packages/react/src/lib/config/supportedWallets.ts +++ b/packages/react/src/lib/config/supportedWallets.ts @@ -1,7 +1,10 @@ import { SeiWallet } from '../provider'; export const FIN_WALLET: SeiWallet = { - getAccounts: async (chainId) => await window?.['fin']?.getOfflineSigner(chainId).getAccounts(), + getAccounts: async (chainId) => { + const offlineSigner = await window?.['fin']?.getOfflineSigner(chainId); + return offlineSigner?.getAccounts() || [] + }, connect: async (chainId) => await window?.['fin']?.enable(chainId), disconnect: async (chainId) => await window?.['fin']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['fin']?.getOfflineSigner(chainId), @@ -15,11 +18,13 @@ export const FIN_WALLET: SeiWallet = { }; export const COMPASS_WALLET: SeiWallet = { - getAccounts: async (chainId) => await window['compass'].getOfflineSigner(chainId).getAccounts(), - connect: async (chainId) => await window['compass'].enable(chainId), - disconnect: async (chainId) => await window['compass'].disable(chainId), - getOfflineSigner: async (chainId) => window['compass'].getOfflineSigner(chainId), - signArbitrary: window['compass'].signArbitrary, + getAccounts: async (chainId) => { + const offlineSigner = await window?.['compass']?.getOfflineSigner(chainId); + return offlineSigner?.getAccounts() || [] + }, connect: async (chainId) => await window?.['compass']?.enable(chainId), + disconnect: async (chainId) => await window?.['compass']?.disable(chainId), + getOfflineSigner: async (chainId) => window?.['compass']?.getOfflineSigner(chainId), + signArbitrary: window?.['compass']?.signArbitrary, walletInfo: { windowKey: 'compass', name: 'Compass', @@ -29,11 +34,13 @@ export const COMPASS_WALLET: SeiWallet = { }; export const KEPLR_WALLET: SeiWallet = { - getAccounts: async (chainId) => await window?.['keplr']?.getOfflineSigner(chainId).getAccounts(), - connect: async (chainId) => await window?.['keplr']?.enable(chainId), + getAccounts: async (chainId) => { + const offlineSigner = await window?.['keplr']?.getOfflineSigner(chainId); + return offlineSigner?.getAccounts() || [] + }, connect: async (chainId) => await window?.['keplr']?.enable(chainId), disconnect: async (chainId) => await window?.['keplr']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['keplr']?.getOfflineSigner(chainId), - signArbitrary: window['keplr'].signArbitrary, + signArbitrary: window?.['keplr']?.signArbitrary, walletInfo: { windowKey: 'keplr', name: 'Keplr', @@ -43,11 +50,13 @@ export const KEPLR_WALLET: SeiWallet = { }; export const LEAP_WALLET: SeiWallet = { - getAccounts: async (chainId) => await window?.['leap']?.getOfflineSigner(chainId).getAccounts(), - connect: async (chainId) => await window?.['leap']?.enable(chainId), + getAccounts: async (chainId) => { + const offlineSigner = await window?.['leap']?.getOfflineSigner(chainId); + return offlineSigner?.getAccounts() || [] + }, connect: async (chainId) => await window?.['leap']?.enable(chainId), disconnect: async (chainId) => await window?.['leap']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSigner(chainId), - signArbitrary: window['leap'].signArbitrary, + signArbitrary: window?.['leap']?.signArbitrary, walletInfo: { windowKey: 'leap', name: 'Leap', diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index 901c2185..30443791 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -1,4 +1,4 @@ -import React, { createContext, useEffect, useState } from 'react'; +import { createContext, useEffect, useState } from 'react'; import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; import { SeiWallet, SeiWalletProviderProps, WalletProvider } from './types'; import { findWalletByWindowKey } from '../config/supportedWallets'; @@ -24,7 +24,7 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect const [connectedWallet, setConnectedWallet] = useState(); const filteredWallets = wallets.reduce((acc: SeiWallet[], wallet) => { - let seiWallet = typeof wallet === 'string' ? findWalletByWindowKey(wallet) : wallet; + const seiWallet = typeof wallet === 'string' ? findWalletByWindowKey(wallet) : wallet; if (seiWallet !== undefined) acc.push(seiWallet); return acc; }, []); @@ -33,7 +33,7 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect if (!targetWallet) return; try { - if (!window[targetWallet.walletInfo.windowKey]) { + if (!window[targetWallet.walletInfo.windowKey as never]) { setConnectionError(targetWallet.walletInfo.windowKey); return; } diff --git a/packages/react/src/lib/provider/types.ts b/packages/react/src/lib/provider/types.ts index cf9c221c..0faa3a0c 100644 --- a/packages/react/src/lib/provider/types.ts +++ b/packages/react/src/lib/provider/types.ts @@ -30,17 +30,17 @@ export interface SeiWallet { icon: string; website: string; }; - getOfflineSigner: (chainId: string) => Promise; - getAccounts: (chainId: string) => Promise; + getOfflineSigner: (chainId: string) => Promise; + getAccounts: (chainId: string) => Promise; connect: (chainId: string) => Promise; disconnect: (chainId: string) => Promise; suggestChain?: (chainId: string) => void; - signArbitrary: (chainId: string, signer: string, message: string) => Promise; + signArbitrary?: (chainId: string, signer: string, message: string) => Promise; } export type SeiWalletProviderProps = { children: ReactNode; chainConfiguration: ChainConfiguration; wallets: SupportedWalletInput[]; - autoConnect: SupportedWalletInput; + autoConnect?: SupportedWalletInput; }; From f97e1b859dc87bf2b1dde6a0c0492bd50cc245dc Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Thu, 8 Jun 2023 22:30:29 -0700 Subject: [PATCH 03/11] -Fixed double modal render issue and simplified modal code and usage -Removed unnecessary dependency --- packages/react/package.json | 3 ++- .../WalletConnectButton/WalletConnectButton.tsx | 9 +++------ .../WalletSelectModal/WalletSelectModal.tsx | 16 ++++++++-------- .../react/src/lib/provider/SeiWalletProvider.tsx | 10 ++++++++-- yarn.lock | 5 +++++ 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index 5cd0bfeb..c4440456 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -24,7 +24,8 @@ "private": false, "dependencies": { "@sei-js/core": "1.3.4", - "react-outside-click-handler": "^1.3.0" + "react-outside-click-handler": "^1.3.0", + "react-icons": "^4.9.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0", diff --git a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx index af61e20a..149fdba1 100644 --- a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx +++ b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import OutsideClickHandler from 'react-outside-click-handler'; import { IconContext } from 'react-icons'; import { SeiWalletContext } from '../../provider'; @@ -6,7 +6,7 @@ import { WalletSelectModal } from '../WalletSelectModal'; import { WalletConnectButtonProps } from './types'; import './styles.css'; import { isValidCSSColor } from '../../utils'; -import {IoCopyOutline, IoLogOutOutline, IoWalletOutline} from "react-icons/io5"; +import { IoCopyOutline, IoLogOutOutline, IoWalletOutline } from 'react-icons/io5'; export const truncateAddress = (address: string) => `${address.slice(0, 3)}....${address.slice(address.length - 5)}`; @@ -94,10 +94,7 @@ const WalletConnectButton = ({ buttonClassName, wallets: inputWallets, primaryCo return ( <> - - {renderButton()} - - + {renderButton()} ); }; diff --git a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx index cc5491d0..f0e206a7 100644 --- a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx +++ b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx @@ -1,14 +1,14 @@ -import { useContext, useEffect, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { WalletSelectModalProps } from './types'; import { SeiWallet, SeiWalletContext } from '../../provider'; import './styles.css'; import { AiFillCloseCircle } from 'react-icons/ai'; -import {BiError, BiErrorAlt} from 'react-icons/bi'; -import { Link } from 'react-router-dom'; -import {FaCheckCircle} from "react-icons/fa"; +import { BiError, BiErrorAlt } from 'react-icons/bi'; +import { FaCheckCircle } from 'react-icons/fa'; const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) => { - const { connectedWallet, setTargetWallet, wallets, connectionError, targetWallet, setConnectionError, showConnectModal, setShowConnectModal } = useContext(SeiWalletContext); + const { connectedWallet, setTargetWallet, wallets, connectionError, targetWallet, setConnectionError, showConnectModal, setShowConnectModal } = + useContext(SeiWalletContext); const visibleWallets = inputWallets || wallets || []; @@ -74,9 +74,9 @@ const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) =>

{targetWallet?.walletInfo?.name || 'Wallet'} not installed

{targetWallet?.walletInfo.website && ( - + Download {targetWallet?.walletInfo.name} - + )}
); @@ -120,7 +120,7 @@ const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) => }; console.log('showConnectModal', showConnectModal); - if(!showConnectModal) return null; + if (!showConnectModal) return null; return (
diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index 30443791..957a08b5 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -1,7 +1,8 @@ -import { createContext, useEffect, useState } from 'react'; +import React, { createContext, useEffect, useState } from 'react'; import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; import { SeiWallet, SeiWalletProviderProps, WalletProvider } from './types'; import { findWalletByWindowKey } from '../config/supportedWallets'; +import { WalletSelectModal } from '../components'; export const SeiWalletContext = createContext({ accounts: [], @@ -86,7 +87,12 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect setConnectionError }; - return {children}; + return ( + + {children} + + + ); }; export default SeiWalletProvider; diff --git a/yarn.lock b/yarn.lock index b6507b4b..e7fae0db 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6873,6 +6873,11 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== +react-icons@^4.9.0: + version "4.9.0" + resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz#ba44f436a053393adb1bdcafbc5c158b7b70d2a3" + integrity sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg== + react-is@^16.13.1: version "16.13.1" resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" From 840499f1c36056ff04b3e4c22ef6d402437c1c0b Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Thu, 8 Jun 2023 22:34:29 -0700 Subject: [PATCH 04/11] Adjusted README --- packages/react/README.md | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/packages/react/README.md b/packages/react/README.md index 9affc303..c81f7092 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -99,7 +99,7 @@ const { cosmWasmClient } = useSeiCosmWasmClient(); # UI Components This package contains two helpful UI components for connecting to a wallet provider. -## WalletConnectButton +## \ This component renders a button that will open a modal to connect to a wallet provider. ```javascript @@ -131,40 +131,29 @@ export default Component; *If your page has a on the page it can be opened programmatically by calling the hook "useSelectWallet"* -## WalletSelectModal -This component renders a modal that will allow you to select a wallet provider and has a hook "useSelectWallet" that will allow you to open and close the modal programmatically. +## useSelectWallet() +This hook allows you to programmatically open and close the wallet modal. + ```javascript import React from "react"; -import {useWallet, WalletSelectModal} from "../lib"; +import { useWallet, useSelectWallet } from "../lib"; const Component = () => { - const { connectedWallet } = useWallet() - const { openModal } = useSelectWallet() - - return
- -

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

- - { /** You must include the with the 'useSelectWallet' hook, unless you already have a rendered somewhere else in your app **/ } - -
+ const { connectedWallet } = useWallet(); + const { openModal, closeModal } = useSelectWallet(); + + return ( +
+ +

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

+
+ ); }; export default Component; ``` - -| Property | Type | Description | -|-----------------|--------------|-----------------------------------------------------------------------------| -| wallets | SeiWallet[]? | A stargate signing client. | -| buttonClassName | string | A css class name for styling the button | -| primaryColor | string | A hex value of the color you want to tint the text and icons with | -| secondaryColor | string | A secondary hex value of the color you want to tint the text and icons with | -| backgroundColor | string | A hex value of the color you want to use as a background | - - - ### Other helpful packages - [@sei-js/core](https://www.npmjs.com/package/@sei-js/core) - TypeScript library containing helper functions for wallet connection, transaction sig From f608f21852e49014e057e9caf13dc7a2b902dd27 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Thu, 8 Jun 2023 23:00:40 -0700 Subject: [PATCH 05/11] - Made useWallet only export certain fields from context - Fixed "react-icons" dependency issue - Fixed fade animations - Adjusted typescript types --- packages/react/README.md | 14 +++++++------- packages/react/package.json | 2 +- .../WalletConnectButton/WalletConnectButton.tsx | 7 +++---- .../WalletSelectModal/WalletSelectModal.tsx | 3 +-- .../lib/components/WalletSelectModal/styles.css | 5 ++--- .../react/src/lib/hooks/useWallet/useWallet.ts | 17 +++++++++++++++-- .../src/lib/provider/SeiWalletProvider.tsx | 5 ++++- packages/react/src/lib/provider/types.ts | 6 +++--- yarn.lock | 8 ++++---- 9 files changed, 40 insertions(+), 27 deletions(-) diff --git a/packages/react/README.md b/packages/react/README.md index c81f7092..1150c758 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -50,8 +50,6 @@ const { offlineSigner, accounts, connectedWallet } = useWallet(); | Property | Type | Description | |------------------|-----------|---------------------------------------------------------| | connectedWallet | string? | The currently connected wallet | -| supportedWallets | string[] | List of supported wallets | -| installedWallets | string[] | List of wallets installed | | chainId | string | Sei chain id | | restUrl | string | The rest url associated with the connected wallet | | rpcUrl | string | The rpc url associated with the connected wallet | @@ -107,12 +105,14 @@ import React from "react"; import {useWallet, WalletConnectButton} from "../lib"; const Component = () => { - const { connectedWallet } = useWallet() + const { connectedWallet } = useWallet(); - return
- -

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

-
+ return ( +
+ +

Connected wallet: {connectedWallet?.walletInfo?.name || "---"}

+
+ ); }; export default Component; diff --git a/packages/react/package.json b/packages/react/package.json index c4440456..19551317 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -25,7 +25,7 @@ "dependencies": { "@sei-js/core": "1.3.4", "react-outside-click-handler": "^1.3.0", - "react-icons": "^4.9.0" + "react-icons": "4.6.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0", diff --git a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx index 149fdba1..48f96f83 100644 --- a/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx +++ b/packages/react/src/lib/components/WalletConnectButton/WalletConnectButton.tsx @@ -1,20 +1,19 @@ import React, { useContext, useEffect, useState } from 'react'; import OutsideClickHandler from 'react-outside-click-handler'; import { IconContext } from 'react-icons'; +import { IoCopyOutline, IoLogOutOutline, IoWalletOutline } from 'react-icons/io5'; import { SeiWalletContext } from '../../provider'; -import { WalletSelectModal } from '../WalletSelectModal'; import { WalletConnectButtonProps } from './types'; import './styles.css'; import { isValidCSSColor } from '../../utils'; -import { IoCopyOutline, IoLogOutOutline, IoWalletOutline } from 'react-icons/io5'; export const truncateAddress = (address: string) => `${address.slice(0, 3)}....${address.slice(address.length - 5)}`; -const WalletConnectButton = ({ buttonClassName, wallets: inputWallets, primaryColor, secondaryColor, backgroundColor }: WalletConnectButtonProps) => { +const WalletConnectButton = ({ buttonClassName, primaryColor, secondaryColor, backgroundColor }: WalletConnectButtonProps) => { const [showMenu, setShowMenu] = useState(false); const [recentlyCopied, setRecentlyCopied] = useState(false); - const { connectedWallet, accounts, setTargetWallet, wallets, setShowConnectModal } = useContext(SeiWalletContext); + const { connectedWallet, accounts, setTargetWallet, setShowConnectModal } = useContext(SeiWalletContext); useEffect(() => { const color = primaryColor && isValidCSSColor(primaryColor) ? primaryColor : '#121212'; diff --git a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx index f0e206a7..39badcf3 100644 --- a/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx +++ b/packages/react/src/lib/components/WalletSelectModal/WalletSelectModal.tsx @@ -45,7 +45,7 @@ const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) => key={wallet.walletInfo.name} onClick={selectWallet} className={`wallet__item ${isConnectedWallet ? 'wallet__item-connected' : ''} ${ - targetWallet?.walletInfo?.windowKey === wallet.walletInfo.windowKey ? 'wallet__item-targetd' : '' + targetWallet?.walletInfo?.windowKey === wallet.walletInfo.windowKey ? 'wallet__item-targeted' : '' }`}>
{wallet.walletInfo.name} @@ -119,7 +119,6 @@ const WalletSelectModal = ({ wallets: inputWallets }: WalletSelectModalProps) => ); }; - console.log('showConnectModal', showConnectModal); if (!showConnectModal) return null; return ( diff --git a/packages/react/src/lib/components/WalletSelectModal/styles.css b/packages/react/src/lib/components/WalletSelectModal/styles.css index 1fbccb95..80c9b295 100644 --- a/packages/react/src/lib/components/WalletSelectModal/styles.css +++ b/packages/react/src/lib/components/WalletSelectModal/styles.css @@ -1,4 +1,4 @@ -@keyframes fadeIn { +@keyframes walletFadeIn { 0% { opacity: 0; } @@ -61,7 +61,6 @@ box-shadow: rgba(14, 15, 16, 0.1) 3px 9px 24px; overflow-x: hidden; gap: 48px; - animation: fadeIn 150ms linear; } .card__header { @@ -135,7 +134,7 @@ width: 100%; padding: 0 36px 0 24px; opacity: 0; - animation: fadeIn 0.15s 0.3s forwards; + animation: walletFadeIn 0.15s 0.3s forwards; } .card__right--item { diff --git a/packages/react/src/lib/hooks/useWallet/useWallet.ts b/packages/react/src/lib/hooks/useWallet/useWallet.ts index 791e888b..960397f9 100644 --- a/packages/react/src/lib/hooks/useWallet/useWallet.ts +++ b/packages/react/src/lib/hooks/useWallet/useWallet.ts @@ -1,6 +1,19 @@ import { useContext } from 'react'; -import { SeiWalletContext, WalletProvider } from '../../provider'; +import { SeiWallet, SeiWalletContext } from '../../provider'; +import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; -const useWallet = (): WalletProvider => useContext(SeiWalletContext); +type UseWallet = { + connectedWallet?: SeiWallet; + chainId: string; + restUrl: string; + rpcUrl: string; + offlineSigner?: OfflineSigner; + accounts: readonly AccountData[]; +}; +const useWallet = (): UseWallet => { + const { connectedWallet, chainId, restUrl, rpcUrl, offlineSigner, accounts } = useContext(SeiWalletContext); + + return { connectedWallet, chainId, restUrl, rpcUrl, offlineSigner, accounts }; +}; export default useWallet; diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index 957a08b5..fab9e9ca 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -5,6 +5,9 @@ import { findWalletByWindowKey } from '../config/supportedWallets'; import { WalletSelectModal } from '../components'; export const SeiWalletContext = createContext({ + chainId: '', + restUrl: '', + rpcUrl: '', accounts: [], connect: () => undefined, disconnect: () => undefined, @@ -90,7 +93,7 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect return ( {children} - + ); }; diff --git a/packages/react/src/lib/provider/types.ts b/packages/react/src/lib/provider/types.ts index 0faa3a0c..d96108cd 100644 --- a/packages/react/src/lib/provider/types.ts +++ b/packages/react/src/lib/provider/types.ts @@ -4,9 +4,9 @@ import { ChainConfiguration } from '../types'; import { StdSignature } from '@cosmjs/amino'; export type WalletProvider = { - chainId?: string; - restUrl?: string; - rpcUrl?: string; + chainId: string; + restUrl: string; + rpcUrl: string; connectionError?: string; setConnectionError: Dispatch>; accounts: readonly AccountData[]; diff --git a/yarn.lock b/yarn.lock index e7fae0db..506b8a31 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6873,10 +6873,10 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -react-icons@^4.9.0: - version "4.9.0" - resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz#ba44f436a053393adb1bdcafbc5c158b7b70d2a3" - integrity sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg== +react-icons@4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz#f83eda179af5d02c047449a20b702c858653d397" + integrity sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g== react-is@^16.13.1: version "16.13.1" From 372506fafb4b280d4a47b35f8831a934d165c26e Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Fri, 9 Jun 2023 21:13:25 -0700 Subject: [PATCH 06/11] -Export useSelectWallet -Add keplr chain suggest -Added prettierrc styles -Added changeset --- .changeset/curvy-mangos-begin.md | 6 +++ .prettierrc | 10 +++- .../react/src/lib/config/supportedWallets.ts | 18 ++++--- packages/react/src/lib/hooks/index.ts | 1 + .../src/lib/hooks/useSelectWallet/index.ts | 1 - .../src/lib/provider/SeiWalletProvider.tsx | 8 +++ .../src/lib/provider/keplrVerification.ts | 53 +++++++++++++++++++ 7 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 .changeset/curvy-mangos-begin.md create mode 100644 packages/react/src/lib/provider/keplrVerification.ts diff --git a/.changeset/curvy-mangos-begin.md b/.changeset/curvy-mangos-begin.md new file mode 100644 index 00000000..4f2bf129 --- /dev/null +++ b/.changeset/curvy-mangos-begin.md @@ -0,0 +1,6 @@ +--- +'@sei-js/react': major +'@sei-js/core': minor +--- + +Refactored wallet provider and UI components, added hook to programatically open wallet connect modal, implemented a new wallet interface, replaced custom styles with tint colors diff --git a/.prettierrc b/.prettierrc index 544138be..bc7cfd37 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,11 @@ { - "singleQuote": true + "tabWidth": 2, + "bracketSpacing": true, + "jsxBracketSameLine": true, + "printWidth": 164, + "singleQuote": true, + "jsxSingleQuote": true, + "trailingComma": "none", + "arrowParens": "always", + "useTabs": true } diff --git a/packages/react/src/lib/config/supportedWallets.ts b/packages/react/src/lib/config/supportedWallets.ts index 07462e8f..946f7ab9 100644 --- a/packages/react/src/lib/config/supportedWallets.ts +++ b/packages/react/src/lib/config/supportedWallets.ts @@ -3,7 +3,7 @@ import { SeiWallet } from '../provider'; export const FIN_WALLET: SeiWallet = { getAccounts: async (chainId) => { const offlineSigner = await window?.['fin']?.getOfflineSigner(chainId); - return offlineSigner?.getAccounts() || [] + return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['fin']?.enable(chainId), disconnect: async (chainId) => await window?.['fin']?.disable(chainId), @@ -20,8 +20,9 @@ export const FIN_WALLET: SeiWallet = { export const COMPASS_WALLET: SeiWallet = { getAccounts: async (chainId) => { const offlineSigner = await window?.['compass']?.getOfflineSigner(chainId); - return offlineSigner?.getAccounts() || [] - }, connect: async (chainId) => await window?.['compass']?.enable(chainId), + return offlineSigner?.getAccounts() || []; + }, + connect: async (chainId) => await window?.['compass']?.enable(chainId), disconnect: async (chainId) => await window?.['compass']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['compass']?.getOfflineSigner(chainId), signArbitrary: window?.['compass']?.signArbitrary, @@ -36,8 +37,9 @@ export const COMPASS_WALLET: SeiWallet = { export const KEPLR_WALLET: SeiWallet = { getAccounts: async (chainId) => { const offlineSigner = await window?.['keplr']?.getOfflineSigner(chainId); - return offlineSigner?.getAccounts() || [] - }, connect: async (chainId) => await window?.['keplr']?.enable(chainId), + return offlineSigner?.getAccounts() || []; + }, + connect: async (chainId) => await window?.['keplr']?.enable(chainId), disconnect: async (chainId) => await window?.['keplr']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['keplr']?.getOfflineSigner(chainId), signArbitrary: window?.['keplr']?.signArbitrary, @@ -52,8 +54,10 @@ export const KEPLR_WALLET: SeiWallet = { export const LEAP_WALLET: SeiWallet = { getAccounts: async (chainId) => { const offlineSigner = await window?.['leap']?.getOfflineSigner(chainId); - return offlineSigner?.getAccounts() || [] - }, connect: async (chainId) => await window?.['leap']?.enable(chainId), + return offlineSigner?.getAccounts() || []; + }, + connect: async (chainId) => await window?.['leap']?.enable(chainId), + // @ts-ignore disconnect: async (chainId) => await window?.['leap']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSigner(chainId), signArbitrary: window?.['leap']?.signArbitrary, diff --git a/packages/react/src/lib/hooks/index.ts b/packages/react/src/lib/hooks/index.ts index 397a1fad..139b31e3 100644 --- a/packages/react/src/lib/hooks/index.ts +++ b/packages/react/src/lib/hooks/index.ts @@ -4,3 +4,4 @@ export * from './useSigningClient'; export * from './useSigningCosmWasmClient'; export * from './useStargateClient'; export * from './useWallet'; +export * from './useSelectWallet'; diff --git a/packages/react/src/lib/hooks/useSelectWallet/index.ts b/packages/react/src/lib/hooks/useSelectWallet/index.ts index 5c8a702c..4e6f4dbf 100644 --- a/packages/react/src/lib/hooks/useSelectWallet/index.ts +++ b/packages/react/src/lib/hooks/useSelectWallet/index.ts @@ -1,2 +1 @@ export { default as useSelectWallet } from './useSelectWallet'; -export type { UseSelectWallet } from './useSelectWallet'; diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index fab9e9ca..d018b8a7 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -3,6 +3,7 @@ import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; import { SeiWallet, SeiWalletProviderProps, WalletProvider } from './types'; import { findWalletByWindowKey } from '../config/supportedWallets'; import { WalletSelectModal } from '../components'; +import { checkKeplrForChain, getVerifiedSuggestChain } from './keplrVerification'; export const SeiWalletContext = createContext({ chainId: '', @@ -42,6 +43,13 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect return; } + if (targetWallet.walletInfo.windowKey === 'keplr') { + const isChainRegistered = await checkKeplrForChain(chainConfiguration.chainId); + if (!isChainRegistered) { + await window.keplr?.experimentalSuggestChain(getVerifiedSuggestChain(chainConfiguration.chainId)); + } + } + const fetchedOfflineSigner = await targetWallet.getOfflineSigner(chainConfiguration.chainId); const fetchedAccounts = await targetWallet.getAccounts(chainConfiguration.chainId); diff --git a/packages/react/src/lib/provider/keplrVerification.ts b/packages/react/src/lib/provider/keplrVerification.ts new file mode 100644 index 00000000..b26327cf --- /dev/null +++ b/packages/react/src/lib/provider/keplrVerification.ts @@ -0,0 +1,53 @@ +export const getVerifiedSuggestChain = (chainId: string) => ({ + chainId: chainId, + chainName: `Sei (${chainId})`, + rpc: `https://rpc.wallet.${chainId}.sei.io`, + rest: `https://rest.wallet.${chainId}.sei.io`, + bip44: { + coinType: 118, + }, + bech32Config: { + bech32PrefixAccAddr: 'sei', + bech32PrefixAccPub: `seipub`, + bech32PrefixValAddr: `seivaloper`, + bech32PrefixValPub: `seivaloperpub`, + bech32PrefixConsAddr: `seivalcons`, + bech32PrefixConsPub: `seivalconspub`, + }, + currencies: [ + { + coinDenom: 'SEI', + coinMinimalDenom: 'usei', + coinDecimals: 6, + }, + ], + feeCurrencies: [ + { + coinDenom: 'SEI', + coinMinimalDenom: 'usei', + coinDecimals: 6, + gasPriceStep: { + low: 0.001, + average: 0.02, + high: 0.03, + }, + }, + ], + stakeCurrency: { + coinDenom: 'SEI', + coinMinimalDenom: 'usei', + coinDecimals: 6, + }, + coinType: 118, + features: ['stargate', 'ibc-transfer', 'cosmwasm'], +}); + +export const checkKeplrForChain = async (chainId: string) => { + const keplrChainInformation = await window[ + 'keplr' + ]?.getChainInfosWithoutEndpoints(); + const chainInfo = keplrChainInformation?.find( + (chainInfo) => chainInfo.chainId === chainId + ); + return !!chainInfo; +}; From dc4732640587c7dd01fb63305a6f369f11dd3123 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Tue, 13 Jun 2023 09:09:25 -0700 Subject: [PATCH 07/11] Removed keplr chain config verification, added connect function for consistency. --- packages/react/src/lib/provider/SeiWalletProvider.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/react/src/lib/provider/SeiWalletProvider.tsx b/packages/react/src/lib/provider/SeiWalletProvider.tsx index d018b8a7..fbfcd5a7 100644 --- a/packages/react/src/lib/provider/SeiWalletProvider.tsx +++ b/packages/react/src/lib/provider/SeiWalletProvider.tsx @@ -3,7 +3,6 @@ import { AccountData, OfflineSigner } from '@cosmjs/proto-signing'; import { SeiWallet, SeiWalletProviderProps, WalletProvider } from './types'; import { findWalletByWindowKey } from '../config/supportedWallets'; import { WalletSelectModal } from '../components'; -import { checkKeplrForChain, getVerifiedSuggestChain } from './keplrVerification'; export const SeiWalletContext = createContext({ chainId: '', @@ -43,13 +42,7 @@ const SeiWalletProvider = ({ children, chainConfiguration, wallets, autoConnect return; } - if (targetWallet.walletInfo.windowKey === 'keplr') { - const isChainRegistered = await checkKeplrForChain(chainConfiguration.chainId); - if (!isChainRegistered) { - await window.keplr?.experimentalSuggestChain(getVerifiedSuggestChain(chainConfiguration.chainId)); - } - } - + await targetWallet.connect(chainConfiguration.chainId); const fetchedOfflineSigner = await targetWallet.getOfflineSigner(chainConfiguration.chainId); const fetchedAccounts = await targetWallet.getAccounts(chainConfiguration.chainId); From 9c010a48878e3e16210240726a245f11a30a2461 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Wed, 14 Jun 2023 16:21:52 -0700 Subject: [PATCH 08/11] Replaced getOfflineSigner with getOfflineSignerAuto --- .../react/src/lib/config/supportedWallets.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/react/src/lib/config/supportedWallets.ts b/packages/react/src/lib/config/supportedWallets.ts index 946f7ab9..4047c289 100644 --- a/packages/react/src/lib/config/supportedWallets.ts +++ b/packages/react/src/lib/config/supportedWallets.ts @@ -2,12 +2,12 @@ import { SeiWallet } from '../provider'; export const FIN_WALLET: SeiWallet = { getAccounts: async (chainId) => { - const offlineSigner = await window?.['fin']?.getOfflineSigner(chainId); + const offlineSigner = await window?.['fin']?.getOfflineSignerAuto(chainId); return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['fin']?.enable(chainId), disconnect: async (chainId) => await window?.['fin']?.disable(chainId), - getOfflineSigner: async (chainId) => window?.['fin']?.getOfflineSigner(chainId), + getOfflineSigner: async (chainId) => window?.['fin']?.getOfflineSignerAuto(chainId), signArbitrary: window?.['fin']?.signArbitrary, walletInfo: { windowKey: 'fin', @@ -19,12 +19,12 @@ export const FIN_WALLET: SeiWallet = { export const COMPASS_WALLET: SeiWallet = { getAccounts: async (chainId) => { - const offlineSigner = await window?.['compass']?.getOfflineSigner(chainId); + const offlineSigner = await window?.['compass']?.getOfflineSignerAuto(chainId); return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['compass']?.enable(chainId), disconnect: async (chainId) => await window?.['compass']?.disable(chainId), - getOfflineSigner: async (chainId) => window?.['compass']?.getOfflineSigner(chainId), + getOfflineSigner: async (chainId) => window?.['compass']?.getOfflineSignerAuto(chainId), signArbitrary: window?.['compass']?.signArbitrary, walletInfo: { windowKey: 'compass', @@ -36,12 +36,12 @@ export const COMPASS_WALLET: SeiWallet = { export const KEPLR_WALLET: SeiWallet = { getAccounts: async (chainId) => { - const offlineSigner = await window?.['keplr']?.getOfflineSigner(chainId); + const offlineSigner = await window?.['keplr']?.getOfflineSignerAuto(chainId); return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['keplr']?.enable(chainId), disconnect: async (chainId) => await window?.['keplr']?.disable(chainId), - getOfflineSigner: async (chainId) => window?.['keplr']?.getOfflineSigner(chainId), + getOfflineSigner: async (chainId) => window?.['keplr']?.getOfflineSignerAuto(chainId), signArbitrary: window?.['keplr']?.signArbitrary, walletInfo: { windowKey: 'keplr', @@ -53,13 +53,13 @@ export const KEPLR_WALLET: SeiWallet = { export const LEAP_WALLET: SeiWallet = { getAccounts: async (chainId) => { - const offlineSigner = await window?.['leap']?.getOfflineSigner(chainId); + const offlineSigner = await window?.['leap']?.getOfflineSignerAuto(chainId); return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['leap']?.enable(chainId), // @ts-ignore disconnect: async (chainId) => await window?.['leap']?.disable(chainId), - getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSigner(chainId), + getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSignerAuto(chainId), signArbitrary: window?.['leap']?.signArbitrary, walletInfo: { windowKey: 'leap', From 292e53a295b2dec6afc6f1530cca44e1d3a408e1 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Fri, 16 Jun 2023 12:14:30 -0700 Subject: [PATCH 09/11] Removed unnecessary @ts-ignore --- packages/react/src/lib/config/supportedWallets.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/react/src/lib/config/supportedWallets.ts b/packages/react/src/lib/config/supportedWallets.ts index 4047c289..32846627 100644 --- a/packages/react/src/lib/config/supportedWallets.ts +++ b/packages/react/src/lib/config/supportedWallets.ts @@ -57,7 +57,6 @@ export const LEAP_WALLET: SeiWallet = { return offlineSigner?.getAccounts() || []; }, connect: async (chainId) => await window?.['leap']?.enable(chainId), - // @ts-ignore disconnect: async (chainId) => await window?.['leap']?.disable(chainId), getOfflineSigner: async (chainId) => window?.['leap']?.getOfflineSignerAuto(chainId), signArbitrary: window?.['leap']?.signArbitrary, From e43c08c5d0476e873c1b049eabb6e3d0eb67e903 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Fri, 16 Jun 2023 13:58:40 -0700 Subject: [PATCH 10/11] Fixed test issue. --- packages/core/src/lib/wallet/connect.spec.ts | 90 ++++++++++---------- packages/core/src/lib/wallet/connect.ts | 43 +++++----- 2 files changed, 63 insertions(+), 70 deletions(-) diff --git a/packages/core/src/lib/wallet/connect.spec.ts b/packages/core/src/lib/wallet/connect.spec.ts index bd004b4a..c2130b5c 100644 --- a/packages/core/src/lib/wallet/connect.spec.ts +++ b/packages/core/src/lib/wallet/connect.spec.ts @@ -6,50 +6,48 @@ import { connect } from './connect'; global.TextEncoder = TextEncoder; describe('connect', () => { - let windowSpy: jest.SpyInstance; - - beforeEach(() => { - windowSpy = jest.spyOn(window, 'window', 'get'); - }); - - afterEach(() => { - windowSpy.mockRestore(); - }); - - it('should throw an error if window is undefined', async () => { - windowSpy.mockImplementation(() => undefined); - - const key = 'keplr'; - const chainId = 'atlantic-1'; - await expect(connect(key, chainId)).rejects.toThrowError(); - }); - - it('should throw an error if no wallet is installed', async () => { - windowSpy.mockImplementation(() => ({})); - - const key = 'keplr'; - const chainId = 'atlantic-1'; - await expect(connect(key, chainId)).rejects.toThrowError(); - }); - - it('should return offlineSigner and accounts', async () => { - const offlineSigner = await DirectSecp256k1HdWallet.fromMnemonic( - 'trip parent program index any save apple extra marble nothing please pulp' - ); - const accounts = await offlineSigner.getAccounts(); - windowSpy.mockImplementation(() => ({ - keplr: { - getOfflineSignerAuto: () => offlineSigner, - experimentalSuggestChain: () => undefined, - enable: () => undefined, - }, - })); - - const key = 'keplr'; - const chainId = 'atlantic-1'; - await expect(connect(key, chainId)).resolves.toEqual({ - offlineSigner, - accounts, - }); - }); + let windowSpy: jest.SpyInstance; + + beforeEach(() => { + windowSpy = jest.spyOn(window, 'window', 'get'); + }); + + afterEach(() => { + windowSpy.mockRestore(); + }); + + it('should throw an error if window is undefined', async () => { + windowSpy.mockImplementation(() => undefined); + + const key = 'keplr'; + const chainId = 'atlantic-1'; + await expect(connect(key, chainId)).rejects.toThrowError(); + }); + + it('should throw an error if no wallet is installed', async () => { + windowSpy.mockImplementation(() => ({})); + + const key = 'keplr'; + const chainId = 'atlantic-2'; + await expect(connect(key, chainId)).rejects.toThrowError(); + }); + + it('should return offlineSigner and accounts', async () => { + const offlineSigner = await DirectSecp256k1HdWallet.fromMnemonic('trip parent program index any save apple extra marble nothing please pulp'); + const accounts = await offlineSigner.getAccounts(); + windowSpy.mockImplementation(() => ({ + keplr: { + getOfflineSignerAuto: () => offlineSigner, + experimentalSuggestChain: () => undefined, + enable: () => undefined + } + })); + + const key = 'keplr'; + const chainId = 'atlantic-1'; + await expect(connect(key, chainId)).resolves.toEqual({ + offlineSigner, + accounts + }); + }); }); diff --git a/packages/core/src/lib/wallet/connect.ts b/packages/core/src/lib/wallet/connect.ts index b1735cff..24f01295 100644 --- a/packages/core/src/lib/wallet/connect.ts +++ b/packages/core/src/lib/wallet/connect.ts @@ -2,34 +2,29 @@ import { Keplr as KeplrWindow } from '@keplr-wallet/types'; import { WalletConnect, WalletWindowInterface, WalletWindowKey } from './types'; declare global { - interface Window { - compass?: WalletWindowInterface; - fin?: WalletWindowInterface; - keplr?: KeplrWindow; - leap?: WalletWindowInterface; - compass?: WalletWindowInterface; - } + interface Window { + compass?: WalletWindowInterface; + fin?: WalletWindowInterface; + keplr?: KeplrWindow; + leap?: WalletWindowInterface; + } } -export const connect = async ( - inputWallet: WalletWindowKey, - chainId: string -): Promise => { - if (typeof window === 'undefined' || !window) { - throw new Error('Window is undefined.'); - } +export const connect = async (inputWallet: WalletWindowKey, chainId: string): Promise => { + if (typeof window === 'undefined' || !window) { + throw new Error('Window is undefined.'); + } - const walletProvider = - inputWallet === 'coin98' ? window[inputWallet]?.keplr : window[inputWallet]; - if (!walletProvider) { - throw new Error(`Wallet ${inputWallet} is not installed.`); - } + const walletProvider = inputWallet === 'coin98' ? window[inputWallet]?.keplr : window[inputWallet]; + if (!walletProvider) { + throw new Error(`Wallet ${inputWallet} is not installed.`); + } - // Enable wallet before attempting to call any methods - await walletProvider.enable(chainId); + // Enable wallet before attempting to call any methods + await walletProvider.enable(chainId); - const offlineSigner = await walletProvider.getOfflineSignerAuto(chainId); - const accounts = await offlineSigner.getAccounts(); + const offlineSigner = await walletProvider.getOfflineSignerAuto(chainId); + const accounts = await offlineSigner.getAccounts(); - return { offlineSigner, accounts }; + return { offlineSigner, accounts }; }; From 2acb936cca716f824361d276d3aceac23ef5d1f9 Mon Sep 17 00:00:00 2001 From: Carson Aberle Date: Tue, 20 Jun 2023 08:39:57 -0700 Subject: [PATCH 11/11] Added react-icons --- packages/react/package.json | 3 ++- yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/react/package.json b/packages/react/package.json index 81a6d15b..cc28234b 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -24,7 +24,8 @@ "private": false, "dependencies": { "@sei-js/core": "1.3.6", - "react-outside-click-handler": "^1.3.0" + "react-outside-click-handler": "^1.3.0", + "react-icons": "4.9.0" }, "peerDependencies": { "react": "^17.0.0 || ^18.0.0", diff --git a/yarn.lock b/yarn.lock index 506b8a31..e91c1d79 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6873,10 +6873,10 @@ quick-lru@^4.0.1: resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -react-icons@4.6.0: - version "4.6.0" - resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.6.0.tgz#f83eda179af5d02c047449a20b702c858653d397" - integrity sha512-rR/L9m9340yO8yv1QT1QurxWQvWpbNHqVX0fzMln2HEb9TEIrQRGsqiNFQfiv9/JEUbyHmHPlNTB2LWm2Ttz0g== +react-icons@4.9.0: + version "4.9.0" + resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.9.0.tgz#ba44f436a053393adb1bdcafbc5c158b7b70d2a3" + integrity sha512-ijUnFr//ycebOqujtqtV9PFS7JjhWg0QU6ykURVHuL4cbofvRCf3f6GMn9+fBktEFQOIVZnuAYLZdiyadRQRFg== react-is@^16.13.1: version "16.13.1"