@@ -16,6 +16,7 @@ import {PaymentInfo} from "src/builder/PaymentInfo.sol";
16
16
import {TokenWrapper} from "src/builder/TokenWrapper.sol " ;
17
17
import {QuarkOperationHelper} from "src/builder/QuarkOperationHelper.sol " ;
18
18
import {List} from "src/builder/List.sol " ;
19
+ import {HashMap} from "src/builder/HashMap.sol " ;
19
20
20
21
contract QuarkBuilderBase {
21
22
/* ===== Output Types ===== */
@@ -36,7 +37,7 @@ contract QuarkBuilderBase {
36
37
37
38
/* ===== Constants ===== */
38
39
39
- string constant VERSION = "0.1.2 " ;
40
+ string constant VERSION = "0.2.0 " ;
40
41
41
42
/* ===== Custom Errors ===== */
42
43
@@ -103,40 +104,33 @@ contract QuarkBuilderBase {
103
104
// Flag to check if the assetSymbolOut (used/supplied/transferred out) is the same as the payment token
104
105
bool paymentTokenIsPartOfAssetSymbolOuts = false ;
105
106
107
+ // Track the amount of each asset that will be bridged to the destination chain
108
+ HashMap.Map memory assetsBridged = HashMap.newMap ();
109
+
106
110
for (uint256 i = 0 ; i < actionIntent.assetSymbolOuts.length ; ++ i) {
111
+ string memory assetSymbolOut = actionIntent.assetSymbolOuts[i];
107
112
assertFundsAvailable (
108
- actionIntent.chainId,
109
- actionIntent.assetSymbolOuts[i],
110
- actionIntent.amountOuts[i],
111
- chainAccountsList,
112
- payment
113
+ actionIntent.chainId, assetSymbolOut, actionIntent.amountOuts[i], chainAccountsList, payment
113
114
);
114
115
// Check if the assetSymbolOut is the same as the payment token
115
- if (Strings.stringEqIgnoreCase (actionIntent.assetSymbolOuts[i] , payment.currency)) {
116
+ if (Strings.stringEqIgnoreCase (assetSymbolOut , payment.currency)) {
116
117
paymentTokenIsPartOfAssetSymbolOuts = true ;
117
118
}
118
119
119
120
if (
120
121
needsBridgedFunds (
121
- actionIntent.assetSymbolOuts[i],
122
- actionIntent.amountOuts[i],
123
- actionIntent.chainId,
124
- chainAccountsList,
125
- payment
122
+ assetSymbolOut, actionIntent.amountOuts[i], actionIntent.chainId, chainAccountsList, payment
126
123
)
127
124
) {
128
125
if (actionIntent.bridgeEnabled) {
129
126
uint256 amountNeededOnDst = actionIntent.amountOuts[i];
130
- if (
131
- payment.isToken && Strings.stringEqIgnoreCase (payment.currency, actionIntent.assetSymbolOuts[i])
132
- ) {
127
+ if (payment.isToken && Strings.stringEqIgnoreCase (payment.currency, assetSymbolOut)) {
133
128
amountNeededOnDst += PaymentInfo.findMaxCost (payment, actionIntent.chainId);
134
129
}
135
-
136
130
(IQuarkWallet.QuarkOperation[] memory bridgeQuarkOperations , Actions.Action[] memory bridgeActions )
137
131
= Actions.constructBridgeOperations (
138
132
Actions.BridgeOperationInfo ({
139
- assetSymbol: actionIntent.assetSymbolOuts[i] ,
133
+ assetSymbol: assetSymbolOut ,
140
134
amountNeededOnDst: amountNeededOnDst,
141
135
dstChainId: actionIntent.chainId,
142
136
recipient: actionIntent.actor,
@@ -147,22 +141,31 @@ contract QuarkBuilderBase {
147
141
payment
148
142
);
149
143
144
+ // Track how much is actually bridged for each asset
145
+ if (HashMap.contains (assetsBridged, abi.encode (assetSymbolOut))) {
146
+ uint256 existingAmountBridged = HashMap.getUint256 (assetsBridged, abi.encode (assetSymbolOut));
147
+ HashMap.putUint256 (
148
+ assetsBridged, abi.encode (assetSymbolOut), existingAmountBridged + amountNeededOnDst
149
+ );
150
+ } else {
151
+ HashMap.putUint256 (assetsBridged, abi.encode (assetSymbolOut), amountNeededOnDst);
152
+ }
153
+
150
154
for (uint256 j = 0 ; j < bridgeQuarkOperations.length ; ++ j) {
151
155
List.addQuarkOperation (quarkOperations, bridgeQuarkOperations[j]);
152
156
List.addAction (actions, bridgeActions[j]);
153
157
}
154
158
} else {
155
- uint256 balanceOnChain = getBalanceOnChain (
156
- actionIntent.assetSymbolOuts[i], actionIntent.chainId, chainAccountsList, payment
157
- );
159
+ uint256 balanceOnChain =
160
+ getBalanceOnChain (assetSymbolOut, actionIntent.chainId, chainAccountsList, payment);
158
161
uint256 amountNeededOnChain = getAmountNeededOnChain (
159
- actionIntent.assetSymbolOuts[i] , actionIntent.amountOuts[i], actionIntent.chainId, payment
162
+ assetSymbolOut , actionIntent.amountOuts[i], actionIntent.chainId, payment
160
163
);
161
164
uint256 maxCostOnChain =
162
165
payment.isToken ? PaymentInfo.findMaxCost (payment, actionIntent.chainId) : 0 ;
163
166
uint256 availableAssetBalance =
164
167
balanceOnChain >= maxCostOnChain ? balanceOnChain - maxCostOnChain : 0 ;
165
- revert FundsUnavailable (actionIntent.assetSymbolOuts[i] , amountNeededOnChain, availableAssetBalance);
168
+ revert FundsUnavailable (assetSymbolOut , amountNeededOnChain, availableAssetBalance);
166
169
}
167
170
}
168
171
}
@@ -196,6 +199,16 @@ contract QuarkBuilderBase {
196
199
payment
197
200
);
198
201
202
+ // Track how much is actually bridged for the payment asset
203
+ if (HashMap.contains (assetsBridged, abi.encode (payment.currency))) {
204
+ uint256 existingAmountBridged = HashMap.getUint256 (assetsBridged, abi.encode (payment.currency));
205
+ HashMap.putUint256 (
206
+ assetsBridged, abi.encode (payment.currency), existingAmountBridged + maxCostOnDstChain
207
+ );
208
+ } else {
209
+ HashMap.putUint256 (assetsBridged, abi.encode (payment.currency), maxCostOnDstChain);
210
+ }
211
+
199
212
for (uint256 i = 0 ; i < bridgeQuarkOperations.length ; ++ i) {
200
213
List.addQuarkOperation (quarkOperations, bridgeQuarkOperations[i]);
201
214
List.addAction (actions, bridgeActions[i]);
@@ -212,13 +225,18 @@ contract QuarkBuilderBase {
212
225
213
226
if (actionIntent.autoWrapperEnabled) {
214
227
for (uint256 i = 0 ; i < actionIntent.assetSymbolOuts.length ; ++ i) {
228
+ string memory assetSymbolOut = actionIntent.assetSymbolOuts[i];
229
+ uint256 supplementalBalance = HashMap.contains (assetsBridged, abi.encode (assetSymbolOut))
230
+ ? HashMap.getUint256 (assetsBridged, abi.encode (assetSymbolOut))
231
+ : 0 ;
215
232
checkAndInsertWrapOrUnwrapAction ({
216
233
actions: actions,
217
234
quarkOperations: quarkOperations,
218
235
chainAccountsList: chainAccountsList,
219
236
payment: payment,
220
- assetSymbol: actionIntent.assetSymbolOuts[i] ,
237
+ assetSymbol: assetSymbolOut ,
221
238
amount: actionIntent.amountOuts[i],
239
+ supplementalBalance: supplementalBalance,
222
240
chainId: actionIntent.chainId,
223
241
account: actionIntent.actor,
224
242
blockTimestamp: actionIntent.blockTimestamp,
@@ -373,6 +391,10 @@ contract QuarkBuilderBase {
373
391
/**
374
392
* @dev If there is not enough of the asset to cover the amount and the asset has a counterpart asset,
375
393
* insert a wrap/unwrap action to cover the gap in amount.
394
+ *
395
+ * `supplementalBalance` param describes an amount of the token that might have been received in the course
396
+ * of an action (for example, bridging to the destination chain), which would therefore not be present in
397
+ * `chainAccountsList` but could be used to cover action costs.
376
398
*/
377
399
function checkAndInsertWrapOrUnwrapAction (
378
400
List.DynamicArray memory actions ,
@@ -381,13 +403,15 @@ contract QuarkBuilderBase {
381
403
PaymentInfo.Payment memory payment ,
382
404
string memory assetSymbol ,
383
405
uint256 amount ,
406
+ uint256 supplementalBalance ,
384
407
uint256 chainId ,
385
408
address account ,
386
409
uint256 blockTimestamp ,
387
410
bool useQuotecall
388
411
) internal pure {
389
412
// Check if inserting wrapOrUnwrap action is necessary
390
- uint256 assetBalanceOnChain = Accounts.getBalanceOnChain (assetSymbol, chainId, chainAccountsList);
413
+ uint256 assetBalanceOnChain =
414
+ Accounts.getBalanceOnChain (assetSymbol, chainId, chainAccountsList) + supplementalBalance;
391
415
if (assetBalanceOnChain < amount && TokenWrapper.hasWrapperContract (chainId, assetSymbol)) {
392
416
// If the asset has a wrapper counterpart, wrap/unwrap the token to cover the transferIntent amount
393
417
string memory counterpartSymbol = TokenWrapper.getWrapperCounterpartSymbol (chainId, assetSymbol);
@@ -468,7 +492,7 @@ contract QuarkBuilderBase {
468
492
if (bridgeActionContext.token == bridgeActions[i].paymentToken) {
469
493
// If the payment token is the transfer token and this is the target chain, we need to account for the transfer amount
470
494
// If its bridge step, check if user has enough balance to cover the bridge amount
471
- if (paymentAssetBalanceOnChain < bridgeActions[i].paymentMaxCost + bridgeActionContext.amount ) {
495
+ if (paymentAssetBalanceOnChain < bridgeActions[i].paymentMaxCost + bridgeActionContext.inputAmount ) {
472
496
revert MaxCostTooHigh ();
473
497
}
474
498
} else {
@@ -479,7 +503,7 @@ contract QuarkBuilderBase {
479
503
}
480
504
481
505
if (Strings.stringEqIgnoreCase (bridgeActionContext.assetSymbol, paymentTokenSymbol)) {
482
- paymentTokenBridgeAmount += bridgeActionContext.amount ;
506
+ paymentTokenBridgeAmount += bridgeActionContext.outputAmount ;
483
507
}
484
508
}
485
509
0 commit comments