diff --git a/src/builder/Actions.sol b/src/builder/Actions.sol index b574652..520a858 100644 --- a/src/builder/Actions.sol +++ b/src/builder/Actions.sol @@ -1281,70 +1281,75 @@ library Actions { return (quarkOperation, action); } - function morphoClaimRewards(MorphoClaimRewards memory claimRewards, PaymentInfo.Payment memory payment) - internal - pure - returns (IQuarkWallet.QuarkOperation memory, Action memory) - { - bytes[] memory scriptSources = new bytes[](1); - scriptSources[0] = type(MorphoRewardsActions).creationCode; - - Accounts.ChainAccounts memory accounts = - Accounts.findChainAccounts(claimRewards.chainId, claimRewards.chainAccountsList); - - Accounts.QuarkState memory accountState = Accounts.findQuarkState(claimRewards.claimer, accounts.quarkStates); - - List.DynamicArray memory rewardsPriceList = List.newList(); - List.DynamicArray memory rewardsSymbolList = List.newList(); - for (uint256 i = 0; i < claimRewards.rewards.length; i++) { - Accounts.AssetPositions memory rewardsAssetPosition = - Accounts.findAssetPositions(claimRewards.rewards[i], accounts.assetPositionsList); - List.addUint256(rewardsPriceList, rewardsAssetPosition.usdPrice); - List.addString(rewardsSymbolList, rewardsAssetPosition.symbol); - } - - bytes memory scriptCalldata = abi.encodeWithSelector( - MorphoRewardsActions.claimAll.selector, - claimRewards.distributors, - claimRewards.accounts, - claimRewards.rewards, - claimRewards.claimables, - claimRewards.proofs - ); - - // Construct QuarkOperation - IQuarkWallet.QuarkOperation memory quarkOperation = IQuarkWallet.QuarkOperation({ - nonce: accountState.quarkNextNonce, - scriptAddress: CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), - scriptCalldata: scriptCalldata, - scriptSources: scriptSources, - expiry: claimRewards.blockTimestamp + STANDARD_EXPIRY_BUFFER - }); - - MorphoClaimRewardsActionContext memory claimRewardsActionContext = MorphoClaimRewardsActionContext({ - amounts: claimRewards.claimables, - assetSymbols: List.toStringArray(rewardsSymbolList), - chainId: claimRewards.chainId, - prices: List.toUint256Array(rewardsPriceList), - tokens: claimRewards.rewards - }); - - Action memory action = Actions.Action({ - chainId: claimRewards.chainId, - quarkAccount: claimRewards.claimer, - actionType: ACTION_TYPE_MORPHO_CLAIM_REWARDS, - actionContext: abi.encode(claimRewardsActionContext), - paymentMethod: PaymentInfo.paymentMethodForPayment(payment, false), - // Null address for OFFCHAIN payment. - paymentToken: payment.isToken - ? PaymentInfo.knownToken(payment.currency, claimRewards.chainId).token - : address(0), - paymentTokenSymbol: payment.currency, - paymentMaxCost: payment.isToken ? PaymentInfo.findMaxCost(payment, claimRewards.chainId) : 0 - }); - - return (quarkOperation, action); - } + // TODO: Commenting because it is currently unused and will result in stack too deep + // function morphoClaimRewards(MorphoClaimRewards memory claimRewards, PaymentInfo.Payment memory payment) + // internal + // pure + // returns (IQuarkWallet.QuarkOperation memory, Action memory) + // { + // bytes[] memory scriptSources = new bytes[](1); + // scriptSources[0] = type(MorphoRewardsActions).creationCode; + + // Accounts.ChainAccounts memory accounts = + // Accounts.findChainAccounts(claimRewards.chainId, claimRewards.chainAccountsList); + + // Accounts.QuarkSecret memory accountSecret = + // Accounts.findQuarkSecret(claimRewards.claimer, accounts.quarkSecrets); + + // string[] memory rewardsAssetSymbols = new string[](claimRewards.rewards.length); + // uint256[] memory rewardsPrices = new uint256[](claimRewards.rewards.length); + // for (uint256 i = 0; i < claimRewards.rewards.length; ++i) { + // Accounts.AssetPositions memory rewardsAssetPosition = + // Accounts.findAssetPositions(claimRewards.rewards[i], accounts.assetPositionsList); + // rewardsAssetSymbols[i] = rewardsAssetPosition.symbol; + // rewardsPrices[i] = rewardsAssetPosition.usdPrice; + // } + + // bytes memory scriptCalldata = abi.encodeWithSelector( + // MorphoRewardsActions.claimAll.selector, + // claimRewards.distributors, + // claimRewards.accounts, + // claimRewards.rewards, + // claimRewards.claimables, + // claimRewards.proofs + // ); + + // // Construct QuarkOperation + // IQuarkWallet.QuarkOperation memory quarkOperation = IQuarkWallet.QuarkOperation({ + // nonce: accountSecret.nonceSecret, + // isReplayable: false, + // scriptAddress: CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), + // scriptCalldata: scriptCalldata, + // scriptSources: scriptSources, + // expiry: claimRewards.blockTimestamp + STANDARD_EXPIRY_BUFFER + // }); + + // MorphoClaimRewardsActionContext memory claimRewardsActionContext = MorphoClaimRewardsActionContext({ + // amounts: claimRewards.claimables, + // assetSymbols: rewardsAssetSymbols, + // chainId: claimRewards.chainId, + // prices: rewardsPrices, + // tokens: claimRewards.rewards + // }); + + // Action memory action = Actions.Action({ + // chainId: claimRewards.chainId, + // quarkAccount: claimRewards.claimer, + // actionType: ACTION_TYPE_MORPHO_CLAIM_REWARDS, + // actionContext: abi.encode(claimRewardsActionContext), + // paymentMethod: PaymentInfo.paymentMethodForPayment(payment, false), + // // Null address for OFFCHAIN payment. + // paymentToken: payment.isToken + // ? PaymentInfo.knownToken(payment.currency, claimRewards.chainId).token + // : address(0), + // paymentTokenSymbol: payment.currency, + // paymentMaxCost: payment.isToken ? PaymentInfo.findMaxCost(payment, claimRewards.chainId) : 0, + // nonceSecret: accountSecret.nonceSecret, + // totalPlays: 1 + // }); + + // return (quarkOperation, action); + // } function wrapOrUnwrapAsset( WrapOrUnwrapAsset memory wrapOrUnwrap, @@ -1527,7 +1532,6 @@ library Actions { }); } - // Construct QuarkOperation IQuarkWallet.QuarkOperation memory quarkOperation; // Local scope to avoid stack too deep { @@ -1538,6 +1542,7 @@ library Actions { bytes memory scriptCalldata = abi.encodeWithSelector(RecurringSwap.swap.selector, swapConfig); bytes32 nonce = generateNonceFromSecret(localVars.accountSecret.nonceSecret, RECURRING_SWAP_TOTAL_PLAYS); + // Construct QuarkOperation quarkOperation = IQuarkWallet.QuarkOperation({ nonce: nonce, isReplayable: true, diff --git a/src/builder/QuarkBuilder.sol b/src/builder/QuarkBuilder.sol index 800024a..018aa10 100644 --- a/src/builder/QuarkBuilder.sol +++ b/src/builder/QuarkBuilder.sol @@ -195,11 +195,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - repayIntent.chainId, - repayIntent.repayer, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: repayIntent.chainId, + account: repayIntent.repayer, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -371,11 +373,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - borrowIntent.chainId, - borrowIntent.borrower, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: borrowIntent.chainId, + account: borrowIntent.borrower, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -638,11 +642,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - cometWithdrawIntent.chainId, - cometWithdrawIntent.withdrawer, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: cometWithdrawIntent.chainId, + account: cometWithdrawIntent.withdrawer, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -1266,11 +1272,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - borrowIntent.chainId, - borrowIntent.borrower, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: borrowIntent.chainId, + account: borrowIntent.borrower, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -1435,11 +1443,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - repayIntent.chainId, - repayIntent.repayer, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: repayIntent.chainId, + account: repayIntent.repayer, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -1690,11 +1700,13 @@ contract QuarkBuilder { } assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - withdrawIntent.chainId, - withdrawIntent.withdrawer, - supplementalPaymentTokenBalance + PaymentBalanceAssertionArgs({ + actions: actionsArray, + chainAccountsList: chainAccountsList, + targetChainId: withdrawIntent.chainId, + account: withdrawIntent.withdrawer, + supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + }) ); } @@ -1718,139 +1730,146 @@ contract QuarkBuilder { }); } - struct MorphoRewardsClaimIntent { - uint256 blockTimestamp; - address claimer; - uint256 chainId; - address[] accounts; - uint256[] claimables; - address[] distributors; - address[] rewards; - bytes32[][] proofs; - } - - function morphoClaimRewards( - MorphoRewardsClaimIntent memory claimIntent, - Accounts.ChainAccounts[] memory chainAccountsList, - PaymentInfo.Payment memory payment - ) external pure returns (BuilderResult memory) { - if ( - claimIntent.accounts.length != claimIntent.claimables.length - || claimIntent.accounts.length != claimIntent.distributors.length - || claimIntent.accounts.length != claimIntent.rewards.length - || claimIntent.accounts.length != claimIntent.proofs.length - ) { - revert InvalidInput(); - } - - bool useQuotecall = false; // never use Quotecall - List.DynamicArray memory actions = List.newList(); - List.DynamicArray memory quarkOperations = List.newList(); - - // when paying with tokens, you may need to bridge the payment token to cover the cost - if (payment.isToken) { - uint256 maxCostOnDstChain = PaymentInfo.findMaxCost(payment, claimIntent.chainId); - // if you're claiming rewards in payment token, you can use the withdrawn amount to cover the cost - for (uint256 i = 0; i < claimIntent.rewards.length; ++i) { - if ( - Strings.stringEqIgnoreCase( - payment.currency, - Accounts.findAssetPositions(claimIntent.rewards[i], claimIntent.chainId, chainAccountsList) - .symbol - ) - ) { - maxCostOnDstChain = Math.subtractFlooredAtZero(maxCostOnDstChain, claimIntent.claimables[i]); - } - } - - if (needsBridgedFunds(payment.currency, maxCostOnDstChain, claimIntent.chainId, chainAccountsList, payment)) - { - (IQuarkWallet.QuarkOperation[] memory bridgeQuarkOperations, Actions.Action[] memory bridgeActions) = - Actions.constructBridgeOperations( - Actions.BridgeOperationInfo({ - assetSymbol: payment.currency, - amountNeededOnDst: maxCostOnDstChain, - dstChainId: claimIntent.chainId, - recipient: claimIntent.claimer, - blockTimestamp: claimIntent.blockTimestamp, - useQuotecall: useQuotecall - }), - chainAccountsList, - payment - ); - - for (uint256 i = 0; i < bridgeQuarkOperations.length; ++i) { - List.addQuarkOperation(quarkOperations, bridgeQuarkOperations[i]); - List.addAction(actions, bridgeActions[i]); - } - } - } - - (IQuarkWallet.QuarkOperation memory cometWithdrawQuarkOperation, Actions.Action memory cometWithdrawAction) = - Actions.morphoClaimRewards( - Actions.MorphoClaimRewards({ - chainAccountsList: chainAccountsList, - accounts: claimIntent.accounts, - blockTimestamp: claimIntent.blockTimestamp, - chainId: claimIntent.chainId, - claimables: claimIntent.claimables, - claimer: claimIntent.claimer, - distributors: claimIntent.distributors, - rewards: claimIntent.rewards, - proofs: claimIntent.proofs - }), - payment - ); - List.addAction(actions, cometWithdrawAction); - List.addQuarkOperation(quarkOperations, cometWithdrawQuarkOperation); - - // Convert actions and quark operations to arrays - Actions.Action[] memory actionsArray = List.toActionArray(actions); - IQuarkWallet.QuarkOperation[] memory quarkOperationsArray = List.toQuarkOperationArray(quarkOperations); - - // Validate generated actions for affordability - if (payment.isToken) { - uint256 supplementalPaymentTokenBalance = 0; - for (uint256 i = 0; i < claimIntent.rewards.length; ++i) { - if ( - Strings.stringEqIgnoreCase( - payment.currency, - Accounts.findAssetPositions(claimIntent.rewards[i], claimIntent.chainId, chainAccountsList) - .symbol - ) - ) { - supplementalPaymentTokenBalance += claimIntent.claimables[i]; - } - } - - assertSufficientPaymentTokenBalances( - actionsArray, - chainAccountsList, - claimIntent.chainId, - claimIntent.claimer, - supplementalPaymentTokenBalance - ); - } - - // Merge operations that are from the same chain into one Multicall operation - (quarkOperationsArray, actionsArray) = - QuarkOperationHelper.mergeSameChainOperations(quarkOperationsArray, actionsArray); - - // Wrap operations around Paycall/Quotecall if payment is with token - if (payment.isToken) { - quarkOperationsArray = QuarkOperationHelper.wrapOperationsWithTokenPayment( - quarkOperationsArray, actionsArray, payment, useQuotecall - ); - } - - return BuilderResult({ - version: VERSION, - actions: actionsArray, - quarkOperations: quarkOperationsArray, - paymentCurrency: payment.currency, - eip712Data: EIP712Helper.eip712DataForQuarkOperations(quarkOperationsArray, actionsArray) - }); - } + // TODO: Commenting because it is currently unused and will result in stack too deep + // Note: The root case for the stack too deep is the yul optimizer. The optimizer currently + // inlines the internal call to `Actions.morphoClaimRewards`. Compiling using `via-ir` but + // without the optimizer works. + + // struct MorphoRewardsClaimIntent { + // uint256 blockTimestamp; + // address claimer; + // uint256 chainId; + // address[] accounts; + // uint256[] claimables; + // address[] distributors; + // address[] rewards; + // bytes32[][] proofs; + // } + + // function morphoClaimRewards( + // MorphoRewardsClaimIntent memory claimIntent, + // Accounts.ChainAccounts[] memory chainAccountsList, + // PaymentInfo.Payment memory payment + // ) external pure returns (BuilderResult memory) { + // if ( + // claimIntent.accounts.length != claimIntent.claimables.length + // || claimIntent.accounts.length != claimIntent.distributors.length + // || claimIntent.accounts.length != claimIntent.rewards.length + // || claimIntent.accounts.length != claimIntent.proofs.length + // ) { + // revert InvalidInput(); + // } + + // bool useQuotecall = false; // never use Quotecall + // List.DynamicArray memory actions = List.newList(); + // List.DynamicArray memory quarkOperations = List.newList(); + + // // when paying with tokens, you may need to bridge the payment token to cover the cost + // if (payment.isToken) { + // uint256 maxCostOnDstChain = PaymentInfo.findMaxCost(payment, claimIntent.chainId); + // // if you're claiming rewards in payment token, you can use the withdrawn amount to cover the cost + // for (uint256 i = 0; i < claimIntent.rewards.length; ++i) { + // if ( + // Strings.stringEqIgnoreCase( + // payment.currency, + // Accounts.findAssetPositions(claimIntent.rewards[i], claimIntent.chainId, chainAccountsList) + // .symbol + // ) + // ) { + // maxCostOnDstChain = Math.subtractFlooredAtZero(maxCostOnDstChain, claimIntent.claimables[i]); + // } + // } + + // if (needsBridgedFunds(payment.currency, maxCostOnDstChain, claimIntent.chainId, chainAccountsList, payment)) + // { + // (IQuarkWallet.QuarkOperation[] memory bridgeQuarkOperations, Actions.Action[] memory bridgeActions) = + // Actions.constructBridgeOperations( + // Actions.BridgeOperationInfo({ + // assetSymbol: payment.currency, + // amountNeededOnDst: maxCostOnDstChain, + // dstChainId: claimIntent.chainId, + // recipient: claimIntent.claimer, + // blockTimestamp: claimIntent.blockTimestamp, + // useQuotecall: useQuotecall + // }), + // chainAccountsList, + // payment + // ); + + // for (uint256 i = 0; i < bridgeQuarkOperations.length; ++i) { + // List.addQuarkOperation(quarkOperations, bridgeQuarkOperations[i]); + // List.addAction(actions, bridgeActions[i]); + // } + // } + // } + + // (IQuarkWallet.QuarkOperation memory cometWithdrawQuarkOperation, Actions.Action memory cometWithdrawAction) = + // Actions.morphoClaimRewards( + // Actions.MorphoClaimRewards({ + // chainAccountsList: chainAccountsList, + // accounts: claimIntent.accounts, + // blockTimestamp: claimIntent.blockTimestamp, + // chainId: claimIntent.chainId, + // claimables: claimIntent.claimables, + // claimer: claimIntent.claimer, + // distributors: claimIntent.distributors, + // rewards: claimIntent.rewards, + // proofs: claimIntent.proofs + // }), + // payment + // ); + // List.addAction(actions, cometWithdrawAction); + // List.addQuarkOperation(quarkOperations, cometWithdrawQuarkOperation); + + // // Convert actions and quark operations to arrays + // Actions.Action[] memory actionsArray = List.toActionArray(actions); + // IQuarkWallet.QuarkOperation[] memory quarkOperationsArray = List.toQuarkOperationArray(quarkOperations); + + // // Validate generated actions for affordability + // if (payment.isToken) { + // uint256 supplementalPaymentTokenBalance = 0; + // for (uint256 i = 0; i < claimIntent.rewards.length; ++i) { + // if ( + // Strings.stringEqIgnoreCase( + // payment.currency, + // Accounts.findAssetPositions(claimIntent.rewards[i], claimIntent.chainId, chainAccountsList) + // .symbol + // ) + // ) { + // supplementalPaymentTokenBalance += claimIntent.claimables[i]; + // } + // } + + // assertSufficientPaymentTokenBalances( + // PaymentBalanceAssertionArgs({ + // actions: actionsArray, + // chainAccountsList: chainAccountsList, + // targetChainId: claimIntent.chainId, + // account: claimIntent.claimer, + // supplementalPaymentTokenBalance: supplementalPaymentTokenBalance + // }) + // ); + // } + + // // Merge operations that are from the same chain into one Multicall operation + // (quarkOperationsArray, actionsArray) = + // QuarkOperationHelper.mergeSameChainOperations(quarkOperationsArray, actionsArray); + + // // Wrap operations around Paycall/Quotecall if payment is with token + // if (payment.isToken) { + // quarkOperationsArray = QuarkOperationHelper.wrapOperationsWithTokenPayment( + // quarkOperationsArray, actionsArray, payment, useQuotecall + // ); + // } + + // return BuilderResult({ + // version: VERSION, + // actions: actionsArray, + // quarkOperations: quarkOperationsArray, + // paymentCurrency: payment.currency, + // eip712Data: EIP712Helper.eip712DataForQuarkOperations(quarkOperationsArray, actionsArray) + // }); + // } // For some reason, funds that may otherwise be bridgeable or held by the user cannot // be made available to fulfill the transaction. @@ -2047,18 +2066,29 @@ contract QuarkBuilder { uint256 targetChainId, address account ) internal pure { - return assertSufficientPaymentTokenBalances(actions, chainAccountsList, targetChainId, account, 0); + return assertSufficientPaymentTokenBalances( + PaymentBalanceAssertionArgs({ + actions: actions, + chainAccountsList: chainAccountsList, + targetChainId: targetChainId, + account: account, + supplementalPaymentTokenBalance: 0 + }) + ); } - function assertSufficientPaymentTokenBalances( - Actions.Action[] memory actions, - Accounts.ChainAccounts[] memory chainAccountsList, - uint256 targetChainId, - address account, - uint256 supplementalPaymentTokenBalance - ) internal pure { - Actions.Action[] memory bridgeActions = Actions.findActionsOfType(actions, Actions.ACTION_TYPE_BRIDGE); - Actions.Action[] memory nonBridgeActions = Actions.findActionsNotOfType(actions, Actions.ACTION_TYPE_BRIDGE); + struct PaymentBalanceAssertionArgs { + Actions.Action[] actions; + Accounts.ChainAccounts[] chainAccountsList; + uint256 targetChainId; + address account; + uint256 supplementalPaymentTokenBalance; + } + + function assertSufficientPaymentTokenBalances(PaymentBalanceAssertionArgs memory args) internal pure { + Actions.Action[] memory bridgeActions = Actions.findActionsOfType(args.actions, Actions.ACTION_TYPE_BRIDGE); + Actions.Action[] memory nonBridgeActions = + Actions.findActionsNotOfType(args.actions, Actions.ACTION_TYPE_BRIDGE); string memory paymentTokenSymbol = nonBridgeActions[0].paymentTokenSymbol; // assumes all actions use the same payment token uint256 paymentTokenBridgeAmount = 0; @@ -2067,7 +2097,9 @@ contract QuarkBuilder { Actions.BridgeActionContext memory bridgeActionContext = abi.decode(bridgeActions[i].actionContext, (Actions.BridgeActionContext)); uint256 paymentAssetBalanceOnChain = Accounts.sumBalances( - Accounts.findAssetPositions(bridgeActions[i].paymentToken, bridgeActions[i].chainId, chainAccountsList) + Accounts.findAssetPositions( + bridgeActions[i].paymentToken, bridgeActions[i].chainId, args.chainAccountsList + ) ); if (bridgeActionContext.token == bridgeActions[i].paymentToken) { // If the payment token is the transfer token and this is the target chain, we need to account for the transfer amount @@ -2087,13 +2119,14 @@ contract QuarkBuilder { } } - uint256 targetChainPaymentTokenBalance = - Accounts.sumBalances(Accounts.findAssetPositions(paymentTokenSymbol, targetChainId, chainAccountsList)); // assumes that all non-bridge actions occur on the target chain + uint256 targetChainPaymentTokenBalance = Accounts.sumBalances( + Accounts.findAssetPositions(paymentTokenSymbol, args.targetChainId, args.chainAccountsList) + ); // assumes that all non-bridge actions occur on the target chain uint256 paymentTokenCost = 0; for (uint256 i = 0; i < nonBridgeActions.length; ++i) { Actions.Action memory nonBridgeAction = nonBridgeActions[i]; - if (nonBridgeAction.chainId != targetChainId) { + if (nonBridgeAction.chainId != args.targetChainId) { revert InvalidActionChain(); } paymentTokenCost += nonBridgeAction.paymentMaxCost; @@ -2110,11 +2143,11 @@ contract QuarkBuilder { abi.decode(nonBridgeAction.actionContext, (Actions.MorphoRepayActionContext)); if (morphoRepayActionContext.amount == type(uint256).max) { paymentTokenCost += morphoRepayMaxAmount( - chainAccountsList, + args.chainAccountsList, morphoRepayActionContext.chainId, morphoRepayActionContext.token, morphoRepayActionContext.collateralToken, - account + args.account ); } else { paymentTokenCost += morphoRepayActionContext.amount; @@ -2125,7 +2158,10 @@ contract QuarkBuilder { if (Strings.stringEqIgnoreCase(cometRepayActionContext.assetSymbol, paymentTokenSymbol)) { if (cometRepayActionContext.amount == type(uint256).max) { paymentTokenCost += cometRepayMaxAmount( - chainAccountsList, cometRepayActionContext.chainId, cometRepayActionContext.comet, account + args.chainAccountsList, + cometRepayActionContext.chainId, + cometRepayActionContext.comet, + args.account ); } else { paymentTokenCost += cometRepayActionContext.amount; @@ -2185,7 +2221,7 @@ contract QuarkBuilder { if ( paymentTokenCost - > (targetChainPaymentTokenBalance + paymentTokenBridgeAmount + supplementalPaymentTokenBalance) + > (targetChainPaymentTokenBalance + paymentTokenBridgeAmount + args.supplementalPaymentTokenBalance) ) { revert MaxCostTooHigh(); } diff --git a/test/CometClaimRewards.t.sol b/test/CometClaimRewards.t.sol index 8fe03f0..8bad92e 100644 --- a/test/CometClaimRewards.t.sol +++ b/test/CometClaimRewards.t.sol @@ -71,8 +71,10 @@ contract CometClaimRewardsTest is Test { ); (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); assertEq(IERC20(COMP).balanceOf(address(wallet)), 0e6); + vm.resumeGasMetering(); wallet.executeQuarkOperation(op, v, r, s); + assertGt(IERC20(COMP).balanceOf(address(wallet)), 0e6); } } diff --git a/test/builder/QuarkBuilderMorphoClaimRewards.t.sol b/test/builder/QuarkBuilderMorphoClaimRewards.t.sol index e5adfd8..304c560 100644 --- a/test/builder/QuarkBuilderMorphoClaimRewards.t.sol +++ b/test/builder/QuarkBuilderMorphoClaimRewards.t.sol @@ -1,253 +1,261 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity ^0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import {Arrays} from "test/builder/lib/Arrays.sol"; -import {Accounts, PaymentInfo, QuarkBuilderTest} from "test/builder/lib/QuarkBuilderTest.sol"; -import {Actions} from "src/builder/Actions.sol"; -import {CCTPBridgeActions} from "src/BridgeScripts.sol"; -import {CodeJarHelper} from "src/builder/CodeJarHelper.sol"; -import {TransferActions} from "src/DeFiScripts.sol"; -import {MorphoInfo} from "src/builder/MorphoInfo.sol"; -import {MorphoRewardsActions} from "src/MorphoScripts.sol"; -import {List} from "src/builder/List.sol"; -import {Paycall} from "src/Paycall.sol"; -import {QuarkBuilder} from "src/builder/QuarkBuilder.sol"; - -contract QuarkBuilderMorphoClaimRewardsTest is Test, QuarkBuilderTest { - // Fixtures of morpho reward data to pass in - address[] fixtureDistributors = - [0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb, 0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb]; - address[] fixtureAccounts = [address(0xa11ce), address(0xa11ce)]; - address[] fixtureRewards = [usdc_(1), weth_(1)]; - address[] fixtureInvalidRewards = [usdc_(1)]; - uint256[] fixtureClaimables = [100e6, 2e18]; - uint256[] fixtureClaimablesLessUSDC = [1e6, 2e18]; - bytes32[][] fixtureProofs = [ - [ - bytes32(0xce63a4c1fabb68437d0e5edc21b732c5a215f1c5a9ed6a52902f0415e148cc0a), - bytes32(0x23b2ad869c44ff4946d49f0e048edd1303f0cef3679d3e21143c4cfdcde97f20), - bytes32(0x937a82a4d574f809052269e6d4a5613fa4ce333064d012e96e9cc3c04fee7a9c), - bytes32(0xf93fea78509a3b4fe28d963d965ab8819bbf6c08f5789bddde16127e98e6f696), - bytes32(0xbb53cefdee57ab5a04a7be61a15c1ea00beacd0a4adb132dd2e046582eafbec8), - bytes32(0x3dcb507af99e19c829fc2f5a8f57418258230818d4db8dc3080e5cafff5bfd3c), - bytes32(0xca3e0c0cc07c55a02cbc21313bbd9a4d27dae6a28580fbd7dfad74216d4edac3), - bytes32(0x59bdab6ff3d8cd5c682ff241da1d56e9bba6f5c0a739c28629c10ffab8bb9c95), - bytes32(0x56a6fd126541d4a6b4902b78125db2c92b3b9cfb3249bbe3681cc2ccf9a6aa2c), - bytes32(0xfcfad3b73969b50e0369e94db6fcd9301b5e776784620a09c0b52a5cf3326f2b), - bytes32(0x7ee3c650dc15c36a6a0284c40b61391f7ac07f57d50802d92d2ccb7a19ff9dbb) - ], - [ - bytes32(0x7ac5a364f8e3d902a778e6f22d9800304bce9a24108a6b375e9d7afffa586648), - bytes32(0xd0e2f9d70a7c8ddfe74cf2e922067421f06af4c16da32c13d13e6226aff54772), - bytes32(0x8417ffe0c1e153c75ad3bf85f8d52b22ebc5370deda637231cb7fef3238d60b7), - bytes32(0x99baa8011e519a6650c7f8887edde764c9198973be390dfad9a43e8af4603326), - bytes32(0x7db554929334c43f06c93b0917a22765ba0b27684eb3bdbb09eefaad665cf51f), - bytes32(0xd35638edfe77f64712acd397cfddd12da5ba480d05d77b52fa5f9f930b8c4a11), - bytes32(0xee0010ba447e3edda1a034acc142e66ce5c772dc9cbbdf86044e5ee760d4159f), - bytes32(0xedca6a5e9ba49d334eebdc4167e1730fcce5c7e4bbc17638c1cb6b4c42e85e9b), - bytes32(0xfd8786de55c7c2e69c4ede4fe80b5d696875621b7aea7f29736451d3ea667427), - bytes32(0xff695c9c3721e77a593d67cf0cbea7d495d0120ed51e31ab1428a7251665ce37), - bytes32(0x487b38c91a22d77f124819ab4d40eea67b11683459c458933cae385630c90816) - ] - ]; - - function morphoClaimRewardsIntent_( - uint256 chainId, - address[] memory accounts, - uint256[] memory claimables, - address[] memory distributors, - address[] memory rewards, - bytes32[][] memory proofs - ) internal pure returns (QuarkBuilder.MorphoRewardsClaimIntent memory) { - return QuarkBuilder.MorphoRewardsClaimIntent({ - blockTimestamp: BLOCK_TIMESTAMP, - claimer: address(0xa11ce), - chainId: chainId, - accounts: accounts, - claimables: claimables, - distributors: distributors, - rewards: rewards, - proofs: proofs - }); - } - - function testMorphoClaimRewards() public { - QuarkBuilder builder = new QuarkBuilder(); - QuarkBuilder.BuilderResult memory result = builder.morphoClaimRewards( - morphoClaimRewardsIntent_( - 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureRewards, fixtureProofs - ), - chainAccountsList_(2e6), // holding 2 USDC in total across 1, 8453 - paymentUsd_() - ); - - assertEq(result.paymentCurrency, "usd", "usd currency"); - - // Check the quark operations - assertEq(result.quarkOperations.length, 1, "one operation"); - assertEq( - result.quarkOperations[0].scriptAddress, - CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), - "script address is correct given the code jar address on mainnet" - ); - assertEq( - result.quarkOperations[0].scriptCalldata, - abi.encodeCall( - MorphoRewardsActions.claimAll, - (fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs) - ), - "calldata is MorphoRewardsActions.claimAll(fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs);" - ); - assertEq( - result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" - ); - - // check the actions - assertEq(result.actions.length, 1, "one action"); - assertEq(result.actions[0].chainId, 1, "operation is on chainId 1"); - assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); - assertEq(result.actions[0].actionType, "MORPHO_CLAIM_REWARDS", "action type is 'MORPHO_CLAIM_REWARDS'"); - assertEq(result.actions[0].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); - assertEq(result.actions[0].paymentToken, address(0), "payment token is null"); - assertEq(result.actions[0].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); - string[] memory assetSymbols = new string[](2); - assetSymbols[0] = "USDC"; - assetSymbols[1] = "WETH"; - uint256[] memory prices = new uint256[](2); - prices[0] = USDC_PRICE; - prices[1] = WETH_PRICE; - address[] memory tokens = new address[](2); - tokens[0] = USDC_1; - tokens[1] = WETH_1; - assertEq( - result.actions[0].actionContext, - abi.encode( - Actions.MorphoClaimRewardsActionContext({ - amounts: fixtureClaimables, - assetSymbols: assetSymbols, - chainId: 1, - prices: prices, - tokens: tokens - }) - ), - "action context encoded from WithdrawActionContext" - ); - - // TODO: Check the contents of the EIP712 data - assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); - assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); - assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); - } - - function testMorphoClaimRewardsPayWithReward() public { - QuarkBuilder builder = new QuarkBuilder(); - PaymentInfo.PaymentMaxCost[] memory maxCosts = new PaymentInfo.PaymentMaxCost[](1); - maxCosts[0] = PaymentInfo.PaymentMaxCost({chainId: 1, amount: 1e6}); - QuarkBuilder.BuilderResult memory result = builder.morphoClaimRewards( - morphoClaimRewardsIntent_( - 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureRewards, fixtureProofs - ), - chainAccountsList_(0), - paymentUsdc_(maxCosts) - ); - - assertEq(result.paymentCurrency, "usdc", "usdc currency"); - - // Check the quark operations - assertEq(result.quarkOperations.length, 1, "one operation"); - assertEq( - result.quarkOperations[0].scriptAddress, - paycallUsdc_(1), - "script address is correct given the code jar address on mainnet" - ); - - assertEq( - result.quarkOperations[0].scriptCalldata, - abi.encodeWithSelector( - Paycall.run.selector, - CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), - abi.encodeCall( - MorphoRewardsActions.claimAll, - (fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs) - ), - 1e6 - ), - "calldata is Paycall.run(MorphoRewardsActions.claimAll(fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs));" - ); - - assertEq( - result.quarkOperations[0].scriptSources[1], - abi.encodePacked(type(Paycall).creationCode, abi.encode(ETH_USD_PRICE_FEED_1, USDC_1)) - ); - assertEq( - result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" - ); - - // check the actions - assertEq(result.actions.length, 1, "one action"); - assertEq(result.actions[0].chainId, 1, "operation is on chainId 1"); - assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); - assertEq(result.actions[0].actionType, "MORPHO_CLAIM_REWARDS", "action type is 'MORPHO_CLAIM_REWARDS'"); - assertEq(result.actions[0].paymentMethod, "PAY_CALL", "payment method is 'PAY_CALL'"); - assertEq(result.actions[0].paymentToken, USDC_1, "payment token is USDC"); - assertEq(result.actions[0].paymentMaxCost, 1e6, "payment max is set to .1e6 in this test case"); - - string[] memory assetSymbols = new string[](2); - assetSymbols[0] = "USDC"; - assetSymbols[1] = "WETH"; - uint256[] memory prices = new uint256[](2); - prices[0] = USDC_PRICE; - prices[1] = WETH_PRICE; - address[] memory tokens = new address[](2); - tokens[0] = USDC_1; - tokens[1] = WETH_1; - assertEq( - result.actions[0].actionContext, - abi.encode( - Actions.MorphoClaimRewardsActionContext({ - amounts: fixtureClaimables, - assetSymbols: assetSymbols, - chainId: 1, - prices: prices, - tokens: tokens - }) - ), - "action context encoded from WithdrawActionContext" - ); - - // TODO: Check the contents of the EIP712 data - assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); - assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); - assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); - } - - function testMorphoClaimRewardsWithNotEnoughRewardToCoverCost() public { - QuarkBuilder builder = new QuarkBuilder(); - PaymentInfo.PaymentMaxCost[] memory maxCosts = new PaymentInfo.PaymentMaxCost[](2); - maxCosts[0] = PaymentInfo.PaymentMaxCost({chainId: 1, amount: 5e6}); - maxCosts[1] = PaymentInfo.PaymentMaxCost({chainId: 8453, amount: 5e6}); - vm.expectRevert(abi.encodeWithSelector(Actions.NotEnoughFundsToBridge.selector, "usdc", 3e6, 3e6)); - builder.morphoClaimRewards( - morphoClaimRewardsIntent_( - 1, fixtureAccounts, fixtureClaimablesLessUSDC, fixtureDistributors, fixtureRewards, fixtureProofs - ), - chainAccountsList_(2e6), - paymentUsdc_(maxCosts) - ); - } - - function testMorphoClaimRewardsInvalid() public { - QuarkBuilder builder = new QuarkBuilder(); - vm.expectRevert(QuarkBuilder.InvalidInput.selector); - builder.morphoClaimRewards( - morphoClaimRewardsIntent_( - 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureInvalidRewards, fixtureProofs - ), - chainAccountsList_(2e6), - paymentUsd_() - ); - } -} +// TODO: Commenting because it is currently unused and will result in stack too deep +// // SPDX-License-Identifier: BSD-3-Clause +// pragma solidity ^0.8.23; + +// import "forge-std/Test.sol"; +// import "forge-std/console.sol"; + +// import {Arrays} from "test/builder/lib/Arrays.sol"; +// import {Accounts, PaymentInfo, QuarkBuilderTest} from "test/builder/lib/QuarkBuilderTest.sol"; +// import {Actions} from "src/builder/Actions.sol"; +// import {CCTPBridgeActions} from "src/BridgeScripts.sol"; +// import {CodeJarHelper} from "src/builder/CodeJarHelper.sol"; +// import {TransferActions} from "src/DeFiScripts.sol"; +// import {MorphoInfo} from "src/builder/MorphoInfo.sol"; +// import {MorphoRewardsActions} from "src/MorphoScripts.sol"; +// import {List} from "src/builder/List.sol"; +// import {Paycall} from "src/Paycall.sol"; +// import {QuarkBuilder} from "src/builder/QuarkBuilder.sol"; + +// contract QuarkBuilderMorphoClaimRewardsTest is Test, QuarkBuilderTest { +// // Fixtures of morpho reward data to pass in +// address[] fixtureDistributors = +// [0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb, 0x330eefa8a787552DC5cAd3C3cA644844B1E61Ddb]; +// address[] fixtureAccounts = [address(0xa11ce), address(0xa11ce)]; +// address[] fixtureRewards = [usdc_(1), weth_(1)]; +// address[] fixtureInvalidRewards = [usdc_(1)]; +// uint256[] fixtureClaimables = [100e6, 2e18]; +// uint256[] fixtureClaimablesLessUSDC = [1e6, 2e18]; +// bytes32[][] fixtureProofs = [ +// [ +// bytes32(0xce63a4c1fabb68437d0e5edc21b732c5a215f1c5a9ed6a52902f0415e148cc0a), +// bytes32(0x23b2ad869c44ff4946d49f0e048edd1303f0cef3679d3e21143c4cfdcde97f20), +// bytes32(0x937a82a4d574f809052269e6d4a5613fa4ce333064d012e96e9cc3c04fee7a9c), +// bytes32(0xf93fea78509a3b4fe28d963d965ab8819bbf6c08f5789bddde16127e98e6f696), +// bytes32(0xbb53cefdee57ab5a04a7be61a15c1ea00beacd0a4adb132dd2e046582eafbec8), +// bytes32(0x3dcb507af99e19c829fc2f5a8f57418258230818d4db8dc3080e5cafff5bfd3c), +// bytes32(0xca3e0c0cc07c55a02cbc21313bbd9a4d27dae6a28580fbd7dfad74216d4edac3), +// bytes32(0x59bdab6ff3d8cd5c682ff241da1d56e9bba6f5c0a739c28629c10ffab8bb9c95), +// bytes32(0x56a6fd126541d4a6b4902b78125db2c92b3b9cfb3249bbe3681cc2ccf9a6aa2c), +// bytes32(0xfcfad3b73969b50e0369e94db6fcd9301b5e776784620a09c0b52a5cf3326f2b), +// bytes32(0x7ee3c650dc15c36a6a0284c40b61391f7ac07f57d50802d92d2ccb7a19ff9dbb) +// ], +// [ +// bytes32(0x7ac5a364f8e3d902a778e6f22d9800304bce9a24108a6b375e9d7afffa586648), +// bytes32(0xd0e2f9d70a7c8ddfe74cf2e922067421f06af4c16da32c13d13e6226aff54772), +// bytes32(0x8417ffe0c1e153c75ad3bf85f8d52b22ebc5370deda637231cb7fef3238d60b7), +// bytes32(0x99baa8011e519a6650c7f8887edde764c9198973be390dfad9a43e8af4603326), +// bytes32(0x7db554929334c43f06c93b0917a22765ba0b27684eb3bdbb09eefaad665cf51f), +// bytes32(0xd35638edfe77f64712acd397cfddd12da5ba480d05d77b52fa5f9f930b8c4a11), +// bytes32(0xee0010ba447e3edda1a034acc142e66ce5c772dc9cbbdf86044e5ee760d4159f), +// bytes32(0xedca6a5e9ba49d334eebdc4167e1730fcce5c7e4bbc17638c1cb6b4c42e85e9b), +// bytes32(0xfd8786de55c7c2e69c4ede4fe80b5d696875621b7aea7f29736451d3ea667427), +// bytes32(0xff695c9c3721e77a593d67cf0cbea7d495d0120ed51e31ab1428a7251665ce37), +// bytes32(0x487b38c91a22d77f124819ab4d40eea67b11683459c458933cae385630c90816) +// ] +// ]; + +// function morphoClaimRewardsIntent_( +// uint256 chainId, +// address[] memory accounts, +// uint256[] memory claimables, +// address[] memory distributors, +// address[] memory rewards, +// bytes32[][] memory proofs +// ) internal pure returns (QuarkBuilder.MorphoRewardsClaimIntent memory) { +// return QuarkBuilder.MorphoRewardsClaimIntent({ +// blockTimestamp: BLOCK_TIMESTAMP, +// claimer: address(0xa11ce), +// chainId: chainId, +// accounts: accounts, +// claimables: claimables, +// distributors: distributors, +// rewards: rewards, +// proofs: proofs +// }); +// } + +// function testMorphoClaimRewards() public { +// QuarkBuilder builder = new QuarkBuilder(); +// QuarkBuilder.BuilderResult memory result = builder.morphoClaimRewards( +// morphoClaimRewardsIntent_( +// 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureRewards, fixtureProofs +// ), +// chainAccountsList_(2e6), // holding 2 USDC in total across 1, 8453 +// paymentUsd_() +// ); + +// assertEq(result.paymentCurrency, "usd", "usd currency"); + +// // Check the quark operations +// assertEq(result.quarkOperations.length, 1, "one operation"); +// assertEq( +// result.quarkOperations[0].scriptAddress, +// CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), +// "script address is correct given the code jar address on mainnet" +// ); +// assertEq( +// result.quarkOperations[0].scriptCalldata, +// abi.encodeCall( +// MorphoRewardsActions.claimAll, +// (fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs) +// ), +// "calldata is MorphoRewardsActions.claimAll(fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs);" +// ); +// assertEq( +// result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" +// ); +// assertEq(result.quarkOperations[0].nonce, ALICE_DEFAULT_SECRET, "unexpected nonce"); +// assertEq(result.quarkOperations[0].isReplayable, false, "isReplayable is false"); + +// // check the actions +// assertEq(result.actions.length, 1, "one action"); +// assertEq(result.actions[0].chainId, 1, "operation is on chainId 1"); +// assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); +// assertEq(result.actions[0].actionType, "MORPHO_CLAIM_REWARDS", "action type is 'MORPHO_CLAIM_REWARDS'"); +// assertEq(result.actions[0].paymentMethod, "OFFCHAIN", "payment method is 'OFFCHAIN'"); +// assertEq(result.actions[0].paymentToken, address(0), "payment token is null"); +// assertEq(result.actions[0].paymentMaxCost, 0, "payment has no max cost, since 'OFFCHAIN'"); +// assertEq(result.actions[0].nonceSecret, ALICE_DEFAULT_SECRET, "unexpected nonce secret"); +// assertEq(result.actions[0].totalPlays, 1, "total plays is 1"); + +// string[] memory assetSymbols = new string[](2); +// assetSymbols[0] = "USDC"; +// assetSymbols[1] = "WETH"; +// uint256[] memory prices = new uint256[](2); +// prices[0] = USDC_PRICE; +// prices[1] = WETH_PRICE; +// address[] memory tokens = new address[](2); +// tokens[0] = USDC_1; +// tokens[1] = WETH_1; +// assertEq( +// result.actions[0].actionContext, +// abi.encode( +// Actions.MorphoClaimRewardsActionContext({ +// amounts: fixtureClaimables, +// assetSymbols: assetSymbols, +// chainId: 1, +// prices: prices, +// tokens: tokens +// }) +// ), +// "action context encoded from WithdrawActionContext" +// ); + +// // TODO: Check the contents of the EIP712 data +// assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); +// assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); +// assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); +// } + +// function testMorphoClaimRewardsPayWithReward() public { +// QuarkBuilder builder = new QuarkBuilder(); +// PaymentInfo.PaymentMaxCost[] memory maxCosts = new PaymentInfo.PaymentMaxCost[](1); +// maxCosts[0] = PaymentInfo.PaymentMaxCost({chainId: 1, amount: 1e6}); +// QuarkBuilder.BuilderResult memory result = builder.morphoClaimRewards( +// morphoClaimRewardsIntent_( +// 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureRewards, fixtureProofs +// ), +// chainAccountsList_(0), +// paymentUsdc_(maxCosts) +// ); + +// assertEq(result.paymentCurrency, "usdc", "usdc currency"); + +// // Check the quark operations +// assertEq(result.quarkOperations.length, 1, "one operation"); +// assertEq( +// result.quarkOperations[0].scriptAddress, +// paycallUsdc_(1), +// "script address is correct given the code jar address on mainnet" +// ); +// assertEq( +// result.quarkOperations[0].scriptCalldata, +// abi.encodeWithSelector( +// Paycall.run.selector, +// CodeJarHelper.getCodeAddress(type(MorphoRewardsActions).creationCode), +// abi.encodeCall( +// MorphoRewardsActions.claimAll, +// (fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs) +// ), +// 1e6 +// ), +// "calldata is Paycall.run(MorphoRewardsActions.claimAll(fixtureDistributors, fixtureAccounts, fixtureRewards, fixtureClaimables, fixtureProofs));" +// ); +// assertEq( +// result.quarkOperations[0].scriptSources[1], +// abi.encodePacked(type(Paycall).creationCode, abi.encode(ETH_USD_PRICE_FEED_1, USDC_1)) +// ); +// assertEq( +// result.quarkOperations[0].expiry, BLOCK_TIMESTAMP + 7 days, "expiry is current blockTimestamp + 7 days" +// ); +// assertEq(result.quarkOperations[0].nonce, ALICE_DEFAULT_SECRET, "unexpected nonce"); +// assertEq(result.quarkOperations[0].isReplayable, false, "isReplayable is false"); + +// // check the actions +// assertEq(result.actions.length, 1, "one action"); +// assertEq(result.actions[0].chainId, 1, "operation is on chainId 1"); +// assertEq(result.actions[0].quarkAccount, address(0xa11ce), "0xa11ce sends the funds"); +// assertEq(result.actions[0].actionType, "MORPHO_CLAIM_REWARDS", "action type is 'MORPHO_CLAIM_REWARDS'"); +// assertEq(result.actions[0].paymentMethod, "PAY_CALL", "payment method is 'PAY_CALL'"); +// assertEq(result.actions[0].paymentToken, USDC_1, "payment token is USDC"); +// assertEq(result.actions[0].paymentMaxCost, 1e6, "payment max is set to .1e6 in this test case"); +// assertEq(result.actions[0].nonceSecret, ALICE_DEFAULT_SECRET, "unexpected nonce secret"); +// assertEq(result.actions[0].totalPlays, 1, "total plays is 1"); + +// string[] memory assetSymbols = new string[](2); +// assetSymbols[0] = "USDC"; +// assetSymbols[1] = "WETH"; +// uint256[] memory prices = new uint256[](2); +// prices[0] = USDC_PRICE; +// prices[1] = WETH_PRICE; +// address[] memory tokens = new address[](2); +// tokens[0] = USDC_1; +// tokens[1] = WETH_1; +// assertEq( +// result.actions[0].actionContext, +// abi.encode( +// Actions.MorphoClaimRewardsActionContext({ +// amounts: fixtureClaimables, +// assetSymbols: assetSymbols, +// chainId: 1, +// prices: prices, +// tokens: tokens +// }) +// ), +// "action context encoded from WithdrawActionContext" +// ); + +// // TODO: Check the contents of the EIP712 data +// assertNotEq(result.eip712Data.digest, hex"", "non-empty digest"); +// assertNotEq(result.eip712Data.domainSeparator, hex"", "non-empty domain separator"); +// assertNotEq(result.eip712Data.hashStruct, hex"", "non-empty hashStruct"); +// } + +// function testMorphoClaimRewardsWithNotEnoughRewardToCoverCost() public { +// QuarkBuilder builder = new QuarkBuilder(); +// PaymentInfo.PaymentMaxCost[] memory maxCosts = new PaymentInfo.PaymentMaxCost[](2); +// maxCosts[0] = PaymentInfo.PaymentMaxCost({chainId: 1, amount: 5e6}); +// maxCosts[1] = PaymentInfo.PaymentMaxCost({chainId: 8453, amount: 5e6}); +// vm.expectRevert(abi.encodeWithSelector(Actions.NotEnoughFundsToBridge.selector, "usdc", 3e6, 3e6)); +// builder.morphoClaimRewards( +// morphoClaimRewardsIntent_( +// 1, fixtureAccounts, fixtureClaimablesLessUSDC, fixtureDistributors, fixtureRewards, fixtureProofs +// ), +// chainAccountsList_(2e6), +// paymentUsdc_(maxCosts) +// ); +// } + +// function testMorphoClaimRewardsInvalid() public { +// QuarkBuilder builder = new QuarkBuilder(); +// vm.expectRevert(QuarkBuilder.InvalidInput.selector); +// builder.morphoClaimRewards( +// morphoClaimRewardsIntent_( +// 1, fixtureAccounts, fixtureClaimables, fixtureDistributors, fixtureInvalidRewards, fixtureProofs +// ), +// chainAccountsList_(2e6), +// paymentUsd_() +// ); +// } +// }