Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into hans/morpho-integrati…
Browse files Browse the repository at this point in the history
…on-4-reward
  • Loading branch information
cwang25 committed Sep 13, 2024
2 parents 255d19f + 528477d commit 1fceff8
Show file tree
Hide file tree
Showing 56 changed files with 2,744 additions and 742 deletions.
90 changes: 60 additions & 30 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
ApproveAndSwapTest:testSwap() (gas: 285500)
ApproveAndSwapTest:testSwapFailsIfWeExpectedTooMuch() (gas: 364666)
ApproveAndSwapTest:testSwapFailsWithNoApproval() (gas: 122231)
ApproveAndSwapTest:testSwap() (gas: 285492)
ApproveAndSwapTest:testSwapFailsIfWeExpectedTooMuch() (gas: 364765)
ApproveAndSwapTest:testSwapFailsWithNoApproval() (gas: 122254)
CCTPBridge:testBridgeToBase() (gas: 146874)
CometClaimRewardsTest:testClaimComp() (gas: 131265)
CometRepayAndWithdrawMultipleAssetsTest:testInvalidInput() (gas: 68171)
CometRepayAndWithdrawMultipleAssetsTest:testInvalidInput() (gas: 68180)
CometRepayAndWithdrawMultipleAssetsTest:testRepayAndWithdrawMultipleAssets() (gas: 153860)
CometSupplyMultipleAssetsAndBorrowTest:testInvalidInput() (gas: 68114)
CometSupplyMultipleAssetsAndBorrowTest:testInvalidInput() (gas: 68123)
CometSupplyMultipleAssetsAndBorrowTest:testSupplyMultipleAssetsAndBorrow() (gas: 295260)
ConditionalMulticallTest:testConditionalRunEmptyInputIsValid() (gas: 51386)
ConditionalMulticallTest:testConditionalRunInvalidInput() (gas: 68840)
Expand All @@ -19,8 +19,15 @@ EthcallTest:testEthcallShouldReturnCallResult() (gas: 52545)
EthcallTest:testEthcallSupplyUSDCToComet() (gas: 171965)
EthcallTest:testEthcallWithdrawUSDCFromComet() (gas: 296115)
GetDripTest:testDrip() (gas: 120591)
MorphoActionsTest:testRepayAndWithdrawCollateral() (gas: 130575)
MorphoActionsTest:testRepayMaxAndWithdrawCollateral() (gas: 118293)
MorphoActionsTest:testSupplyCollateralAndBorrow() (gas: 250037)
MorphoRewardsActionsTest:testClaim() (gas: 112124)
MorphoRewardsActionsTest:testClaimAll() (gas: 186069)
MorphoVaultActionsTest:testDeposit() (gas: 762206)
MorphoVaultActionsTest:testWithdraw() (gas: 570979)
MulticallTest:testCallcodeToMulticallSucceedsWhenUninitialized() (gas: 83316)
MulticallTest:testCreateSubWalletAndExecute() (gas: 608534)
MulticallTest:testCreateSubWalletAndExecute() (gas: 619226)
MulticallTest:testEmptyInputIsValid() (gas: 50892)
MulticallTest:testExecutorCanMulticallAcrossSubwallets() (gas: 303309)
MulticallTest:testInvalidInput() (gas: 68358)
Expand All @@ -29,23 +36,42 @@ MulticallTest:testMulticallError() (gas: 309667)
MulticallTest:testMulticallShouldReturnCallResults() (gas: 86409)
MulticallTest:testRevertsForInvalidCallContext() (gas: 11574)
MulticallTest:testSupplyWETHWithdrawUSDCOnComet() (gas: 259628)
PaycallTest:testInitializeProperlyFromConstructor() (gas: 6425)
PaycallTest:testPaycallForPayWithUSDT() (gas: 121389)
PaycallTest:testPaycallForPayWithWBTC() (gas: 115864)
PaycallTest:testReturnCallResult() (gas: 89981)
PaycallTest:testRevertsForInvalidCallContext() (gas: 16521)
PaycallTest:testRevertsWhenCostIsMoreThanMaxPaymentCost() (gas: 119258)
PaycallTest:testSimpleCounterAndPayWithUSDC() (gas: 143929)
PaycallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 139569)
PaycallTest:testSupplyWETHWithdrawUSDCOnCometAndPayWithUSDC() (gas: 300142)
ReplayableTransactionsTest:testCancelRecurringPurchase() (gas: 267995)
ReplayableTransactionsTest:testRecurringPurchaseHappyPath() (gas: 210285)
ReplayableTransactionsTest:testRecurringPurchaseMultiplePurchases() (gas: 365698)
ReplayableTransactionsTest:testRecurringPurchaseWithDifferentCalldata() (gas: 590229)
ReplayableTransactionsTest:testRevertsForExpiredQuarkOperation() (gas: 11893)
ReplayableTransactionsTest:testRevertsForExpiredUniswapParams() (gas: 118215)
ReplayableTransactionsTest:testRevertsForPurchaseBeforeNextPurchasePeriod() (gas: 283863)
ReplayableTransactionsTest:testRevertsForPurchasingOverTheLimit() (gas: 284305)
PaycallTest:testInitializeProperlyFromConstructor() (gas: 6430)
PaycallTest:testPaycallForPayWithUSDT() (gas: 125307)
PaycallTest:testPaycallForPayWithWBTC() (gas: 119793)
PaycallTest:testPaycallRevertsWhenCallReverts() (gas: 72350)
PaycallTest:testReturnCallResult() (gas: 92209)
PaycallTest:testRevertWhenCostIsMoreThanMaxPaymentCost() (gas: 119383)
PaycallTest:testRevertsForInvalidCallContext() (gas: 16562)
PaycallTest:testSimpleCounterAndPayWithUSDC() (gas: 148804)
PaycallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 144444)
PaycallTest:testSupplyWETHWithdrawUSDCOnCometAndPayWithUSDC() (gas: 305017)
PaycallWrapperTest:testSimpleTransferAndWrapForPaycall() (gas: 1342091)
QuotecallTest:testInitializeProperlyFromConstructor() (gas: 7036)
QuotecallTest:testQuotecallForPayWithUSDT() (gas: 125408)
QuotecallTest:testQuotecallForPayWithWBTC() (gas: 119962)
QuotecallTest:testQuotecallRevertsWhenCallReverts() (gas: 108579)
QuotecallTest:testReturnCallResult() (gas: 112319)
QuotecallTest:testRevertsForInvalidCallContext() (gas: 16537)
QuotecallTest:testRevertsWhenQuoteTooHigh() (gas: 155229)
QuotecallTest:testRevertsWhenQuoteTooLow() (gas: 155071)
QuotecallTest:testSimpleCounterAndPayWithUSDC() (gas: 148952)
QuotecallTest:testSimpleTransferTokenAndPayWithUSDC() (gas: 144596)
QuotecallWrapperTest:testSimpleTransferAndWrapForQuotecall() (gas: 1407584)
RecurringSwapTest:testCancelRecurringSwap() (gas: 293567)
RecurringSwapTest:testRecurringSwapCanSwapMultipleTimes() (gas: 425136)
RecurringSwapTest:testRecurringSwapExactInAlternateSwap() (gas: 229791)
RecurringSwapTest:testRecurringSwapExactInSwap() (gas: 237508)
RecurringSwapTest:testRecurringSwapExactOutAlternateSwap() (gas: 233113)
RecurringSwapTest:testRecurringSwapExactOutSwap() (gas: 239457)
RecurringSwapTest:testRecurringSwapWithDifferentCalldata() (gas: 724141)
RecurringSwapTest:testRecurringSwapWithMultiplePriceFeeds() (gas: 254663)
RecurringSwapTest:testRevertsForExpiredQuarkOperation() (gas: 12817)
RecurringSwapTest:testRevertsForInvalidInput() (gas: 143492)
RecurringSwapTest:testRevertsForSwapBeforeNextSwapWindow() (gas: 307929)
RecurringSwapTest:testRevertsForSwapBeforeStartTime() (gas: 9223372036854754743)
RecurringSwapTest:testRevertsWhenSlippageParamsConfiguredWrong() (gas: 260596)
RecurringSwapTest:testRevertsWhenSlippageTooHigh() (gas: 260796)
SupplyActionsTest:testInvalidInput() (gas: 67616)
SupplyActionsTest:testRepayBorrow() (gas: 89628)
SupplyActionsTest:testSupply() (gas: 131927)
Expand All @@ -57,7 +83,7 @@ TransferActionsTest:testRevertsForTransferReentrancyAttackWithReentrancyGuard()
TransferActionsTest:testRevertsForTransferReentrancyAttackWithoutCallbackEnabled() (gas: 99351)
TransferActionsTest:testRevertsForTransferReentrantAttackWithStolenSignature() (gas: 110234)
TransferActionsTest:testTransferERC20TokenToEOA() (gas: 70280)
TransferActionsTest:testTransferERC20TokenToQuarkWallet() (gas: 71600)
TransferActionsTest:testTransferERC20TokenToQuarkWallet() (gas: 71604)
TransferActionsTest:testTransferERC777SuccessWithEvilReceiverWithoutAttackAttempt() (gas: 106630)
TransferActionsTest:testTransferERC777TokenReentrancyAttackSuccessWithCallbackEnabled() (gas: 144402)
TransferActionsTest:testTransferNativeTokenToEOA() (gas: 79394)
Expand All @@ -73,14 +99,18 @@ UniswapFlashSwapExactOutTest:testInvalidCallerFlashSwap() (gas: 70209)
UniswapFlashSwapExactOutTest:testNotEnoughToPayFlashSwap() (gas: 293339)
UniswapFlashSwapExactOutTest:testRevertsIfCalledDirectly() (gas: 10724)
UniswapFlashSwapExactOutTest:testUniswapFlashSwapExactOutLeverageComet() (gas: 353254)
UniswapSwapActionsTest:testApprovalRefund() (gas: 163571)
UniswapSwapActionsTest:testBuyAssetOneStop() (gas: 251129)
UniswapSwapActionsTest:testBuyAssetTwoStops() (gas: 357689)
UniswapSwapActionsTest:testSellAssetOneStop() (gas: 248467)
UniswapSwapActionsTest:testSellAssetTwoStops() (gas: 361614)
UniswapSwapActionsTest:testApprovalRefund() (gas: 163410)
UniswapSwapActionsTest:testBuyAssetOneStop() (gas: 250917)
UniswapSwapActionsTest:testBuyAssetTwoStops() (gas: 357575)
UniswapSwapActionsTest:testSellAssetOneStop() (gas: 248255)
UniswapSwapActionsTest:testSellAssetTwoStops() (gas: 361500)
WithdrawActionsTest:testBorrow() (gas: 153735)
WithdrawActionsTest:testInvalidInput() (gas: 67488)
WithdrawActionsTest:testWithdraw() (gas: 83594)
WithdrawActionsTest:testWithdrawFrom() (gas: 83108)
WithdrawActionsTest:testWithdrawMultipleAssets() (gas: 159195)
WithdrawActionsTest:testWithdrawTo() (gas: 83573)
WithdrawActionsTest:testWithdrawTo() (gas: 83573)
WrapperScriptsTest:testUnwrapWETH() (gas: 57751)
WrapperScriptsTest:testUnwrapWstETH() (gas: 100520)
WrapperScriptsTest:testWrapETH() (gas: 76834)
WrapperScriptsTest:testWrapStETH() (gas: 124640)
7 changes: 5 additions & 2 deletions .github/workflows/gas-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,14 @@ jobs:
run: |
set -euo pipefail
# grep -E '^test' -- skip over test results, just get diffs
forge snapshot --diff \
FOUNDRY_PROFILE=ir forge snapshot --no-match-path test/builder/**/*.t.sol --diff \
| grep -E '^test' \
| tee .gas-snapshot.new
env:
NODE_PROVIDER_BYPASS_KEY: ${{ secrets.NODE_PROVIDER_BYPASS_KEY }}
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
BASE_MAINNET_RPC_URL: ${{ secrets.BASE_MAINNET_RPC_URL }}
SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_RPC_URL }}
BASE_SEPOLIA_RPC_URL: ${{ secrets.BASE_SEPOLIA_RPC_URL }}

