Skip to content

Commit

Permalink
Refactor QuarkBuilder and Bridge routing fix (#93)
Browse files Browse the repository at this point in the history
- Trim off repetitive codes from quark scripts into individual files for
ease of maintainability and readability.
- Adjusted some test assertion on revert that was different due to when
people copying codes in quark scripts people changed a bit here and
there causing some discrepancies on some check logics (such as check
sufficient payment token, check sufficient bridges...etc) Now with
shared base code, it will always goes through the exact same checks and
codes
- Bug fix to bridge routing logics. Old bridge logics will try to bridge
0 balances if multiple accounts appears in the list. Now it won't try to
bridge zero balance assets. (Found from after adding all alice/bob/fella
accounts across all chain-id.

---------

Co-authored-by: Hans Wang <hans@compound.finance>
Co-authored-by: Kevin Cheng <kevincheng96@hotmail.com>
  • Loading branch information
3 people authored Oct 28, 2024
1 parent 6d738f6 commit 4a9f282
Show file tree
Hide file tree
Showing 24 changed files with 2,397 additions and 2,710 deletions.
4 changes: 2 additions & 2 deletions src/builder/EIP712Helper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pragma solidity 0.8.27;
import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol";
import {QuarkWalletMetadata} from "quark-core/src/QuarkWallet.sol";

import {Actions} from "./Actions.sol";
import {Errors} from "./Errors.sol";
import {Actions} from "src/builder/actions/Actions.sol";
import {Errors} from "src/builder/Errors.sol";

library EIP712Helper {
/* ===== Constants ===== */
Expand Down
2 changes: 1 addition & 1 deletion src/builder/List.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity 0.8.27;

import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol";
import {Actions} from "./Actions.sol";
import {Actions} from "src/builder/actions/Actions.sol";

library List {
error IndexOutOfBound();
Expand Down
2,240 changes: 15 additions & 2,225 deletions src/builder/QuarkBuilder.sol

Large diffs are not rendered by default.

651 changes: 651 additions & 0 deletions src/builder/QuarkBuilderBase.sol

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions src/builder/QuarkOperationHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ pragma solidity ^0.8.27;

import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol";

import {Multicall} from "../Multicall.sol";

import {Actions} from "./Actions.sol";
import {CodeJarHelper} from "./CodeJarHelper.sol";
import {Errors} from "./Errors.sol";
import {PaycallWrapper} from "./PaycallWrapper.sol";
import {PaymentInfo} from "./PaymentInfo.sol";
import {QuotecallWrapper} from "./QuotecallWrapper.sol";
import {List} from "./List.sol";
import {HashMap} from "./HashMap.sol";
import {Multicall} from "src/Multicall.sol";

import {Actions} from "src/builder/actions/Actions.sol";
import {CodeJarHelper} from "src/builder/CodeJarHelper.sol";
import {Errors} from "src/builder/Errors.sol";
import {PaycallWrapper} from "src/builder/PaycallWrapper.sol";
import {PaymentInfo} from "src/builder/PaymentInfo.sol";
import {QuotecallWrapper} from "src/builder/QuotecallWrapper.sol";
import {List} from "src/builder/List.sol";
import {HashMap} from "src/builder/HashMap.sol";

// Helper library to for transforming Quark Operations
library QuarkOperationHelper {
Expand Down
212 changes: 107 additions & 105 deletions src/builder/Actions.sol → src/builder/actions/Actions.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.27;

import {Accounts} from "./Accounts.sol";
import {BridgeRoutes, CCTP} from "./BridgeRoutes.sol";
import {CodeJarHelper} from "./CodeJarHelper.sol";
import {Accounts} from "src/builder/Accounts.sol";
import {BridgeRoutes, CCTP} from "src/builder/BridgeRoutes.sol";
import {CodeJarHelper} from "src/builder/CodeJarHelper.sol";
import {Math} from "src/lib/Math.sol";
import {PriceFeeds} from "./PriceFeeds.sol";
import {Strings} from "./Strings.sol";
import {UniswapRouter} from "./UniswapRouter.sol";
import {PriceFeeds} from "src/builder/PriceFeeds.sol";
import {Strings} from "src/builder/Strings.sol";
import {UniswapRouter} from "src/builder/UniswapRouter.sol";

import {
ApproveAndSwap,
Expand All @@ -16,17 +16,17 @@ import {
CometSupplyMultipleAssetsAndBorrow,
CometWithdrawActions,
TransferActions
} from "../DeFiScripts.sol";
} from "src/DeFiScripts.sol";
import {Math} from "src/lib/Math.sol";
import {MorphoActions, MorphoRewardsActions, MorphoVaultActions} from "../MorphoScripts.sol";
import {RecurringSwap} from "../RecurringSwap.sol";
import {WrapperActions} from "../WrapperScripts.sol";
import {MorphoActions, MorphoRewardsActions, MorphoVaultActions} from "src/MorphoScripts.sol";
import {RecurringSwap} from "src/RecurringSwap.sol";
import {WrapperActions} from "src/WrapperScripts.sol";
import {IQuarkWallet} from "quark-core/src/interfaces/IQuarkWallet.sol";
import {IMorpho, Position} from "../interfaces/IMorpho.sol";
import {PaymentInfo} from "./PaymentInfo.sol";
import {TokenWrapper} from "./TokenWrapper.sol";
import {MorphoInfo} from "./MorphoInfo.sol";
import {List} from "./List.sol";
import {IMorpho, Position} from "src/interfaces/IMorpho.sol";
import {PaymentInfo} from "src/builder/PaymentInfo.sol";
import {TokenWrapper} from "src/builder/TokenWrapper.sol";
import {MorphoInfo} from "src/builder/MorphoInfo.sol";
import {List} from "src/builder/List.sol";

library Actions {
/* ===== Constants ===== */
Expand Down Expand Up @@ -564,28 +564,30 @@ library Actions {
}
}

amountLeftToBridge -= amountToBridge;

(IQuarkWallet.QuarkOperation memory operation, Actions.Action memory action) = bridgeAsset(
BridgeAsset({
chainAccountsList: chainAccountsList,
assetSymbol: bridgeInfo.assetSymbol,
amount: amountToBridge,
// where it comes from
srcChainId: srcChainAccounts.chainId,
sender: srcAccountBalances[j].account,
// where it goes
destinationChainId: bridgeInfo.dstChainId,
recipient: bridgeInfo.recipient,
blockTimestamp: bridgeInfo.blockTimestamp
}),
payment,
bridgeInfo.useQuotecall
);

List.addAction(actions, action);
List.addQuarkOperation(quarkOperations, operation);
bridgeActionCount++;
if (amountToBridge > 0) {
amountLeftToBridge -= amountToBridge;

(IQuarkWallet.QuarkOperation memory operation, Actions.Action memory action) = bridgeAsset(
BridgeAsset({
chainAccountsList: chainAccountsList,
assetSymbol: bridgeInfo.assetSymbol,
amount: amountToBridge,
// where it comes from
srcChainId: srcChainAccounts.chainId,
sender: srcAccountBalances[j].account,
// where it goes
destinationChainId: bridgeInfo.dstChainId,
recipient: bridgeInfo.recipient,
blockTimestamp: bridgeInfo.blockTimestamp
}),
payment,
bridgeInfo.useQuotecall
);

List.addAction(actions, action);
List.addQuarkOperation(quarkOperations, operation);
bridgeActionCount++;
}
}
}

Expand Down Expand Up @@ -1283,74 +1285,74 @@ library Actions {
}

// 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 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,
Expand Down
Loading

0 comments on commit 4a9f282

Please sign in to comment.