Skip to content

Commit 0703496

Browse files
hernan-clichfadeev
andauthored
feat: Integrate dynamic wallet on Hello frontend example (#280)
Co-authored-by: Denis Fadeev <[email protected]>
1 parent f3f9c52 commit 0703496

38 files changed

+6541
-1191
lines changed

examples/hello/frontend/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
},
1212
"dependencies": {
1313
"@zetachain/toolkit": "16.0.1",
14+
"@zetachain/wallet": "1.0.12",
1415
"clsx": "^2.1.1",
1516
"ethers": "^6.13.2",
1617
"react": "^19.1.0",
@@ -32,5 +33,9 @@
3233
"typescript": "~5.8.3",
3334
"typescript-eslint": "^8.35.1",
3435
"vite": "^7.0.4"
36+
},
37+
"resolutions": {
38+
"@noble/hashes": "1.8.0",
39+
"@noble/curves": "1.9.7"
3540
}
3641
}

examples/hello/frontend/src/App.tsx

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1+
import { UniversalSignInContextProvider } from '@zetachain/wallet/react';
2+
13
import { AppContent } from './AppContent';
24
import { Header } from './components/Header';
3-
import { ThemeProvider } from './context/ThemeProvider';
5+
import { USE_DYNAMIC_WALLET } from './constants/wallets';
6+
import { useTheme } from './hooks/useTheme';
47

58
function App() {
6-
return (
7-
<ThemeProvider>
9+
const { theme } = useTheme();
10+
11+
return USE_DYNAMIC_WALLET ? (
12+
<UniversalSignInContextProvider environment="sandbox" theme={theme}>
13+
<Header />
14+
<AppContent />
15+
</UniversalSignInContextProvider>
16+
) : (
17+
<>
818
<Header />
919
<AppContent />
10-
</ThemeProvider>
20+
</>
1121
);
1222
}
1323

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,7 @@
1-
import { ConnectedContent } from './ConnectedContent';
2-
import { SUPPORTED_CHAINS } from './constants/chains';
3-
import { DisconnectedContent } from './DisconnectedContent';
4-
import { useWallet } from './hooks/useWallet';
1+
import { USE_DYNAMIC_WALLET } from './constants/wallets';
2+
import { DynamicAppContent } from './DynamicAppContent';
3+
import { Eip6963AppContent } from './Eip6963AppContent';
54

65
export function AppContent() {
7-
const { account, selectedProvider, decimalChainId } = useWallet();
8-
9-
const supportedChain = SUPPORTED_CHAINS.find(
10-
(chain) => chain.chainId === decimalChainId
11-
);
12-
13-
if (!account || !selectedProvider) {
14-
return <DisconnectedContent />;
15-
}
16-
17-
return (
18-
<ConnectedContent
19-
selectedProvider={selectedProvider}
20-
supportedChain={supportedChain}
21-
/>
22-
);
6+
return USE_DYNAMIC_WALLET ? <DynamicAppContent /> : <Eip6963AppContent />;
237
}

examples/hello/frontend/src/ConnectedContent.css

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -42,32 +42,6 @@
4242
}
4343
}
4444

45-
.call-input {
46-
background-color: rgba(var(--primary-color-rgb), 0.8);
47-
font-size: 1.25rem;
48-
height: 100%;
49-
padding: 0.785rem;
50-
width: 100%;
51-
}
52-
53-
.call-button {
54-
font-size: 1rem;
55-
padding: 1rem;
56-
transition: all 0.2s ease-in-out;
57-
white-space: nowrap;
58-
width: 100%;
59-
60-
&:disabled {
61-
background-color: rgba(126, 126, 126, 0.5);
62-
cursor: not-allowed;
63-
opacity: 0.7;
64-
}
65-
}
66-
67-
.input-counter {
68-
font-size: 1.5rem;
69-
}
70-
7145
[data-theme='light'] .content-container-inner-description {
7246
color: #696e75;
7347

@@ -103,22 +77,4 @@
10377
.content-container-inner-description {
10478
font-size: 18px;
10579
}
106-
107-
.input-container-inner {
108-
align-items: center;
109-
display: flex;
110-
flex-direction: row;
111-
justify-content: space-between;
112-
}
113-
114-
.call-input {
115-
font-size: 1.75rem;
116-
padding: 0.785rem;
117-
}
118-
119-
.call-button {
120-
font-size: 1.5rem;
121-
padding: 1rem 2rem;
122-
width: max-content;
123-
}
12480
}

examples/hello/frontend/src/ConnectedContent.tsx

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,67 @@
11
import './ConnectedContent.css';
22

