Skip to content

Commit 99dff0f

Browse files
authored
chore: improve progressive fee comments and test coverage (#7153)
1 parent a58c170 commit 99dff0f

File tree

3 files changed

+131
-21
lines changed

3 files changed

+131
-21
lines changed

solidity/contracts/token/fees/ProgressiveFee.sol

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,26 @@ import {BaseFee, FeeType} from "./BaseFee.sol";
55

66
/**
77
* @title Progressive Fee Structure
8-
* @dev Implements a progressive fee model where the fee percentage increases as the transfer amount increases.
8+
* @dev Implements a progressive fee model where the fee percentage increases with transfer amount
9+
* until reaching a peak at halfAmount, then decreases as the absolute fee approaches maxFee.
910
*
1011
* The fee calculation uses a rational function: fee = (maxFee * amount^2) / (halfAmount^2 + amount^2)
1112
*
1213
* Key characteristics:
13-
* - Higher fee percentage for larger transfers
14-
* - Lower fee percentage for smaller transfers
15-
* - Fee approaches but never reaches maxFee as amount increases
14+
* - Fee percentage increases for transfers below halfAmount (progressive phase)
15+
* - Fee percentage peaks at halfAmount where fee = maxFee/2
16+
* - Fee percentage decreases for transfers above halfAmount (regressive phase due to maxFee cap)
17+
* - Absolute fee approaches but never reaches maxFee as amount increases
1618
* - Fee approaches 0 as amount approaches 0
17-
1819
*
1920
* Example:
20-
* - If maxFee = 1000 and halfAmount = 1000:
21-
* - Transfer of 100 wei: fee = (1000 * 100^2) / (1000^2 + 100^2) = 9.9 wei (9.9%)
22-
* - Transfer of 1000 wei: fee = (1000 * 1000^2) / (1000^2 + 1000^2) = 500 wei (50%)
23-
* - Transfer of 10000 wei: fee = (1000 * 10000^2) / (1000^2 + 10000^2) = 990 wei (99%)
21+
* - If maxFee = 1000 and halfAmount = 10000:
22+
* - Transfer of 2000 wei: fee = (1000 * 2000^2) / (10000^2 + 2000^2) = 38.5 wei (1.92% of amount)
23+
* - Transfer of 10000 wei: fee = (1000 * 10000^2) / (10000^2 + 10000^2) = 500 wei (5% of amount)
24+
* - Transfer of 50000 wei: fee = (1000 * 50000^2) / (10000^2 + 50000^2) = 961.5 wei (1.92% of amount)
2425
*
25-
* This structure encourages smaller transactions while applying higher fees to larger transfers.
26+
* This structure encourages mid-sized transfers while applying lower effective rates to both
27+
* very small and very large transactions.
2628
*/
2729
contract ProgressiveFee is BaseFee {
2830
constructor(
@@ -35,9 +37,11 @@ contract ProgressiveFee is BaseFee {
3537
function _quoteTransfer(
3638
uint256 amount
3739
) internal view override returns (uint256 fee) {
40+
if (amount == 0) {
41+
return 0;
42+
}
3843
uint256 amountSquared = amount ** 2;
39-
uint256 denominator = halfAmount ** 2 + amountSquared;
40-
return denominator == 0 ? 0 : (maxFee * amountSquared) / denominator;
44+
return (maxFee * amountSquared) / (halfAmount ** 2 + amountSquared);
4145
}
4246

4347
function feeType() external pure override returns (FeeType) {

solidity/contracts/token/fees/RegressiveFee.sol

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,24 @@ import {BaseFee, FeeType} from "./BaseFee.sol";
55

66
/**
77
* @title Regressive Fee Structure
8-
* @dev Implements a regressive fee model where the fee percentage decreases as the transfer amount increases.
8+
* @dev Implements a regressive fee model where the fee percentage continuously decreases as the transfer amount increases.
99
*
1010
* The fee calculation uses a rational function: fee = (maxFee * amount) / (halfAmount + amount)
1111
*
1212
* Key characteristics:
13-
* - Higher fee percentage for smaller transfers
14-
* - Lower fee percentage for larger transfers
15-
* - Fee approaches maxFee as amount approaches infinity
13+
* - Fee percentage continuously decreases as amount increases (regressive throughout)
14+
* - At halfAmount, fee = maxFee/2 and fee percentage = maxFee/(2*halfAmount)
15+
* - Absolute fee approaches but never reaches maxFee as amount approaches infinity
1616
* - Fee approaches 0 as amount approaches 0
1717
*
1818
* Example:
19-
* - If maxFee = 1000 and halfAmount = 1000:
20-
* - Transfer of 100 wei: fee = (1000 * 100) / (1000 + 100) = 90.9 wei (90.9%)
21-
* - Transfer of 1000 wei: fee = (1000 * 1000) / (1000 + 1000) = 500 wei (50%)
22-
* - Transfer of 10000 wei: fee = (1000 * 10000) / (1000 + 10000) = 909 wei (9.09%)
19+
* - If maxFee = 1000 and halfAmount = 5000:
20+
* - Transfer of 1000 wei: fee = (1000 * 1000) / (5000 + 1000) = 166.7 wei (16.67% of amount)
21+
* - Transfer of 5000 wei: fee = (1000 * 5000) / (5000 + 5000) = 500 wei (10% of amount)
22+
* - Transfer of 20000 wei: fee = (1000 * 20000) / (5000 + 20000) = 800 wei (4% of amount)
2323
*
24-
* This structure encourages larger transfers while applying higher fees to smaller transactions.
24+
* This structure encourages larger transfers while discouraging smaller transactions with higher
25+
* effective fee rates.
2526
*/
2627
contract RegressiveFee is BaseFee {
2728
constructor(

solidity/test/token/Fees.t.sol

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,81 @@ contract ProgressiveFeeTest is BaseFeeTest {
177177
"Progressive fee mismatch"
178178
);
179179
}
180+
181+
function test_ProgressiveFee_IncreasingPercentageBeforePeak() public {
182+
// Test that fee percentage increases as amount increases toward halfAmount
183+
ProgressiveFee localProgressiveFee = new ProgressiveFee(
184+
address(token),
185+
1000,
186+
10000,
187+
OWNER
188+
);
189+
190+
uint256 amount1 = 2000;
191+
uint256 amount2 = 5000;
192+
uint256 amount3 = 10000;
193+
194+
uint256 fee1 = localProgressiveFee
195+
.quoteTransferRemote(destination, recipient, amount1)[0].amount;
196+
uint256 fee2 = localProgressiveFee
197+
.quoteTransferRemote(destination, recipient, amount2)[0].amount;
198+
uint256 fee3 = localProgressiveFee
199+
.quoteTransferRemote(destination, recipient, amount3)[0].amount;
200+
201+
// Calculate percentages (scaled by 1e18 for precision)
202+
uint256 percentage1 = (fee1 * 1e18) / amount1;
203+
uint256 percentage2 = (fee2 * 1e18) / amount2;
204+
uint256 percentage3 = (fee3 * 1e18) / amount3;
205+
206+
// Verify percentages increase before peak
207+
assertLt(percentage1, percentage2, "Percentage should increase");
208+
assertLt(percentage2, percentage3, "Percentage should increase");
209+
}
210+
211+
function test_ProgressiveFee_DecreasingPercentageAfterPeak() public {
212+
// Test that fee percentage decreases as amount increases beyond halfAmount
213+
ProgressiveFee localProgressiveFee = new ProgressiveFee(
214+
address(token),
215+
1000,
216+
10000,
217+
OWNER
218+
);
219+
220+
uint256 amount1 = 10000;
221+
uint256 amount2 = 20000;
222+
uint256 amount3 = 50000;
223+
224+
uint256 fee1 = localProgressiveFee
225+
.quoteTransferRemote(destination, recipient, amount1)[0].amount;
226+
uint256 fee2 = localProgressiveFee
227+
.quoteTransferRemote(destination, recipient, amount2)[0].amount;
228+
uint256 fee3 = localProgressiveFee
229+
.quoteTransferRemote(destination, recipient, amount3)[0].amount;
230+
231+
// Calculate percentages (scaled by 1e18 for precision)
232+
uint256 percentage1 = (fee1 * 1e18) / amount1;
233+
uint256 percentage2 = (fee2 * 1e18) / amount2;
234+
uint256 percentage3 = (fee3 * 1e18) / amount3;
235+
236+
// Verify percentages decrease after peak
237+
assertGt(percentage1, percentage2, "Percentage should decrease");
238+
assertGt(percentage2, percentage3, "Percentage should decrease");
239+
}
240+
241+
function test_ProgressiveFee_ZeroAmount() public {
242+
// Test that fee is zero when amount is zero
243+
ProgressiveFee localProgressiveFee = new ProgressiveFee(
244+
address(token),
245+
1000,
246+
10000,
247+
OWNER
248+
);
249+
250+
uint256 fee = localProgressiveFee
251+
.quoteTransferRemote(destination, recipient, 0)[0].amount;
252+
253+
assertEq(fee, 0, "Fee should be zero for zero amount");
254+
}
180255
}
181256

182257
// --- RegressiveFee Tests ---
@@ -225,6 +300,36 @@ contract RegressiveFeeTest is BaseFeeTest {
225300
"Regressive fee mismatch"
226301
);
227302
}
303+
304+
function test_RegressiveFee_ContinuouslyDecreasingPercentage() public {
305+
// Test that fee percentage continuously decreases as amount increases
306+
RegressiveFee localRegressiveFee = new RegressiveFee(
307+
address(token),
308+
1000,
309+
5000,
310+
OWNER
311+
);
312+
313+
uint256 amount1 = 1000;
314+
uint256 amount2 = 5000;
315+
uint256 amount3 = 20000;
316+
317+
uint256 fee1 = localRegressiveFee
318+
.quoteTransferRemote(destination, recipient, amount1)[0].amount;
319+
uint256 fee2 = localRegressiveFee
320+
.quoteTransferRemote(destination, recipient, amount2)[0].amount;
321+
uint256 fee3 = localRegressiveFee
322+
.quoteTransferRemote(destination, recipient, amount3)[0].amount;
323+
324+
// Calculate percentages (scaled by 1e18 for precision)
325+
uint256 percentage1 = (fee1 * 1e18) / amount1;
326+
uint256 percentage2 = (fee2 * 1e18) / amount2;
327+
uint256 percentage3 = (fee3 * 1e18) / amount3;
328+
329+
// Verify percentages continuously decrease
330+
assertGt(percentage1, percentage2, "Percentage should decrease");
331+
assertGt(percentage2, percentage3, "Percentage should decrease");
332+
}
228333
}
229334

230335
// --- RoutingFee Tests ---

0 commit comments

Comments
 (0)