- name: Check diff tolerance
run: |
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,7 @@ jobs:
forge test -vvv
id: test
env:
NODE_PROVIDER_BYPASS_KEY: ${{ secrets.NODE_PROVIDER_BYPASS_KEY }}
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
BASE_MAINNET_RPC_URL: ${{ secrets.BASE_MAINNET_RPC_URL }}
SEPOLIA_RPC_URL: ${{ secrets.SEPOLIA_RPC_URL }}
BASE_SEPOLIA_RPC_URL: ${{ secrets.BASE_SEPOLIA_RPC_URL }}
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "lib/quark"]
path = lib/quark
url = [email protected]:compound-finance/quark
[submodule "lib/swap-router-contracts"]
path = lib/swap-router-contracts
url = https://github.com/Uniswap/swap-router-contracts
21 changes: 8 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ Quark is an Ethereum smart contract wallet system, designed to run custom code

Replayable scripts are Quark scripts that can re-executed multiple times using the same signature of a _Quark operation_. More specifically, replayable scripts explicitly clear the nonce used by the transaction (can be done via the `allowReplay` helper function in [`QuarkScript.sol`](./lib/quark/src/quark-core/src/QuarkScript.sol)) to allow for the same nonce to be re-used with the same script address.

An example use-case for replayable scripts is recurring purchases. If a user wanted to buy X WETH using 1,000 USDC every Wednesday until 10,000 USDC is spent, they can achieve this by signing a single _Quark operation_ of a replayable script ([example](./test/lib/RecurringPurchase.sol)). A submitter can then submit this same signed _Quark operation_ every Wednesday to execute the recurring purchase. The replayable script should have checks to ensure conditions are met before purchasing the WETH.
An example use-case for replayable scripts is recurring purchases. If a user wanted to buy X WETH using 1,000 USDC every Wednesday until 10,000 USDC is spent, they can achieve this by signing a single _Quark operation_ of a replayable script ([example](./src/RecurringSwap.sol)). A submitter can then submit this same signed _Quark operation_ every Wednesday to execute the recurring purchase. The replayable script should have checks to ensure conditions are met before purchasing the WETH.

