@@ -456,9 +456,9 @@ adapterNames.forEach((adapterName) => {
456456 describe ( 'calculateFlashLoanAmounts' , ( ) => {
457457 test ( 'should calculate correct flash loan fee amount' , ( ) => {
458458 const sellAmount = BigInt ( '1000000000000000000' ) // 1 ETH
459- const flashLoanFeePercent = 0.05 // 0.05%
459+ const flashLoanFeeBps = 5 // 0.05%
460460
461- const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeePercent } )
461+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
462462
463463 // 0.05% of 1 ETH = 0.0005 ETH = 500000000000000 wei
464464 expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '500000000000000' ) )
@@ -467,30 +467,30 @@ adapterNames.forEach((adapterName) => {
467467
468468 test ( 'should handle zero flash loan fee' , ( ) => {
469469 const sellAmount = BigInt ( '1000000000000000000' ) // 1 ETH
470- const flashLoanFeePercent = 0
470+ const flashLoanFeeBps = 0
471471
472- const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeePercent } )
472+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
473473
474474 expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '0' ) )
475475 expect ( result . sellAmountToSign ) . toBe ( sellAmount )
476476 } )
477477
478478 test ( 'should calculate fee for small amounts' , ( ) => {
479479 const sellAmount = BigInt ( '100' ) // Very small amount
480- const flashLoanFeePercent = 0.09 // 0.09%
480+ const flashLoanFeeBps = 9 // 0.09%
481481
482- const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeePercent } )
482+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
483483
484- // Fee should be 0 due to rounding down
485- expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '0 ' ) )
486- expect ( result . sellAmountToSign ) . toBe ( sellAmount )
484+ // 100 * 9 / 10000 = 0.09, ceil rounds up to 1
485+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1 ' ) )
486+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '99' ) )
487487 } )
488488
489489 test ( 'should calculate fee for large percentage' , ( ) => {
490490 const sellAmount = BigInt ( '10000000000000000000' ) // 10 ETH
491- const flashLoanFeePercent = 1.0 // 1%
491+ const flashLoanFeeBps = 100 // 1%
492492
493- const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeePercent } )
493+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
494494
495495 // 1% of 10 ETH = 0.1 ETH = 100000000000000000 wei
496496 expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '100000000000000000' ) )
@@ -499,12 +499,119 @@ adapterNames.forEach((adapterName) => {
499499
500500 test ( 'should handle fractional percentages precisely' , ( ) => {
501501 const sellAmount = BigInt ( '1000000000000000000' ) // 1 ETH
502- const flashLoanFeePercent = 0.0123 // 0.0123%
502+ const flashLoanFeeBps = 123 // 1.23% (123 basis points)
503+
504+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
505+
506+ // 1000000000000000000 * 123 / 10000 = 12300000000000000 (exact)
507+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '12300000000000000' ) )
508+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '987700000000000000' ) )
509+ } )
510+
511+ test ( 'should round up for very small amounts with tiny remainders' , ( ) => {
512+ // Test edge case: amount so small that fee rounds to 1 wei
513+ const sellAmount = BigInt ( '1' ) // 1 wei
514+ const flashLoanFeeBps = 5 // 0.05%
515+
516+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
517+
518+ // 1 * 5 / 10000 = 0.0005, should round up to 1
519+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1' ) )
520+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '0' ) )
521+ } )
522+
523+ test ( 'should handle exact division with no remainder' , ( ) => {
524+ // Test case where division is exact (no rounding needed)
525+ const sellAmount = BigInt ( '10000' ) // 10000 wei
526+ const flashLoanFeeBps = 1 // 0.01%
527+
528+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
529+
530+ // 10000 * 1 / 10000 = 1 (exact)
531+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1' ) )
532+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '9999' ) )
533+ } )
534+
535+ test ( 'should round up for small remainder' , ( ) => {
536+ // Test with remainder = 1
537+ const sellAmount = BigInt ( '10001' )
538+ const flashLoanFeeBps = 1 // 0.01%
539+
540+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
541+
542+ // 10001 * 1 / 10000 = 1.0001, should round up to 2
543+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '2' ) )
544+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '9999' ) )
545+ } )
546+
547+ test ( 'should round up for medium amounts' , ( ) => {
548+ // Test with typical DeFi amounts
549+ const sellAmount = BigInt ( '1500000000000000000' ) // 1.5 ETH
550+ const flashLoanFeeBps = 9 // 0.09%
551+
552+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
553+
554+ // 1.5 ETH * 0.09% = 0.00135 ETH = 1350000000000000 wei
555+ // 1500000000000000000 * 9 / 10000 = 1350000000000000 (exact)
556+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1350000000000000' ) )
557+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '1498650000000000000' ) )
558+ } )
559+
560+ test ( 'should round up for large amounts with small remainder' , ( ) => {
561+ // Test with very large amounts (100 ETH)
562+ const sellAmount = BigInt ( '100000000000000000001' ) // 100 ETH + 1 wei
563+ const flashLoanFeeBps = 5 // 0.05%
564+
565+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
566+
567+ // 100000000000000000001 * 5 / 10000 = 50000000000000000.0005
568+ // Should round up to 50000000000000001
569+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '50000000000000001' ) )
570+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '99950000000000000000' ) )
571+ } )
572+
573+ test ( 'should handle maximum uint256-like amounts' , ( ) => {
574+ // Test with very large numbers (close to max supply of common tokens)
575+ const sellAmount = BigInt ( '1000000000000000000000000' ) // 1 million tokens with 18 decimals
576+ const flashLoanFeeBps = 10 // 0.1%
577+
578+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
579+
580+ // 1000000000000000000000000 * 10 / 10000 = 1000000000000000000000 (exact)
581+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1000000000000000000000' ) )
582+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '999000000000000000000000' ) )
583+ } )
584+
585+ test ( 'should round up with fractional percentage that creates remainder' , ( ) => {
586+ // Test with percentage that guarantees a remainder
587+ const sellAmount = BigInt ( '123456789' )
588+ const flashLoanFeeBps = 7 // 0.07%
589+
590+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
591+
592+ // 123456789 * 7 / 10000 = 86419.7523, should round up to 86420
593+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '86420' ) )
594+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '123370369' ) )
595+ } )
596+
597+ test ( 'should match Aave PercentageMath.percentMul() rounding behavior' , ( ) => {
598+ // This test verifies we use ceil rounding (round up on any remainder)
599+ // Our implementation: if (remainder > 0) round up by 1
600+
601+ const sellAmount = BigInt ( '2813982695824406449' )
602+ const flashLoanFeeBps = 5 // 0.05%
603+
604+ const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeeBps } )
503605
504- const result = flashLoanSdk . calculateFlashLoanAmounts ( { sellAmount, flashLoanFeePercent } )
606+ // Manual calculation:
607+ // bps = 0.05 * 100 = 5
608+ // product = 2813982695824406449 * 5 = 14069913479122032245
609+ // quotient = 14069913479122032245 / 10000 = 1406991347912203 (truncated)
610+ // remainder = 14069913479122032245 % 10000 = 2245
611+ // Since remainder > 0, round up: 1406991347912203 + 1 = 1406991347912204
505612
506- expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '123000000000000 ' ) )
507- expect ( result . sellAmountToSign ) . toBe ( BigInt ( '999877000000000000 ' ) )
613+ expect ( result . flashLoanFeeAmount ) . toBe ( BigInt ( '1406991347912204 ' ) )
614+ expect ( result . sellAmountToSign ) . toBe ( BigInt ( '2813982695824406449' ) - BigInt ( '1406991347912204 ') )
508615 } )
509616 } )
510617
0 commit comments