@@ -148,43 +148,68 @@ export default function InteractionClient() {
148
148
}
149
149
} , [ ] ) ;
150
150
151
- // Function to calculate user amount after fees
152
- // Simple math: if fee is 0.5%, then userAmount = mintAmount * 0.995
151
+ // PRECISION IMPROVEMENT: Using BigInt arithmetic for exact 18-decimal precision
152
+ // Function to calculate user amount after fees using precise BigInt arithmetic
153
+ // Matches smart contract: feeAmount = (amount * 500) / 100000; userAmount = amount - feeAmount
153
154
const calculateUserAmountAfterFees = useCallback ( ( amount : string ) => {
154
155
if ( ! amount || isNaN ( Number ( amount ) ) || Number ( amount ) <= 0 ) {
155
156
setUserAmountAfterFees ( 0 ) ;
156
157
return ;
157
158
}
158
159
159
- const mintAmount = Number ( amount ) ;
160
-
161
- // Simple calculation: userAmount = mintAmount * (1 - 0.005)
162
- const userAmount = mintAmount * 0.995 ;
163
-
164
- // Round to 6 decimal places for better UX
165
- const roundedUserAmount = Math . round ( userAmount * 1000000 ) / 1000000 ;
166
-
167
- setUserAmountAfterFees ( roundedUserAmount ) ;
160
+ try {
161
+ // Convert to BigInt with 18 decimals for precise calculation
162
+ const amountBigInt = parseUnits ( amount , decimals ) ;
163
+
164
+ // Smart contract constants: clowderFee = 500, denominator = 100000
165
+ const clowderFee = BigInt ( 500 ) ;
166
+ const denominator = BigInt ( 100000 ) ;
167
+
168
+ // Calculate fee: (amount * 500) / 100000
169
+ const feeAmountBigInt = ( amountBigInt * clowderFee ) / denominator ;
170
+
171
+ // Calculate user amount: amount - fee
172
+ const userAmountBigInt = amountBigInt - feeAmountBigInt ;
173
+
174
+ // Convert back to number for display (maintaining precision)
175
+ const userAmountNumber = Number ( formatUnits ( userAmountBigInt , decimals ) ) ;
176
+
177
+ setUserAmountAfterFees ( userAmountNumber ) ;
178
+ } catch ( error ) {
179
+ console . error ( 'Error calculating user amount after fees:' , error ) ;
180
+ setUserAmountAfterFees ( 0 ) ;
181
+ }
168
182
} , [ ] ) ;
169
183
170
- // Function to calculate mint amount needed to achieve desired receive amount
171
- // Simple math: if fee is 0.5%, then receiveAmount = mintAmount * 0.995
172
- // Therefore: mintAmount = receiveAmount / 0.995
184
+ // PRECISION IMPROVEMENT: Reverse calculation also uses precise BigInt arithmetic
185
+ // Function to calculate mint amount needed to achieve desired receive amount using precise BigInt arithmetic
186
+ // Reverse calculation: if userAmount = amount - (amount * 500) / 100000, then amount = userAmount * 100000 / (100000 - 500)
173
187
const calculateMintAmountFromReceive = useCallback ( ( desiredReceiveAmount : string ) => {
174
188
if ( ! desiredReceiveAmount || isNaN ( Number ( desiredReceiveAmount ) ) || Number ( desiredReceiveAmount ) <= 0 ) {
175
189
setCalculatedMintAmount ( 0 ) ;
176
190
return ;
177
191
}
178
192
179
- const receiveAmount = Number ( desiredReceiveAmount ) ;
180
-
181
- // Simple calculation: mintAmount = receiveAmount / (1 - 0.005)
182
- const mintAmount = receiveAmount / 0.995 ;
183
-
184
- // Round to 6 decimal places for better UX
185
- const roundedMintAmount = Math . round ( mintAmount * 1000000 ) / 1000000 ;
186
-
187
- setCalculatedMintAmount ( roundedMintAmount ) ;
193
+ try {
194
+ // Convert to BigInt with 18 decimals for precise calculation
195
+ const receiveAmountBigInt = parseUnits ( desiredReceiveAmount , decimals ) ;
196
+
197
+ // Smart contract constants: clowderFee = 500, denominator = 100000
198
+ const clowderFee = BigInt ( 500 ) ;
199
+ const denominator = BigInt ( 100000 ) ;
200
+
201
+ // Calculate mint amount: receiveAmount * denominator / (denominator - clowderFee)
202
+ // This gives us: receiveAmount * 100000 / (100000 - 500) = receiveAmount * 100000 / 99500
203
+ const mintAmountBigInt = ( receiveAmountBigInt * denominator ) / ( denominator - clowderFee ) ;
204
+
205
+ // Convert back to number for display (maintaining precision)
206
+ const mintAmountNumber = Number ( formatUnits ( mintAmountBigInt , decimals ) ) ;
207
+
208
+ setCalculatedMintAmount ( mintAmountNumber ) ;
209
+ } catch ( error ) {
210
+ console . error ( 'Error calculating mint amount from receive:' , error ) ;
211
+ setCalculatedMintAmount ( 0 ) ;
212
+ }
188
213
} , [ ] ) ;
189
214
190
215
// Add new state for transaction signing
@@ -1289,14 +1314,30 @@ export default function InteractionClient() {
1289
1314
type = "number"
1290
1315
placeholder = "Enter amount to mint"
1291
1316
value = { mintAmount }
1292
- onChange = { ( e ) => setMintAmount ( Math . min ( Number ( e . target . value ) , tokenDetails . maxMintableAmount ) . toString ( ) ) }
1317
+ onChange = { ( e ) => {
1318
+ const inputValue = e . target . value ;
1319
+ if ( inputValue === '' || inputValue === '.' ) {
1320
+ setMintAmount ( inputValue ) ;
1321
+ } else {
1322
+ const numValue = Number ( inputValue ) ;
1323
+ if ( ! isNaN ( numValue ) && numValue >= 0 ) {
1324
+ // Only limit to max mintable amount if it exceeds the limit
1325
+ if ( numValue <= tokenDetails . maxMintableAmount ) {
1326
+ setMintAmount ( inputValue ) ;
1327
+ } else {
1328
+ setMintAmount ( tokenDetails . maxMintableAmount . toString ( ) ) ;
1329
+ }
1330
+ }
1331
+ }
1332
+ } }
1293
1333
className = "h-10 text-sm bg-white/60 dark:bg-[#2a1a00] border-2 border-gray-200 dark:border-yellow-400/20 text-gray-600 dark:text-yellow-200"
1294
1334
/>
1295
1335
< Button
1296
1336
type = "button"
1297
1337
onClick = { ( ) => {
1298
1338
const safeMaxAmount = Math . max ( 0 , tokenDetails . maxMintableAmount ) ;
1299
- setMintAmount ( safeMaxAmount . toFixed ( 6 ) ) ;
1339
+ // Maintain full precision for input, but limit displayed decimals for UX
1340
+ setMintAmount ( safeMaxAmount . toString ( ) ) ;
1300
1341
} }
1301
1342
disabled = { tokenDetails . maxMintableAmount === 0 }
1302
1343
className = "h-10 px-3 text-sm bg-gray-500 dark:bg-gray-600 hover:bg-gray-600 dark:hover:bg-gray-700 text-white rounded-xl whitespace-nowrap"
@@ -1349,9 +1390,24 @@ export default function InteractionClient() {
1349
1390
< Button
1350
1391
type = "button"
1351
1392
onClick = { ( ) => {
1352
- // Set a reasonable max receive amount (slightly less than max mintable due to fees)
1353
- const safeMaxReceiveAmount = Math . max ( 0 , tokenDetails . maxMintableAmount * 0.99 ) ;
1354
- setReceiveAmount ( safeMaxReceiveAmount . toFixed ( 6 ) ) ;
1393
+ // Calculate precise max receive amount using exact smart contract fee calculation
1394
+ const maxMintableAmount = tokenDetails . maxMintableAmount ;
1395
+ if ( maxMintableAmount > 0 ) {
1396
+ try {
1397
+ const amountBigInt = parseUnits ( maxMintableAmount . toString ( ) , decimals ) ;
1398
+ const clowderFee = BigInt ( 500 ) ;
1399
+ const denominator = BigInt ( 100000 ) ;
1400
+ const feeAmountBigInt = ( amountBigInt * clowderFee ) / denominator ;
1401
+ const maxReceiveAmountBigInt = amountBigInt - feeAmountBigInt ;
1402
+ const maxReceiveAmount = formatUnits ( maxReceiveAmountBigInt , decimals ) ;
1403
+ setReceiveAmount ( maxReceiveAmount ) ;
1404
+ } catch ( error ) {
1405
+ console . error ( 'Error calculating max receive amount:' , error ) ;
1406
+ setReceiveAmount ( '0' ) ;
1407
+ }
1408
+ } else {
1409
+ setReceiveAmount ( '0' ) ;
1410
+ }
1355
1411
} }
1356
1412
disabled = { tokenDetails . maxMintableAmount === 0 }
1357
1413
className = "h-10 px-3 text-sm bg-gray-500 dark:bg-gray-600 hover:bg-gray-600 dark:hover:bg-gray-700 text-white rounded-xl whitespace-nowrap"
0 commit comments