@@ -115,13 +115,20 @@ export class SwapAmountConfig extends AmountConfig {
115115 } ) ;
116116 }
117117
118+ // fee 차감 전 순수 잔고. fee에 의존하지 않으므로 fee가 변해도 값이 불변.
119+ // max 모드에서 route query의 amount로 사용하여
120+ // fee→amount→route→simulation→fee 순환을 차단한다.
118121 @computed
119- get maxAmount ( ) : CoinPretty {
120- let result = this . queriesStore
122+ get rawBalance ( ) : CoinPretty {
123+ return this . queriesStore
121124 . get ( this . chainId )
122- // CHECK: queryBalances대신 querySpendableBalance를 사용해야 하는지 확인
123125 . queryBalances . getQueryBech32Address ( this . senderConfig . sender )
124126 . getBalanceFromCurrency ( this . currency ) ;
127+ }
128+
129+ @computed
130+ get maxAmount ( ) : CoinPretty {
131+ let result = this . rawBalance ;
125132 if ( this . feeConfig && ! this . disableSubFeeFromFaction ) {
126133 for ( const fee of this . feeConfig . fees ) {
127134 result = result . sub ( fee ) ;
@@ -137,10 +144,14 @@ export class SwapAmountConfig extends AmountConfig {
137144 @override
138145 override get value ( ) : string {
139146 if ( this . fraction > 0 ) {
140- let result = this . maxAmount ;
141-
142- const queryRoute = this . getQueryRoute ( result ) ;
147+ // route query에 rawBalance(fee 차감 전)를 사용한다.
148+ // maxAmount(balance - fee)를 사용하면 fee가 바뀔 때마다 route query 키가 변경되어
149+ // route fetch → simulation → fee 변경 → route fetch ... 무한루프에 빠진다.
150+ // fee 차이에 의한 amount 변동은 전체 잔고 대비 극히 미미하므로 route 결과에 영향 없다.
151+ const queryRoute = this . getQueryRoute ( this . rawBalance ) ;
143152 if ( queryRoute ?. response != null ) {
153+ // 실제 value 계산에는 maxAmount(balance - fee)를 사용하여 정확도 유지
154+ let result = this . maxAmount ;
144155 const bridgeFee = queryRoute . bridgeFees . reduce (
145156 ( acc : CoinPretty , fee : CoinPretty ) => {
146157 if (
@@ -155,23 +166,23 @@ export class SwapAmountConfig extends AmountConfig {
155166 if ( bridgeFee . toDec ( ) . gt ( SwapAmountConfig . ZERO_DEC ) ) {
156167 result = result . sub ( bridgeFee ) ;
157168 }
158- } else {
159- return this . _oldValue ;
160- }
161169
162- if ( result . toDec ( ) . lte ( SwapAmountConfig . ZERO_DEC ) ) {
163- return "0" ;
164- }
170+ if ( result . toDec ( ) . lte ( SwapAmountConfig . ZERO_DEC ) ) {
171+ return "0" ;
172+ }
165173
166- const newValue = result
167- . mul ( new Dec ( this . fraction ) )
168- . trim ( true )
169- . locale ( false )
170- . hideDenom ( true )
171- . toString ( ) ;
172- this . _oldValue = newValue ;
174+ const newValue = result
175+ . mul ( new Dec ( this . fraction ) )
176+ . trim ( true )
177+ . locale ( false )
178+ . hideDenom ( true )
179+ . toString ( ) ;
180+ this . _oldValue = newValue ;
173181
174- return newValue ;
182+ return newValue ;
183+ } else {
184+ return this . _oldValue ;
185+ }
175186 }
176187
177188 this . _oldValue = this . _value ;
@@ -292,9 +303,10 @@ export class SwapAmountConfig extends AmountConfig {
292303 }
293304
294305 get isFetchingInAmount ( ) : boolean {
306+ // value getter와 동일하게 rawBalance를 사용하여 같은 route query를 참조.
295307 if ( this . fraction === 1 ) {
296308 return (
297- this . getQueryRoute ( this . maxAmount ) ?. isFetching ??
309+ this . getQueryRoute ( this . rawBalance ) ?. isFetching ??
298310 this . getQueryRoute ( ) ?. isFetching ??
299311 false
300312 ) ;
@@ -818,8 +830,10 @@ export class SwapAmountConfig extends AmountConfig {
818830 }
819831
820832 // max amount인 경우엔 route를 두 번 쿼리하기 때문에 첫 번째 쿼리도 체크한다.
833+ // value getter와 동일하게 rawBalance를 사용하여 같은 route query를 참조한다.
834+ // maxAmount를 사용하면 fee 변동 시 다른 쿼리 키가 생성되어 무한 fetch 사이클 유발.
821835 if ( this . fraction === 1 ) {
822- const querySwapHelper = this . getQuerySwapHelper ( this . maxAmount ) ;
836+ const querySwapHelper = this . getQuerySwapHelper ( this . rawBalance ) ;
823837 if ( ! querySwapHelper ) {
824838 return {
825839 ...prev ,
0 commit comments