#### Same script address, but different calldata

For replayable transactions where the nonce is cleared, _Quark State Manager_ requires future transactions using that nonce to use the same script. This is to ensure that the same nonce is not accidentally used by two different scripts. However, it does not require the `calldata` passed to that script to be the same. This means that a cleared nonce can be executed with the same script but different calldata.

Allowing the calldata to change greatly increases the flexibility of replayable scripts. One can think of a replayable script like a sub-module of a wallet that supports different functionality. In the [example script](./test/lib/RecurringPurchase.sol) for recurring purchases, there is a separate `cancel` function that the user can sign to cancel the nonce, and therefore, cancel all the recurring purchases that use this nonce. The user can also also sign multiple `purchase` calls, each with different purchase configurations. This means that multiple variations of recurring purchases can exist on the same nonce and can all be cancelled together.
Allowing the calldata to change greatly increases the flexibility of replayable scripts. One can think of a replayable script like a sub-module of a wallet that supports different functionality. In the [example script](./src/RecurringSwap.sol) for recurring purchases, there is a separate `cancel` function that the user can sign to cancel the nonce, and therefore, cancel all the recurring purchases that use this nonce. The user can also also sign multiple `purchase` calls, each with different purchase configurations. This means that multiple variations of recurring purchases can exist on the same nonce and can all be cancelled together.

