Skip to content

Commit bebcea7

Browse files
committed
feat: add general configs and tests
1 parent f0b7542 commit bebcea7

File tree

3 files changed

+93
-5
lines changed

3 files changed

+93
-5
lines changed

internal/adapters/entrypoints/rest/assets/static/management.js

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {
22
weiToEther,
33
etherToWei,
44
isFeeKey,
5+
isMaxLiquidityKey,
6+
validateMaxLiquidity,
57
validateConfig,
68
formatGeneralConfig,
79
postConfig,
@@ -49,7 +51,12 @@ const createInput = (section, key, value) => {
4951

5052
const label = document.createElement('label');
5153
label.classList.add('form-label');
52-
label.textContent = key;
54+
label.textContent = getDisplayLabel(key);
55+
56+
// Make Maximum Liquidity label bold
57+
if (isMaxLiquidityKey(key)) {
58+
label.style.fontWeight = 'bold';
59+
}
5360

5461
const inputContainer = document.createElement('div');
5562
inputContainer.classList.add('input-container');
@@ -139,8 +146,11 @@ const createFeeInput = (inputContainer, label, section, key, value) => {
139146
input.value = isFeeKey(key) ? weiToEther(value) : value;
140147
input.addEventListener('input', () => setChanged(section.id));
141148
inputContainer.appendChild(input);
142-
const questionIcon = createQuestionIcon(getTooltipText(key));
143-
label.appendChild(questionIcon);
149+
// Skip tooltip for maxLiquidity
150+
if (!isMaxLiquidityKey(key)) {
151+
const questionIcon = createQuestionIcon(getTooltipText(key));
152+
label.appendChild(questionIcon);
153+
}
144154
};
145155

146156
const createFeePercentageInput = (inputContainer, section, key, value) => {
@@ -184,6 +194,13 @@ const createQuestionIcon = (tooltipText) => {
184194
return questionIcon;
185195
};
186196

197+
const getDisplayLabel = (key) => {
198+
const labels = {
199+
maxLiquidity: 'Maximum Liquidity'
200+
};
201+
return labels[key] || key;
202+
};
203+
187204
const getTooltipText = (key) => {
188205
const tooltips = {
189206
timeForDeposit: 'The time (in seconds) for which a deposit is considered valid.',
@@ -195,7 +212,8 @@ const getTooltipText = (key) => {
195212
expireBlocks: 'The number of blocks after which a quote is considered expired.',
196213
bridgeTransactionMin: 'The amount of rBTC that needs to be gathered in peg out refunds before executing a native peg out.',
197214
fixedFee: 'A fixed fee charged for transactions.',
198-
feePercentage: 'A percentage fee charged based on the transaction amount.'
215+
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.'
199217
};
200218
return tooltips[key] || 'No description available';
201219
};
@@ -472,7 +490,20 @@ function getRegularConfig(sectionId) {
472490
value = '0';
473491
}
474492
} else {
475-
if (isFeeKey(key)) {
493+
if (isMaxLiquidityKey(key)) {
494+
// Validate maxLiquidity: must be positive and max 18 decimal places
495+
const validation = validateMaxLiquidity(input.value);
496+
if (!validation.isValid) {
497+
showErrorToast(`"${sectionId}": ${validation.error}`);
498+
throw new Error(validation.error);
499+
}
500+
try {
501+
value = etherToWei(input.value).toString();
502+
} catch (error) {
503+
showErrorToast(`"${sectionId}": Invalid input "${input.value}" for field "${key}". Please enter a valid number.`);
504+
throw error;
505+
}
506+
} else if (isFeeKey(key)) {
476507
try {
477508
value = etherToWei(input.value).toString();
478509
} catch (error) {

internal/adapters/entrypoints/rest/handlers/set_general_config_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,33 @@ func TestSetGeneralConfigHandler(t *testing.T) {
9797
assert.Equal(t, http.StatusBadRequest, w.Code)
9898
useCase.AssertNotCalled(t, "Run")
9999
})
100+
t.Run("should return bad request if maxLiquidity has decimal places", func(t *testing.T) {
101+
useCase := new(mocks.SetGeneralConfigUseCaseMock)
102+
103+
handler := handlers.NewSetGeneralConfigHandler(useCase)
104+
// Backend expects wei values (integers), so decimal values should fail
105+
reqBody := `{"configuration": {"btcConfirmations": {"5": 10}, "rskConfirmations": {"10": 20}, "publicLiquidityCheck": true, "maxLiquidity": "1000.5"}}`
106+
req := httptest.NewRequest(http.MethodPost, "/configuration", strings.NewReader(reqBody))
107+
req.Header.Set("Content-Type", "application/json")
108+
w := httptest.NewRecorder()
109+
handler(w, req)
110+
assert.Equal(t, http.StatusBadRequest, w.Code)
111+
useCase.AssertNotCalled(t, "Run")
112+
})
113+
t.Run("should return success with maxLiquidity having 18 digits precision", func(t *testing.T) {
114+
useCase := new(mocks.SetGeneralConfigUseCaseMock)
115+
useCase.EXPECT().Run(mock.Anything, mock.Anything).Return(nil)
116+
117+
handler := handlers.NewSetGeneralConfigHandler(useCase)
118+
// 1 RBTC in wei (18 decimal places) - valid large integer
119+
reqBody := `{"configuration": {"btcConfirmations": {"5": 10}, "rskConfirmations": {"10": 20}, "publicLiquidityCheck": true, "maxLiquidity": "1000000000000000000"}}`
120+
req := httptest.NewRequest(http.MethodPost, "/configuration", strings.NewReader(reqBody))
121+
req.Header.Set("Content-Type", "application/json")
122+
w := httptest.NewRecorder()
123+
handler(w, req)
124+
assert.Equal(t, http.StatusNoContent, w.Code)
125+
useCase.AssertExpectations(t)
126+
})
100127
t.Run("should return server internal error if the use case fails", func(t *testing.T) {
101128
useCase := new(mocks.SetGeneralConfigUseCaseMock)
102129
useCase.EXPECT().Run(mock.Anything, mock.Anything).Return(assert.AnError)

pkg/liquidity_provider_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,36 @@ func TestFromGeneralConfigurationDTO(t *testing.T) {
682682
assert.Empty(t, config)
683683
require.ErrorContains(t, err, "cannot deserialize RSK confirmations key invalid")
684684
})
685+
686+
t.Run("handles maxLiquidity with 18-digit precision correctly", func(t *testing.T) {
687+
// 1 RBTC = 1000000000000000000 wei (18 zeros)
688+
dto := pkg.GeneralConfigurationDTO{
689+
RskConfirmations: map[string]uint16{"1000000000000000000": 5},
690+
BtcConfirmations: map[string]uint16{"1000000000000000000": 10},
691+
PublicLiquidityCheck: true,
692+
MaxLiquidity: "1000000000000000000",
693+
}
694+
695+
config, err := pkg.FromGeneralConfigurationDTO(dto)
696+
697+
require.NoError(t, err)
698+
assert.Equal(t, "1000000000000000000", config.MaxLiquidity.String())
699+
})
700+
701+
t.Run("handles very large maxLiquidity values", func(t *testing.T) {
702+
// 100 RBTC = 100 * 10^18 wei
703+
dto := pkg.GeneralConfigurationDTO{
704+
RskConfirmations: map[string]uint16{"1000000000000000000": 5},
705+
BtcConfirmations: map[string]uint16{"1000000000000000000": 10},
706+
PublicLiquidityCheck: false,
707+
MaxLiquidity: "100000000000000000000",
708+
}
709+
710+
config, err := pkg.FromGeneralConfigurationDTO(dto)
711+
712+
require.NoError(t, err)
713+
assert.Equal(t, "100000000000000000000", config.MaxLiquidity.String())
714+
})
685715
}
686716

687717
func TestToBtcAssetLocationDTO(t *testing.T) {

0 commit comments

Comments
 (0)