3+
import { type PrimaryWallet } from '@zetachain/wallet';
4+
35
import { NetworkSelector } from './components/NetworkSelector';
46
import type { SupportedChain } from './constants/chains';
7+
import { USE_DYNAMIC_WALLET } from './constants/wallets';
58
import { Footer } from './Footer';
9+
import { useDynamicSwitchChainHook } from './hooks/useDynamicSwitchChainHook';
610
import { useSwitchChain } from './hooks/useSwitchChain';
711
import { MessageFlowCard } from './MessageFlowCard';
812
import type { EIP6963ProviderDetail } from './types/wallet';
913

1014
interface ConnectedContentProps {
11-
selectedProvider: EIP6963ProviderDetail;
15+
selectedProvider: EIP6963ProviderDetail | null;
1216
supportedChain: SupportedChain | undefined;
17+
primaryWallet?: PrimaryWallet | null; // Dynamic wallet from context
1318
}
1419

15-
export function ConnectedContent({
20+
const DynamicConnectedContent = ({
1621
selectedProvider,
1722
supportedChain,
18-
}: ConnectedContentProps) {
23+
primaryWallet,
24+
}: ConnectedContentProps) => {
25+
const { switchChain } = useDynamicSwitchChainHook();
26+
27+
const handleNetworkSelect = (chain: SupportedChain) => {
28+
switchChain(chain.chainId);
29+
};
30+
31+
return (
32+
<div className="main-container">
33+
<div className="content-container">
34+
<div className="content-container-inner">
35+
<div className="content-container-inner-header">
36+
<h1>Say Hello from</h1>
37+
<NetworkSelector
38+
selectedChain={supportedChain}
39+
onNetworkSelect={handleNetworkSelect}
40+
/>
41+
</div>
42+
<p className="content-container-inner-description">
43+
Make a cross-chain call with a message from{' '}
44+
{supportedChain?.name || 'a supported network'} to a universal
45+
contract on ZetaChain that emits a{' '}
46+
<span className="highlight">HelloEvent</span>.
47+
</p>
48+
</div>
49+
<MessageFlowCard
50+
selectedProvider={selectedProvider}
51+
supportedChain={supportedChain}
52+
primaryWallet={primaryWallet}
53+
/>
54+
</div>
55+
<Footer />
56+
</div>
57+
);
58+
};
59+
60+
const Eip6963ConnectedContent = ({
61+
selectedProvider,
62+
supportedChain,
63+
primaryWallet,
64+
}: ConnectedContentProps) => {
1965
const { switchChain } = useSwitchChain();
2066

2167
const handleNetworkSelect = (chain: SupportedChain) => {
@@ -43,9 +89,30 @@ export function ConnectedContent({
4389
<MessageFlowCard
4490
selectedProvider={selectedProvider}
4591
supportedChain={supportedChain}
92+
primaryWallet={primaryWallet}
4693
/>
4794
</div>
4895
<Footer />
4996
</div>
5097
);
98+
};
99+
100+
export function ConnectedContent({
101+
selectedProvider,
102+
supportedChain,
103+
primaryWallet,
104+
}: ConnectedContentProps) {
105+
return USE_DYNAMIC_WALLET ? (
106+
<DynamicConnectedContent
107+
selectedProvider={selectedProvider}
108+
supportedChain={supportedChain}
109+
primaryWallet={primaryWallet}
110+
/>
111+
) : (
112+
<Eip6963ConnectedContent
113+
selectedProvider={selectedProvider}
114+
supportedChain={supportedChain}
115+
primaryWallet={primaryWallet}
116+
/>
117+
);
51118
}

examples/hello/frontend/src/DisconnectedContent.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import './DisconnectedContent.css';
22

3-
import { ConnectWallet } from './components/ConnectWallet';
3+
import { ConnectDynamicWallet } from './components/ConnectDynamicWallet';
4+
import { ConnectEip6963Wallet } from './components/ConnectEip6963Wallet';
45
import { IconAnimation } from './components/icons/IconAnimation';
56
import { IconZetaChainLogo } from './components/icons/IconZetaChainLogo';
7+
import { USE_DYNAMIC_WALLET } from './constants/wallets';
68
import { Footer } from './Footer';
79

810
export function DisconnectedContent() {
@@ -21,7 +23,11 @@ export function DisconnectedContent() {
2123
Connect your EVM wallet and trigger the Universal Hello contract on
2224
ZetaChain testnet from any currently supported EVM chain.
2325
</p>
24-
<ConnectWallet />
26+
{USE_DYNAMIC_WALLET ? (
27+
<ConnectDynamicWallet />
28+
) : (
29+
<ConnectEip6963Wallet />
30+
)}
2531
</div>
2632
<div className="hero-content-animation">
2733
<IconAnimation />
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useUniversalSignInContext } from '@zetachain/wallet/react';
2+
3+
import { ConnectedContent } from './ConnectedContent';
4+
import { SUPPORTED_CHAINS } from './constants/chains';
5+
import { DisconnectedContent } from './DisconnectedContent';
6+
7+
export function DynamicAppContent() {
8+
const { primaryWallet, network } = useUniversalSignInContext();
9+
10+
const account = primaryWallet?.address || null;
11+
const decimalChainId = network || null;
12+
13+
const supportedChain = SUPPORTED_CHAINS.find(
14+
(chain) => chain.chainId === decimalChainId
15+
);
16+
17+
const isDisconnected = !account;
18+
19+
if (isDisconnected) {
20+
return <DisconnectedContent />;
21+
}
22+
23+
return (
24+
<ConnectedContent
25+
selectedProvider={null}
26+
supportedChain={supportedChain}
27+
primaryWallet={primaryWallet}
28+
/>
29+
);
30+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ConnectedContent } from './ConnectedContent';
2+
import { SUPPORTED_CHAINS } from './constants/chains';
3+
import { DisconnectedContent } from './DisconnectedContent';
4+
import { useEip6963Wallet } from './hooks/useEip6963Wallet';
5+
6+
export function Eip6963AppContent() {
7+
const { selectedProvider, decimalChainId } = useEip6963Wallet();
8+
9+
const supportedChain = SUPPORTED_CHAINS.find(
10+
(chain) => chain.chainId === decimalChainId
11+
);
12+
13+
const isDisconnected = !selectedProvider;
14+
15+
if (isDisconnected) {
16+
return <DisconnectedContent />;
17+
}
18+
19+
return (
20+
<ConnectedContent
21+
selectedProvider={selectedProvider}
22+
supportedChain={supportedChain}
23+
/>
24+
);
25+
}

examples/hello/frontend/src/MessageFlowCard.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
11
import './MessageFlowCard.css';
22

33
import { evmCall } from '@zetachain/toolkit/chains/evm';
4-
import { ethers, ZeroAddress } from 'ethers';
4+
import { type PrimaryWallet } from '@zetachain/wallet';
5+
import { ZeroAddress } from 'ethers';
56
import { useEffect, useRef, useState } from 'react';
67

78
import { Button } from './components/Button';
89
import { IconApprove, IconEnvelope, IconSendTitle } from './components/icons';
910
import { ConfirmedContent } from './ConfirmedContent';
1011
import type { SupportedChain } from './constants/chains';
12+
import { HELLO_UNIVERSAL_CONTRACT_ADDRESS } from './constants/contracts';
1113
import type { EIP6963ProviderDetail } from './types/wallet';
14+
import { getSignerAndProvider } from './utils/ethersHelpers';
1215
import { formatNumberWithLocale } from './utils/formatNumber';
1316

1417
interface MessageFlowCardProps {
15-
selectedProvider: EIP6963ProviderDetail;
18+
selectedProvider: EIP6963ProviderDetail | null;
1619
supportedChain: SupportedChain | undefined;
20+
primaryWallet?: PrimaryWallet | null; // Dynamic wallet from context
1721
}
1822

1923
export function MessageFlowCard({
2024
selectedProvider,
2125
supportedChain,
26+
primaryWallet = null,
2227
}: MessageFlowCardProps) {
28+
2329
const MAX_STRING_LENGTH = 2000;
2430
const [isUserSigningTx, setIsUserSigningTx] = useState(false);
2531
const [isTxReceiptLoading, setIsTxReceiptLoading] = useState(false);
@@ -33,17 +39,19 @@ export function MessageFlowCard({
3339

3440
const handleEvmCall = async () => {
3541
try {
36-
const ethersProvider = new ethers.BrowserProvider(
37-
selectedProvider.provider
38-
);
39-
const signer =
40-
(await ethersProvider.getSigner()) as ethers.AbstractSigner;
42+
const signerAndProvider = await getSignerAndProvider({
43+
selectedProvider,
44+
primaryWallet,
45+
});
46+
47+
if (!signerAndProvider) {
48+
throw new Error('Failed to get signer');
49+
}
4150

42-
const helloUniversalContractAddress =
43-
'0x61a184EB30D29eD0395d1ADF38CC7d2F966c4A82';
51+
const { signer } = signerAndProvider;
4452

4553
const evmCallParams = {
46-
receiver: helloUniversalContractAddress,
54+
receiver: HELLO_UNIVERSAL_CONTRACT_ADDRESS,
4755
types: ['string'],
4856
values: [stringValue],
4957
revertOptions: {

0 commit comments

Comments
 (0)