One danger of flexible `calldata` in replayable scripts is that previously signed `calldata` can always be re-executed. The Quark system does not disallow previously used calldata when a new calldata is executed. This means that scripts may need to implement their own method of invalidating previously-used `calldata`.

Expand All @@ -32,23 +32,18 @@ Callbacks need to be explicitly turned on by Quark scripts. Specifically, this i

[Quark Builder Helper](./src/builder/QuarkBuilderHelper.sol) is a contract with functions outside of constructing _Quark operations_ that might still be helpful for those using the QuarkBuilder. For example, there is a helper function to determine the bridgeability of assets on different chains.

## Fork tests and NODE_PROVIDER_BYPASS_KEY
## Fork tests and MAINNET_RPC_URL

Some tests require forking mainnet, e.g. to exercise use-cases like
supplying and borrowing in a comet market.

For a "fork url" we use our rate-limited node provider endpoint at
`https://node-provider.compound.finance/ethereum-mainnet`. Setting up a
fork quickly exceeds the rate limits, so we use a bypass key to allow fork
tests to exceed the rate limits.
The "fork url" is specified using the environment variable `MAINNET_RPC_URL`.
It can be any node provider for Ethereum mainnet, such as Infura or Alchemy.

A bypass key for Quark development can be found in 1Password as a
credential named "Quark Dev node-provider Bypass Key". The key can then be
set during tests via the environment variable `NODE_PROVIDER_BYPASS_KEY`,
like so:
The environment variable can be set when running tests, like so:

