@@ -4,9 +4,9 @@ import globalContext from '../..';
44// Constants
55const UNIVERSAL_ROUTER = '0x66a9893cc07d91d95644aedd05d03f95e1dba8af' ;
66const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' ;
7- const FEE_RECIPIENT = '0x58c51ee8998e8ef06362df26a0d966bbd0cf5113' ;
8- const ETH_AMOUNT = '0x10a741a462780 ' ; // 0.0003 ETH in hex
9- const FEE_BIPS = 5000 ; // 50% in basis points
7+ const DEFAULT_FEE_RECIPIENT = '0x58c51ee8998e8ef06362df26a0d966bbd0cf5113' ;
8+ const DEFAULT_ETH_AMOUNT = '0.0003 ' ; // 0.0003 ETH
9+ const DEFAULT_FEE_PERCENTAGE = 50 ; // 50%
1010const EMPTY_BYTES = '0x' ;
1111
1212const Actions = {
@@ -42,6 +42,83 @@ const V4_BASE_ACTIONS_ABI_DEFINITION = {
4242 ] ,
4343} ;
4444
45+ // Helper functions for input validation and parsing
46+ function isValidAddress ( address ) {
47+ return ethers . utils . isAddress ( address ) ;
48+ }
49+
50+ function parseEthAmount ( input ) {
51+ const value = input . trim ( ) ;
52+ if ( ! value ) {
53+ return null ;
54+ }
55+
56+ try {
57+ const parsed = parseFloat ( value ) ;
58+ if ( isNaN ( parsed ) || parsed < 0 ) {
59+ return null ;
60+ }
61+ // Convert ETH to wei and then to hex
62+ return ethers . utils . parseEther ( value . toString ( ) ) . toHexString ( ) ;
63+ } catch {
64+ return null ;
65+ }
66+ }
67+
68+ function parseFeePercentage ( input ) {
69+ const value = input . trim ( ) ;
70+ if ( ! value ) {
71+ return null ;
72+ }
73+
74+ try {
75+ const parsed = parseFloat ( value ) ;
76+ if ( isNaN ( parsed ) || parsed < 0 || parsed > 100 ) {
77+ return null ;
78+ }
79+ // Convert percentage to basis points (1% = 100 basis points)
80+ return Math . round ( parsed * 100 ) ;
81+ } catch {
82+ return null ;
83+ }
84+ }
85+
86+ function getConfigValues ( ) {
87+ const feeRecipientElement = document . getElementById ( 'feeRecipientInput' ) ;
88+ const ethAmountElement = document . getElementById ( 'ethAmountInput' ) ;
89+ const feePercentageElement = document . getElementById ( 'feePercentageInput' ) ;
90+
91+ const feeRecipientInput = feeRecipientElement
92+ ? feeRecipientElement . value . trim ( )
93+ : '' ;
94+ const ethAmountInput = ethAmountElement ? ethAmountElement . value . trim ( ) : '' ;
95+ const feePercentageInput = feePercentageElement
96+ ? feePercentageElement . value . trim ( )
97+ : '' ;
98+
99+ // Use defaults if inputs are empty or invalid
100+ const feeRecipient =
101+ feeRecipientInput && isValidAddress ( feeRecipientInput )
102+ ? feeRecipientInput
103+ : DEFAULT_FEE_RECIPIENT ;
104+
105+ const ethAmountHex =
106+ parseEthAmount ( ethAmountInput ) ||
107+ ethers . utils . parseEther ( DEFAULT_ETH_AMOUNT ) . toHexString ( ) ;
108+
109+ const feeBips =
110+ parseFeePercentage ( feePercentageInput ) || DEFAULT_FEE_PERCENTAGE * 100 ;
111+
112+ return {
113+ feeRecipient,
114+ ethAmountHex,
115+ feeBips,
116+ ethAmountDisplay : ethAmountInput || DEFAULT_ETH_AMOUNT ,
117+ feePercentageDisplay :
118+ feePercentageInput || DEFAULT_FEE_PERCENTAGE . toString ( ) ,
119+ } ;
120+ }
121+
45122function createAction ( action , parameters ) {
46123 const encodedInput = ethers . utils . defaultAbiCoder . encode (
47124 V4_BASE_ACTIONS_ABI_DEFINITION [ action ] . map ( ( v ) => v . type ) ,
@@ -72,22 +149,87 @@ export function swapComparisonComponent(parentContainer) {
72149 </h4>
73150
74151 <p class="info-text alert alert-warning">
75- ⚠️ This swap includes a 50% fee deduction for testing purposes
152+ ⚠️ This swap includes a high fee for testing purposes
76153 </p>
77154
78155 <div class="alert alert-secondary">
79- <strong>Swap Details:</strong><br/>
80- • From: 0.0003 ETH<br/>
156+ <strong>Current Swap Details:</strong><br/>
157+ <span id="swapDetailsDisplay">
158+ • From: <span id="displayEthAmount">${ DEFAULT_ETH_AMOUNT } </span> ETH<br/>
81159 • To: USDC<br/>
82- • Fee: 50% of output
160+ • Fee: <span id="displayFeePercentage">${ DEFAULT_FEE_PERCENTAGE } </span>% of output
161+ </span>
162+ </div>
163+
164+ <!-- Configuration Section -->
165+ <div class="card mb-3" style="background-color: #f8f9fa;">
166+ <div class="card-body">
167+ <h6 class="card-subtitle mb-3 text-muted">
168+ <strong>Configuration (Optional)</strong>
169+ </h6>
170+
171+ <div class="form-group mb-3">
172+ <label for="ethAmountInput" style="font-size: 0.9em;">
173+ <strong>ETH Amount</strong>
174+ </label>
175+ <input
176+ type="number"
177+ class="form-control"
178+ id="ethAmountInput"
179+ placeholder="${ DEFAULT_ETH_AMOUNT } "
180+ step="0.0001"
181+ min="0"
182+ style="font-size: 0.9em;"
183+ />
184+ <small class="form-text text-muted">Leave empty for default (${ DEFAULT_ETH_AMOUNT } ETH)</small>
185+ </div>
186+
187+ <div class="form-group mb-3">
188+ <label for="feePercentageInput" style="font-size: 0.9em;">
189+ <strong>Fee Percentage</strong>
190+ </label>
191+ <input
192+ type="number"
193+ class="form-control"
194+ id="feePercentageInput"
195+ placeholder="${ DEFAULT_FEE_PERCENTAGE } "
196+ step="1"
197+ min="0"
198+ max="100"
199+ style="font-size: 0.9em;"
200+ />
201+ <small class="form-text text-muted">Leave empty for default (${ DEFAULT_FEE_PERCENTAGE } %)</small>
202+ </div>
203+
204+ <div class="form-group mb-3">
205+ <label for="feeRecipientInput" style="font-size: 0.9em;">
206+ <strong>Fee Recipient Address</strong>
207+ </label>
208+ <input
209+ type="text"
210+ class="form-control"
211+ id="feeRecipientInput"
212+ placeholder="${ DEFAULT_FEE_RECIPIENT } "
213+ style="font-size: 0.85em; font-family: monospace;"
214+ />
215+ <small class="form-text text-muted">Leave empty for default address</small>
216+ </div>
217+
218+ <button
219+ class="btn btn-sm btn-secondary btn-block"
220+ id="resetConfigButton"
221+ >
222+ Reset to Defaults
223+ </button>
224+ </div>
83225 </div>
84226
85227 <button
86228 class="btn btn-primary btn-lg btn-block mb-3"
87229 id="swapComparisonSwapButton"
88230 disabled
89231 >
90- Swap ETH to USDC (50% Fee)
232+ Swap ETH to USDC
91233 </button>
92234
93235 <p class="info-text alert alert-secondary">
@@ -105,6 +247,31 @@ export function swapComparisonComponent(parentContainer) {
105247 const swapButton = document . getElementById ( 'swapComparisonSwapButton' ) ;
106248 const statusDisplay = document . getElementById ( 'swapStatus' ) ;
107249 const txHashDisplay = document . getElementById ( 'swapTxHash' ) ;
250+ const resetButton = document . getElementById ( 'resetConfigButton' ) ;
251+ const ethAmountInput = document . getElementById ( 'ethAmountInput' ) ;
252+ const feePercentageInput = document . getElementById ( 'feePercentageInput' ) ;
253+ const feeRecipientInput = document . getElementById ( 'feeRecipientInput' ) ;
254+ const displayEthAmount = document . getElementById ( 'displayEthAmount' ) ;
255+ const displayFeePercentage = document . getElementById ( 'displayFeePercentage' ) ;
256+
257+ // Function to update the swap details display
258+ function updateSwapDetailsDisplay ( ) {
259+ const config = getConfigValues ( ) ;
260+ displayEthAmount . textContent = config . ethAmountDisplay ;
261+ displayFeePercentage . textContent = config . feePercentageDisplay ;
262+ }
263+
264+ // Add event listeners for input changes to update display
265+ ethAmountInput . addEventListener ( 'input' , updateSwapDetailsDisplay ) ;
266+ feePercentageInput . addEventListener ( 'input' , updateSwapDetailsDisplay ) ;
267+
268+ // Reset button handler
269+ resetButton . addEventListener ( 'click' , function ( ) {
270+ ethAmountInput . value = '' ;
271+ feePercentageInput . value = '' ;
272+ feeRecipientInput . value = '' ;
273+ updateSwapDetailsDisplay ( ) ;
274+ } ) ;
108275
109276 document . addEventListener ( 'globalConnectionChange' , function ( e ) {
110277 if ( e . detail . connected ) {
@@ -122,25 +289,38 @@ export function swapComparisonComponent(parentContainer) {
122289 swapButton . onclick = async ( ) => {
123290 try {
124291 statusDisplay . innerHTML = 'Building transaction...' ;
292+
293+ // Get configuration values from inputs or use defaults
294+ const config = getConfigValues ( ) ;
295+
125296 console . log ( '=== Swap Comparison Transaction ===' ) ;
126297 console . log ( 'Router:' , UNIVERSAL_ROUTER ) ;
127- console . log ( 'ETH Amount:' , ETH_AMOUNT , '(0.0003 ETH)' ) ;
128- console . log ( 'Fee Recipient:' , FEE_RECIPIENT ) ;
129- console . log ( 'Fee %:' , FEE_BIPS / 100 , '%' ) ;
298+ console . log (
299+ 'ETH Amount:' ,
300+ config . ethAmountHex ,
301+ `(${ config . ethAmountDisplay } ETH)` ,
302+ ) ;
303+ console . log ( 'Fee Recipient:' , config . feeRecipient ) ;
304+ console . log ( 'Fee %:' , config . feeBips / 100 , '%' ) ;
130305
131306 // Use working example as template and modify only necessary values
132307 const deadline = Math . floor ( Date . now ( ) / 1000 ) + 1200 ; // 20 minutes from now
133308
134309 // Build calldata using template from working example
135- const data = buildCalldata ( deadline , FEE_RECIPIENT , FEE_BIPS ) ;
310+ const data = buildCalldata (
311+ deadline ,
312+ config . feeRecipient ,
313+ config . feeBips ,
314+ config . ethAmountHex ,
315+ ) ;
136316
137317 statusDisplay . innerHTML = 'Sending transaction...' ;
138318
139319 // Send the transaction
140320 const txParams = {
141321 from : globalContext . accounts [ 0 ] ,
142322 to : UNIVERSAL_ROUTER ,
143- value : ETH_AMOUNT ,
323+ value : config . ethAmountHex ,
144324 data,
145325 } ;
146326
@@ -166,16 +346,20 @@ export function swapComparisonComponent(parentContainer) {
166346
167347/**
168348 * Build calldata using the working example as a template
169- * Only modifies: deadline, fee recipient, fee bips, and user address
349+ * Only modifies: deadline, fee recipient, fee bips, eth amount, and user address
170350 */
171- function buildCalldata ( deadline , feeRecipient , feeBips ) {
351+ function buildCalldata ( deadline , feeRecipient , feeBips , ethAmount ) {
172352 // Working example from the original transaction
173353 // We'll use ethers to properly encode with our values
174354 const commands = '0x10' ; // V4_SWAP
175355
176356 // V4_SWAP input - extracted from working example
177357 // This is the complex nested structure that we keep as-is
178- const v4SwapInput = buildV4SwapInputFromExample ( feeRecipient , feeBips ) ;
358+ const v4SwapInput = buildV4SwapInputFromExample (
359+ feeRecipient ,
360+ feeBips ,
361+ ethAmount ,
362+ ) ;
179363
180364 // Encode the execute function call
181365 const inputs = [ v4SwapInput ] ; //, payPortionInput, sweepInput];
@@ -197,7 +381,7 @@ function buildCalldata(deadline, feeRecipient, feeBips) {
197381 * Build V4_SWAP input from working example structure
198382 * This uses the exact structure from a known working transaction
199383 */
200- function buildV4SwapInputFromExample ( feeRecipient , feeBips ) {
384+ function buildV4SwapInputFromExample ( feeRecipient , feeBips , ethAmount ) {
201385 // From working example: actions = 0x070b0e
202386 let v4Actions = EMPTY_BYTES ;
203387 const v4Params = [ ] ;
@@ -219,7 +403,7 @@ function buildV4SwapInputFromExample(feeRecipient, feeBips) {
219403 {
220404 poolKey,
221405 zeroForOne : true , // The direction of swap is ETH to USDC. Change it to 'false' for the reverse direction
222- amountIn : ETH_AMOUNT ,
406+ amountIn : ethAmount ,
223407 amountOutMinimum, // Change according to the slippage desired
224408 hookData : '0x00' ,
225409 } ,
@@ -229,7 +413,7 @@ function buildV4SwapInputFromExample(feeRecipient, feeBips) {
229413
230414 const settleAll = addAction ( Actions . SETTLE_ALL , [
231415 poolKey . currency0 ,
232- ETH_AMOUNT ,
416+ ethAmount ,
233417 ] ) ;
234418 v4Actions = v4Actions . concat ( settleAll . newAction ) ;
235419 v4Params . push ( settleAll . newParam ) ;
0 commit comments