Skip to content

Commit 200208c

Browse files
authored
Add fast fill demo support for all vms (#926)
1 parent d9d384c commit 200208c

File tree

2 files changed

+238
-110
lines changed

2 files changed

+238
-110
lines changed

demo/pages/sdk/actions/fastFill.tsx

Lines changed: 154 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,132 +1,127 @@
11
import { NextPage } from 'next'
2-
import { useMemo, useState } from 'react'
3-
import { useWalletClient } from 'wagmi'
2+
import { useEffect, useMemo, useState } from 'react'
43
import { useRelayClient } from '@relayprotocol/relay-kit-ui'
54
import { ConnectButton } from 'components/ConnectButton'
65
import {
7-
adaptViemWallet,
86
type QuoteBody,
97
type AdaptedWallet,
108
type Execute,
11-
type TransactionStepItem
9+
adaptViemWallet
1210
} from '@relayprotocol/relay-sdk'
11+
import { createFastFillWallet } from 'utils/createFastFillWallet'
12+
import { useDynamicContext } from '@dynamic-labs/sdk-react-core'
13+
import { isEthereumWallet } from '@dynamic-labs/ethereum'
14+
import { isSolanaWallet } from '@dynamic-labs/solana'
15+
import { isBitcoinWallet } from '@dynamic-labs/bitcoin'
16+
import { isSuiWallet } from '@dynamic-labs/sui'
17+
import { isTronWallet, type TronWallet } from '@dynamic-labs/tron'
18+
import { adaptSolanaWallet } from '@relayprotocol/relay-svm-wallet-adapter'
19+
import { adaptBitcoinWallet } from '@relayprotocol/relay-bitcoin-wallet-adapter'
20+
import { adaptSuiWallet } from '@relayprotocol/relay-sui-wallet-adapter'
21+
import { adaptTronWallet } from '@relayprotocol/relay-tron-wallet-adapter'
1322

1423
const FastFillPage: NextPage = () => {
15-
const { data: wallet } = useWalletClient()
1624
const client = useRelayClient()
25+
const { primaryWallet } = useDynamicContext()
1726
const [quoteParams, setQuoteParams] = useState<string>('')
1827
const [quote, setQuote] = useState<Execute | null>(null)
1928
const [result, setResult] = useState<any>(null)
2029
const [progress, setProgress] = useState<any>(null)
2130
const [loading, setLoading] = useState(false)
2231
const [error, setError] = useState<string | null>(null)
2332
const [fastFillPassword, setFastFillPassword] = useState<string>('')
33+
const [solverInputCurrencyAmount, setSolverInputCurrencyAmount] = useState<string>('')
34+
const [adaptedWallet, setAdaptedWallet] = useState<AdaptedWallet | null>(null)
2435

25-
// Create a custom wallet adapter that wraps the viemWallet adapter
26-
// and intercepts handleSendTransactionStep to call fastFill
27-
const createFastFillWalletAdapter = (
28-
originalWallet: AdaptedWallet,
29-
password: string
30-
): AdaptedWallet => {
31-
return {
32-
...originalWallet,
33-
handleSendTransactionStep: async (
34-
chainId: number,
35-
stepItem: TransactionStepItem,
36-
step: Execute['steps'][0]
37-
) => {
38-
const txHash = await originalWallet.handleSendTransactionStep(
39-
chainId,
40-
stepItem,
41-
step
42-
)
43-
// Call fastFill proxy API if requestId is available
44-
if (step.requestId && step.id === 'deposit') {
45-
try {
46-
console.log('Calling fastFill proxy for requestId:', step.requestId)
47-
const response = await fetch('/api/fast-fill', {
48-
method: 'POST',
49-
headers: {
50-
'Content-Type': 'application/json'
51-
},
52-
body: JSON.stringify({
53-
requestId: step.requestId,
54-
password
55-
})
56-
})
57-
58-
if (response.ok) {
59-
const data = await response.json()
60-
console.log('FastFill called successfully:', data)
61-
} else {
62-
const error = await response.json()
63-
console.warn(
64-
'FastFill error (continuing with transaction):',
65-
error.error || error.message
66-
)
36+
// Adapt wallet whenever primaryWallet changes
37+
useEffect(() => {
38+
const adaptWallet = async () => {
39+
if (!primaryWallet) {
40+
setAdaptedWallet(null)
41+
return
42+
}
43+
44+
try {
45+
let wallet: AdaptedWallet | null = null
46+
47+
if (isEthereumWallet(primaryWallet)) {
48+
const walletClient = await primaryWallet.getWalletClient()
49+
wallet = adaptViemWallet(walletClient)
50+
} else if (isSolanaWallet(primaryWallet)) {
51+
const connection = await primaryWallet.getConnection()
52+
const signer = await primaryWallet.getSigner()
53+
54+
if (!connection || !signer?.signTransaction) {
55+
throw new Error('Unable to setup Solana wallet')
56+
}
57+
58+
wallet = adaptSolanaWallet(
59+
primaryWallet.address,
60+
792703809, // Solana chain ID
61+
connection,
62+
signer.signAndSendTransaction
63+
)
64+
} else if (isBitcoinWallet(primaryWallet)) {
65+
wallet = adaptBitcoinWallet(
66+
primaryWallet.address,
67+
async (_address, _psbt, dynamicParams) => {
68+
const response = await primaryWallet.signPsbt(dynamicParams)
69+
if (!response) {
70+
throw new Error('Missing psbt response')
71+
}
72+
return response.signedPsbt
6773
}
68-
} catch (e: any) {
69-
// Log error but don't fail the transaction
70-
console.warn(
71-
'FastFill error (continuing with transaction):',
72-
e?.message || String(e)
73-
)
74+
)
75+
} else if (isSuiWallet(primaryWallet)) {
76+
const walletClient = await primaryWallet.getWalletClient()
77+
78+
if (!walletClient) {
79+
throw new Error('Unable to setup Sui wallet')
7480
}
75-
}
7681

77-
return txHash
78-
},
79-
handleBatchTransactionStep: async (chainId, items, step) => {
80-
const txHash = await originalWallet?.handleBatchTransactionStep?.(
81-
chainId,
82-
items,
83-
step
84-
)
85-
// Call fastFill proxy API if requestId is available
86-
if (txHash) {
87-
try {
88-
console.log('Calling fastFill proxy for requestId:', step.requestId)
89-
const response = await fetch('/api/fast-fill', {
90-
method: 'POST',
91-
headers: {
92-
'Content-Type': 'application/json'
93-
},
94-
body: JSON.stringify({
95-
requestId: step.requestId,
96-
password
82+
wallet = adaptSuiWallet(
83+
primaryWallet.address,
84+
784, // Sui chain ID placeholder - will be updated based on quote params
85+
walletClient,
86+
async (tx) => {
87+
const signedTransaction = await primaryWallet.signTransaction(tx)
88+
const executionResult = await walletClient.executeTransactionBlock({
89+
options: {},
90+
signature: signedTransaction.signature,
91+
transactionBlock: signedTransaction.bytes
9792
})
98-
})
99-
100-
if (response.ok) {
101-
const data = await response.json()
102-
console.log('FastFill called successfully:', data)
103-
} else {
104-
const error = await response.json()
105-
console.warn(
106-
'FastFill error (continuing with transaction):',
107-
error.error || error.message
108-
)
93+
return executionResult
10994
}
110-
} catch (e: any) {
111-
// Log error but don't fail the transaction
112-
console.warn(
113-
'FastFill error (continuing with transaction):',
114-
e?.message || String(e)
115-
)
95+
)
96+
} else if (isTronWallet(primaryWallet)) {
97+
const tronWeb = (primaryWallet as TronWallet).getTronWeb()
98+
if (!tronWeb) {
99+
throw new Error('Unable to setup Tron wallet')
116100
}
101+
wallet = adaptTronWallet(
102+
(primaryWallet as TronWallet).address,
103+
tronWeb
104+
)
117105
}
106+
// Note: Hyperliquid support can be added here following a similar pattern
118107

119-
return txHash
108+
setAdaptedWallet(wallet)
109+
} catch (e: any) {
110+
console.error('Error adapting wallet:', e)
111+
setError(`Error adapting wallet: ${e?.message || String(e)}`)
112+
setAdaptedWallet(null)
120113
}
121114
}
122-
}
115+
116+
adaptWallet()
117+
}, [primaryWallet])
123118

124119
const handleExecute = async () => {
125120
if (!client) {
126121
setError('Missing Client!')
127122
return
128123
}
129-
if (!wallet) {
124+
if (!adaptedWallet) {
130125
setError('Please connect your wallet!')
131126
return
132127
}
@@ -147,13 +142,11 @@ const FastFillPage: NextPage = () => {
147142
}
148143

149144
try {
150-
// Adapt the wallet
151-
const adaptedWallet = adaptViemWallet(wallet)
152-
153-
// Wrap it with our fastFill adapter
154-
const fastFillWallet = createFastFillWalletAdapter(
145+
// Wrap the adapted wallet with our fastFill wrapper
146+
const fastFillWallet = createFastFillWallet(
155147
adaptedWallet,
156-
fastFillPassword
148+
fastFillPassword,
149+
solverInputCurrencyAmount || undefined
157150
)
158151

159152
// Execute the quote with the fastFill wallet adapter
@@ -176,7 +169,7 @@ const FastFillPage: NextPage = () => {
176169
setError('Missing Client!')
177170
return
178171
}
179-
if (!wallet) {
172+
if (!adaptedWallet || !primaryWallet) {
180173
setError('Please connect your wallet!')
181174
return
182175
}
@@ -195,13 +188,13 @@ const FastFillPage: NextPage = () => {
195188
return
196189
}
197190

198-
const userAddress = wallet.account?.address
191+
const userAddress = primaryWallet.address
199192
if (!userAddress) {
200193
setError('Could not get address from connected wallet')
201194
setLoading(false)
202195
return
203196
}
204-
const adaptedWallet = adaptViemWallet(wallet)
197+
205198
const quoteResult = await client.actions.getQuote(
206199
{
207200
chainId: params.originChainId,
@@ -231,7 +224,7 @@ const FastFillPage: NextPage = () => {
231224
} catch (e) {
232225
return ''
233226
}
234-
}, [result])
227+
}, [progress])
235228

236229
return (
237230
<div
@@ -248,6 +241,20 @@ const FastFillPage: NextPage = () => {
248241
>
249242
<ConnectButton />
250243

244+
{adaptedWallet && (
245+
<div
246+
style={{
247+
width: '100%',
248+
padding: '10px',
249+
background: '#e0f0ff',
250+
borderRadius: '8px',
251+
marginBottom: 10
252+
}}
253+
>
254+
<b>Connected Wallet VM Type:</b> {adaptedWallet.vmType}
255+
</div>
256+
)}
257+
251258
<div style={{ width: '100%' }}>
252259
<label style={{ display: 'block', marginBottom: 8, fontWeight: 600 }}>
253260
Quote Parameters (JSON):
@@ -262,12 +269,13 @@ const FastFillPage: NextPage = () => {
262269
}}
263270
>
264271
Note that user/recipient are not required — they are taken from the
265-
connected wallet.
272+
connected wallet. This demo now supports all wallet types: EVM, Solana
273+
(SVM), Bitcoin (BVM), Sui, Tron (TVM), and Hyperliquid.
266274
</div>
267275
<textarea
268276
value={quoteParams}
269277
onChange={(e) => setQuoteParams(e.target.value)}
270-
placeholder='{"chainId": 1, "toChainId": 8453, "currency": "0x0000000000000000000000000000000000000000", "toCurrency": "0x0000000000000000000000000000000000000000", "amount": "1000000000000000000", "tradeType": "EXACT_INPUT"}'
278+
placeholder='{"originChainId": 1, "destinationChainId": 8453, "originCurrency": "0x0000000000000000000000000000000000000000", "destinationCurrency": "0x0000000000000000000000000000000000000000", "amount": "1000000000000000000", "tradeType": "EXACT_INPUT"}'
271279
style={{
272280
width: '100%',
273281
minHeight: 150,
@@ -292,7 +300,7 @@ const FastFillPage: NextPage = () => {
292300
cursor: 'pointer',
293301
width: '100%'
294302
}}
295-
disabled={!wallet || loading || !quoteParams.trim()}
303+
disabled={!adaptedWallet || loading || !quoteParams.trim()}
296304
onClick={handleGetQuote}
297305
>
298306
{loading && !quote ? 'Getting Quote...' : 'Get Quote'}
@@ -308,9 +316,9 @@ const FastFillPage: NextPage = () => {
308316
marginTop: 10
309317
}}
310318
>
311-
<b>Quote obtained! Request IDs:</b>
319+
<b>Quote obtained! Request ID:</b>
312320
<pre style={{ fontSize: 11, overflow: 'auto', maxHeight: 200 }}>
313-
{quote.steps.find((step) => step.requestId)?.requestId ?? ''}
321+
{quote.steps.find((step) => step.requestId)?.requestId ?? 'No request ID found'}
314322
</pre>
315323
</div>
316324
)}
@@ -334,6 +342,25 @@ const FastFillPage: NextPage = () => {
334342
/>
335343
</div>
336344

345+
<div style={{ width: '100%' }}>
346+
<label style={{ display: 'block', marginBottom: 8, fontWeight: 600 }}>
347+
Solver Input Currency Amount (Optional):
348+
</label>
349+
<input
350+
type="text"
351+
value={solverInputCurrencyAmount}
352+
onChange={(e) => setSolverInputCurrencyAmount(e.target.value)}
353+
placeholder="Enter solver input currency amount"
354+
style={{
355+
width: '100%',
356+
padding: 12,
357+
fontSize: 14,
358+
border: '1px solid #ccc',
359+
borderRadius: 8
360+
}}
361+
/>
362+
</div>
363+
337364
<button
338365
style={{
339366
marginTop: 20,
@@ -347,7 +374,7 @@ const FastFillPage: NextPage = () => {
347374
cursor: 'pointer',
348375
width: '100%'
349376
}}
350-
disabled={!wallet || !quote || loading || !fastFillPassword.trim()}
377+
disabled={!adaptedWallet || !quote || loading || !fastFillPassword.trim()}
351378
onClick={handleExecute}
352379
>
353380
{loading ? 'Executing with Fast Fill...' : 'Execute with Fast Fill'}
@@ -375,6 +402,23 @@ const FastFillPage: NextPage = () => {
375402
</pre>
376403
</div>
377404
)}
405+
406+
{result && (
407+
<div
408+
style={{
409+
marginTop: 20,
410+
padding: '10px',
411+
background: '#e0ffe0',
412+
borderRadius: '8px',
413+
width: '100%'
414+
}}
415+
>
416+
<b>Result:</b>
417+
<pre style={{ fontSize: 11, overflow: 'auto', maxHeight: 300 }}>
418+
{JSON.stringify(result, null, 2)}
419+
</pre>
420+
</div>
421+
)}
378422
</div>
379423
)
380424
}

0 commit comments

Comments
 (0)