```
$ NODE_PROVIDER_BYPASS_KEY=... forge test
$ MAINNET_RPC_URL=... forge test
```

## Updating gas snapshots
Expand All @@ -61,7 +56,7 @@ You can accept the diff and update the baseline if the increased gas usage
is intentional. Just run the following command:

```sh
$ NODE_PROVIDER_BYPASS_KEY=... ./script/update-snapshot.sh
$ MAINNET_RPC_URL=... ./script/update-snapshot.sh
```

Then commit the updated snapshot file:
Expand Down
3 changes: 3 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ via_ir = true
optimizer = true
optimizer_runs = 100000000

[fmt]
ignore = ["src/vendor/**/*"]

bytecode_hash = "none"
cbor_metadata = false
1 change: 1 addition & 0 deletions lib/swap-router-contracts
Submodule swap-router-contracts added at c696aa
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ openzeppelin-contracts/=lib/openzeppelin-contracts/
openzeppelin/=lib/openzeppelin-contracts/contracts/
v3-core/=lib/v3-core/
v3-periphery/=lib/v3-periphery/contracts/
swap-router-contracts/=lib/swap-router-contracts/contracts/
codejar/=lib/quark/src/codejar/
quark-core/=lib/quark/src/quark-core/
quark-factory/=lib/quark/src/quark-factory/
Expand Down
7 changes: 7 additions & 0 deletions script/list-errors.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

# This script searches for custom Solidity error declarations in the ./lib and ./src directories,
# excluding any files with 'test' or 'mock' in their names.

# Run the grep command to find error declarations
grep -r "error [A-Z]" ./lib ./src --include \*.sol --exclude '*test*' --exclude '*mock*'
2 changes: 1 addition & 1 deletion script/update-snapshot.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash

FOUNDRY_PROFILE=ir forge snapshot
FOUNDRY_PROFILE=ir forge snapshot --no-match-path test/builder/**/*.t.sol
14 changes: 5 additions & 9 deletions src/DeFiScripts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity 0.8.23;

import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol";
import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol";
import {ISwapRouter} from "v3-periphery/interfaces/ISwapRouter.sol";
import {ISwapRouter02, IV3SwapRouter} from "src/vendor/uniswap-swap-router-contracts/ISwapRouter02.sol";

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

Expand Down Expand Up @@ -136,7 +136,6 @@ contract UniswapSwapActions {
uint256 amount;
// Minimum amount of target token to receive (revert if return amount is less than this)
uint256 amountOutMinimum;
uint256 deadline;
// Path of the swap
bytes path;
}
Expand All @@ -148,7 +147,6 @@ contract UniswapSwapActions {
uint256 amount;
// Maximum amount of input token to spend (revert if input amount is greater than this)
uint256 amountInMaximum;
uint256 deadline;
// Path of the swap
bytes path;
}
Expand All @@ -159,11 +157,10 @@ contract UniswapSwapActions {
*/
function swapAssetExactIn(SwapParamsExactIn calldata params) external {
IERC20(params.tokenFrom).forceApprove(params.uniswapRouter, params.amount);
ISwapRouter(params.uniswapRouter).exactInput(
ISwapRouter.ExactInputParams({
ISwapRouter02(params.uniswapRouter).exactInput(
IV3SwapRouter.ExactInputParams({
path: params.path,
recipient: params.recipient,
deadline: params.deadline,
amountIn: params.amount,
amountOutMinimum: params.amountOutMinimum
})
Expand All @@ -176,11 +173,10 @@ contract UniswapSwapActions {
*/
function swapAssetExactOut(SwapParamsExactOut calldata params) external {
IERC20(params.tokenFrom).forceApprove(params.uniswapRouter, params.amountInMaximum);
uint256 amountIn = ISwapRouter(params.uniswapRouter).exactOutput(
ISwapRouter.ExactOutputParams({
uint256 amountIn = ISwapRouter02(params.uniswapRouter).exactOutput(
IV3SwapRouter.ExactOutputParams({
path: params.path,
recipient: params.recipient,
deadline: params.deadline,
amountOut: params.amount,
amountInMaximum: params.amountInMaximum
})
Expand Down
Loading

0 comments on commit 1fceff8

Please sign in to comment.