@@ -10,7 +10,10 @@ import {
1010 hasDuplicateConfirmationAmounts ,
1111 isfeePercentageKey ,
1212 isToggableFeeKey ,
13- formatCap
13+ formatCap ,
14+ isExcessToleranceFixedKey ,
15+ validateExcessToleranceFixed ,
16+ validateExcessTolerancePercentage
1417} from './configUtils.js' ;
1518
1619const generalChanged = { value : false } ;
@@ -45,7 +48,12 @@ const populateProviderData = (providerData, rskAddress, btcAddress, coldWallet)
4548 setTextContent ( 'isOperational' , providerData . status ? "Operational" : "Not Operational" ) ;
4649} ;
4750
48- const createInput = ( section , key , value ) => {
51+ const createInput = ( section , key , value , config ) => {
52+ // Skip excessTolerancePercentage - handled together with excessToleranceFixed
53+ if ( key === 'excessTolerancePercentage' ) {
54+ return ;
55+ }
56+
4957 const div = document . createElement ( 'div' ) ;
5058 div . classList . add ( 'mb-3' ) ;
5159
@@ -63,6 +71,10 @@ const createInput = (section, key, value) => {
6371
6472 if ( typeof value === 'boolean' ) {
6573 createCheckboxInput ( inputContainer , section , key , value ) ;
74+ } else if ( isExcessToleranceFixedKey ( key ) ) {
75+ // Handle excess tolerance with combined fixed/percentage toggle
76+ const percentageValue = config ?. excessTolerancePercentage ?? 0 ;
77+ createExcessToleranceInput ( inputContainer , label , section , value , percentageValue ) ;
6678 } else if ( isToggableFeeKey ( key ) ) {
6779 createToggableFeeInput ( inputContainer , label , section , key , value ) ;
6880 } else if ( isFeeKey ( key ) ) {
@@ -136,6 +148,86 @@ const createToggableFeeInput = (inputContainer, label, section, key, value) => {
136148 label . appendChild ( questionIcon ) ;
137149} ;
138150
151+ const createExcessToleranceInput = ( inputContainer , label , section , fixedValue , percentageValue ) => {
152+ // Toggle: ON = Fixed mode, OFF = Percentage mode
153+ const toggle = document . createElement ( 'input' ) ;
154+ toggle . type = 'checkbox' ;
155+ toggle . classList . add ( 'form-check-input' ) ;
156+ toggle . style . marginRight = '10px' ;
157+ toggle . dataset . key = 'excessTolerance_isFixed' ;
158+ toggle . setAttribute ( 'data-testid' , `config-${ section . id . replace ( 'Config' , '' ) } -excessTolerance-toggle` ) ;
159+
160+ const toggleLabel = document . createElement ( 'span' ) ;
161+ toggleLabel . classList . add ( 'toggle-label' ) ;
162+ toggleLabel . style . marginRight = '10px' ;
163+ toggleLabel . style . fontSize = '0.85em' ;
164+ toggleLabel . style . color = '#666' ;
165+
166+ const input = document . createElement ( 'input' ) ;
167+ input . type = 'text' ;
168+ input . style . width = '40%' ;
169+ input . classList . add ( 'form-control' ) ;
170+ input . dataset . key = 'excessTolerance_value' ;
171+ input . setAttribute ( 'data-testid' , `config-${ section . id . replace ( 'Config' , '' ) } -excessTolerance-input` ) ;
172+
173+ // Store original values for reference
174+ input . dataset . originalFixedValue = fixedValue ;
175+ input . dataset . originalPercentageValue = percentageValue ;
176+
177+ // Determine initial mode based on which value is non-zero
178+ const fixedNum = parseFloat ( fixedValue ) || 0 ;
179+ const percentageNum = parseFloat ( percentageValue ) || 0 ;
180+
181+ if ( fixedNum > 0 ) {
182+ // Fixed mode
183+ toggle . checked = true ;
184+ toggleLabel . textContent = 'Fixed' ;
185+ input . value = weiToEther ( fixedValue ) ;
186+ input . placeholder = 'Enter amount in rBTC' ;
187+ } else if ( percentageNum > 0 ) {
188+ // Percentage mode
189+ toggle . checked = false ;
190+ toggleLabel . textContent = 'Percentage' ;
191+ input . value = percentageValue ;
192+ input . placeholder = 'Enter percentage (0-100)' ;
193+ } else {
194+ // Default to percentage mode when both are 0
195+ toggle . checked = false ;
196+ toggleLabel . textContent = 'Percentage' ;
197+ input . value = '0' ;
198+ input . placeholder = 'Enter percentage (0-100)' ;
199+ }
200+
201+ toggle . addEventListener ( 'change' , ( ) => {
202+ if ( toggle . checked ) {
203+ // Fixed mode
204+ toggleLabel . textContent = 'Fixed' ;
205+ input . placeholder = 'Enter amount in rBTC' ;
206+ // Convert current value if possible, otherwise clear
207+ const currentVal = parseFloat ( input . value ) ;
208+ if ( ! isNaN ( currentVal ) && currentVal > 0 ) {
209+ input . value = '' ;
210+ } else {
211+ input . value = '0' ;
212+ }
213+ } else {
214+ // Percentage mode
215+ toggleLabel . textContent = 'Percentage' ;
216+ input . placeholder = 'Enter percentage (0-100)' ;
217+ input . value = '0' ;
218+ }
219+ setChanged ( section . id ) ;
220+ } ) ;
221+
222+ input . addEventListener ( 'input' , ( ) => setChanged ( section . id ) ) ;
223+
224+ inputContainer . appendChild ( toggle ) ;
225+ inputContainer . appendChild ( toggleLabel ) ;
226+ inputContainer . appendChild ( input ) ;
227+ const questionIcon = createQuestionIcon ( getTooltipText ( 'excessTolerance' ) ) ;
228+ label . appendChild ( questionIcon ) ;
229+ } ;
230+
139231const createFeeInput = ( inputContainer , label , section , key , value ) => {
140232 const input = document . createElement ( 'input' ) ;
141233 input . type = 'text' ;
@@ -196,7 +288,8 @@ const createQuestionIcon = (tooltipText) => {
196288
197289const getDisplayLabel = ( key ) => {
198290 const labels = {
199- maxLiquidity : 'Maximum Liquidity'
291+ maxLiquidity : 'Maximum Liquidity' ,
292+ excessToleranceFixed : 'Excess Tolerance'
200293 } ;
201294 return labels [ key ] || key ;
202295} ;
@@ -213,7 +306,8 @@ const getTooltipText = (key) => {
213306 bridgeTransactionMin : 'The amount of rBTC that needs to be gathered in peg out refunds before executing a native peg out.' ,
214307 fixedFee : 'A fixed fee charged for transactions.' ,
215308 feePercentage : 'A percentage fee charged based on the transaction amount.' ,
216- maxLiquidity : 'The maximum liquidity (in rBTC) the provider is willing to offer. Must be a positive value with up to 18 decimal places.'
309+ maxLiquidity : 'The maximum liquidity (in rBTC) the provider is willing to offer. Must be a positive value with up to 18 decimal places.' ,
310+ excessTolerance : 'The excess tolerance for transactions. Toggle ON for a fixed amount (in rBTC), OFF for a percentage (0-100%).'
217311 } ;
218312 return tooltips [ key ] || 'No description available' ;
219313} ;
@@ -333,7 +427,7 @@ const populateConfigSection = (sectionId, config) => {
333427 if ( key === 'rskConfirmations' || key === 'btcConfirmations' ) {
334428 createConfirmationConfig ( section , key , value ) ;
335429 } else {
336- createInput ( section , key , value ) ;
430+ createInput ( section , key , value , config ) ;
337431 }
338432 } ) ;
339433} ;
@@ -483,6 +577,11 @@ function getRegularConfig(sectionId) {
483577 const key = input . dataset . key ;
484578 let value ;
485579
580+ // Skip excess tolerance value - handled separately
581+ if ( key === 'excessTolerance_value' ) {
582+ return ;
583+ }
584+
486585 if ( input . disabled ) {
487586 if ( isfeePercentageKey ( key ) ) {
488587 value = 0 ;
@@ -541,9 +640,46 @@ function getRegularConfig(sectionId) {
541640
542641 checkboxes . forEach ( input => {
543642 const key = input . dataset . key ;
544- if ( ! key . endsWith ( '_enabled' ) ) config [ key ] = input . checked ;
643+ if ( ! key . endsWith ( '_enabled' ) && key !== 'excessTolerance_isFixed' ) {
644+ config [ key ] = input . checked ;
645+ }
545646 } ) ;
546647
648+ // Handle excess tolerance separately
649+ const excessToleranceToggle = document . querySelector ( `#${ sectionId } input[data-key="excessTolerance_isFixed"]` ) ;
650+ const excessToleranceInput = document . querySelector ( `#${ sectionId } input[data-key="excessTolerance_value"]` ) ;
651+
652+ if ( excessToleranceToggle && excessToleranceInput ) {
653+ const isFixed = excessToleranceToggle . checked ;
654+ const rawValue = excessToleranceInput . value . trim ( ) ;
655+
656+ if ( isFixed ) {
657+ // Fixed mode - validate as non-negative number and convert to wei
658+ const validation = validateExcessToleranceFixed ( rawValue ) ;
659+ if ( ! validation . isValid ) {
660+ showErrorToast ( `"${ sectionId } ": ${ validation . error } ` ) ;
661+ throw new Error ( validation . error ) ;
662+ }
663+ try {
664+ config . excessToleranceFixed = rawValue === '' || rawValue === '0' ? '0' : etherToWei ( rawValue ) . toString ( ) ;
665+ config . excessTolerancePercentage = 0 ;
666+ } catch ( error ) {
667+ showErrorToast ( `"${ sectionId } ": Invalid input "${ rawValue } " for excess tolerance fixed. Please enter a valid number.` ) ;
668+ throw error ;
669+ }
670+ } else {
671+ // Percentage mode - validate as 0-100 percentage
672+ const validation = validateExcessTolerancePercentage ( rawValue ) ;
673+ if ( ! validation . isValid ) {
674+ showErrorToast ( `"${ sectionId } ": ${ validation . error } ` ) ;
675+ throw new Error ( validation . error ) ;
676+ }
677+ const percentageValue = parseFloat ( rawValue ) || 0 ;
678+ config . excessToleranceFixed = '0' ;
679+ config . excessTolerancePercentage = percentageValue ;
680+ }
681+ }
682+
547683 return config ;
548684}
549685
0 commit comments