From 9d8f07d571298b448852bf63997b9b5278725f3e Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Mon, 1 Apr 2024 17:02:26 -0700 Subject: [PATCH] Remove Legend scripts (#193) Legend scripts are now moved into their own separate repo. --- remappings-relative.txt | 1 - remappings.txt | 1 - script/DeployLegendScripts.s.sol | 87 ---- script/deploy-legend-scripts.sh | 31 -- src/legend-scripts/foundry.toml | 9 - src/legend-scripts/remappings.txt | 1 - src/legend-scripts/src/GetDrip.sol | 19 - src/legend-scripts/src/LegendErrors.sol | 14 - src/legend-scripts/src/interfaces/IComet.sol | 49 -- .../src/interfaces/ICometRewards.sol | 6 - src/legend-scripts/src/interfaces/IWETH.sol | 8 - test/legend-scripts/ApproveAndSwap.t.sol | 139 ------ test/legend-scripts/CometClaimRewards.t.sol | 83 ---- .../CometRepayAndWithdrawMultipleAssets.t.sol | 112 ----- test/legend-scripts/CometSupplyActions.t.sol | 208 -------- .../CometSupplyMultipleAssetsAndBorrow.t.sol | 107 ----- .../legend-scripts/CometWithdrawActions.t.sol | 245 ---------- test/legend-scripts/GetDrip.t.sol | 55 --- test/legend-scripts/TransferActions.t.sol | 454 ------------------ test/legend-scripts/UniswapSwapActions.t.sol | 326 ------------- .../lib/DeFiScripts.sol | 149 +----- test/lib/EvilReceiver.sol | 2 +- test/lib/Transfer.sol | 35 ++ test/quark-core-scripts/Multicall.t.sol | 8 +- test/quark-core-scripts/Paycall.t.sol | 8 +- 25 files changed, 51 insertions(+), 2106 deletions(-) delete mode 100644 script/DeployLegendScripts.s.sol delete mode 100755 script/deploy-legend-scripts.sh delete mode 100644 src/legend-scripts/foundry.toml delete mode 120000 src/legend-scripts/remappings.txt delete mode 100644 src/legend-scripts/src/GetDrip.sol delete mode 100644 src/legend-scripts/src/LegendErrors.sol delete mode 100644 src/legend-scripts/src/interfaces/IComet.sol delete mode 100644 src/legend-scripts/src/interfaces/ICometRewards.sol delete mode 100644 src/legend-scripts/src/interfaces/IWETH.sol delete mode 100644 test/legend-scripts/ApproveAndSwap.t.sol delete mode 100644 test/legend-scripts/CometClaimRewards.t.sol delete mode 100644 test/legend-scripts/CometRepayAndWithdrawMultipleAssets.t.sol delete mode 100644 test/legend-scripts/CometSupplyActions.t.sol delete mode 100644 test/legend-scripts/CometSupplyMultipleAssetsAndBorrow.t.sol delete mode 100644 test/legend-scripts/CometWithdrawActions.t.sol delete mode 100644 test/legend-scripts/GetDrip.t.sol delete mode 100644 test/legend-scripts/TransferActions.t.sol delete mode 100644 test/legend-scripts/UniswapSwapActions.t.sol rename src/legend-scripts/src/LegendScript.sol => test/lib/DeFiScripts.sol (57%) create mode 100644 test/lib/Transfer.sol diff --git a/remappings-relative.txt b/remappings-relative.txt index 12a03a60..13722af3 100644 --- a/remappings-relative.txt +++ b/remappings-relative.txt @@ -4,7 +4,6 @@ quark-core=../quark-core/ quark-factory=../quark-factory/ quark-proxy=../quark-proxy/ quark-core-scripts=../quark-core-scripts/ -legend-scripts=../legend-scripts/ @uniswap/=../../lib/ ds-test/=../../lib/forge-std/lib/ds-test/src/ erc4626-tests/=../../lib/openzeppelin-contracts/lib/erc4626-tests/ diff --git a/remappings.txt b/remappings.txt index 17fad3c3..075c671e 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,5 @@ test=test/ codejar=src/codejar/ -legend-scripts=src/legend-scripts/ quark-core=src/quark-core/ quark-core-scripts=src/quark-core-scripts/ quark-factory=src/quark-factory/ diff --git a/script/DeployLegendScripts.s.sol b/script/DeployLegendScripts.s.sol deleted file mode 100644 index 262e4464..00000000 --- a/script/DeployLegendScripts.s.sol +++ /dev/null @@ -1,87 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import { - CometSupplyActions, - CometWithdrawActions, - UniswapSwapActions, - TransferActions, - CometClaimRewards, - CometSupplyMultipleAssetsAndBorrow, - CometRepayAndWithdrawMultipleAssets -} from "legend-scripts/src/LegendScript.sol"; - -// Deploy with: -// $ set -a && source .env && ./script/deploy.sh --broadcast - -// Required ENV vars: -// RPC_URL -// DEPLOYER_PK -// QUARK_WALLET_FACTORY_ADDRESS - -// Optional ENV vars: -// ETHERSCAN_KEY - -contract DeployLegendScripts is Script { - CodeJar codeJar; - CometSupplyActions cometSupplyActions; - CometWithdrawActions cometWithdrawActions; - UniswapSwapActions uniswapSwapActions; - TransferActions transferActions; - CometClaimRewards cometClaimRewards; - CometSupplyMultipleAssetsAndBorrow cometSupplyMultipleAssetsAndBorrow; - CometRepayAndWithdrawMultipleAssets cometRepayAndWithdrawMultipleAssets; - - function run() public { - address deployer = vm.addr(vm.envUint("DEPLOYER_PK")); - codeJar = CodeJar(vm.envAddress("CODE_JAR")); - console.log("Code Jar Address: ", address(codeJar)); - - vm.startBroadcast(deployer); - - console.log("============================================================="); - console.log("Deploying Terminal Scripts"); - - cometSupplyActions = - CometSupplyActions(codeJar.saveCode(abi.encodePacked(type(CometSupplyActions).creationCode))); - console.log("CometSupplyActions Deployed:", address(cometSupplyActions)); - - cometWithdrawActions = - CometWithdrawActions(codeJar.saveCode(abi.encodePacked(type(CometWithdrawActions).creationCode))); - console.log("CometWithdrawActions Deployed:", address(cometWithdrawActions)); - - uniswapSwapActions = - UniswapSwapActions(codeJar.saveCode(abi.encodePacked(type(UniswapSwapActions).creationCode))); - console.log("UniswapSwapActions Deployed:", address(uniswapSwapActions)); - - transferActions = TransferActions(codeJar.saveCode(abi.encodePacked(type(TransferActions).creationCode))); - console.log("TransferActions Deployed:", address(transferActions)); - - cometClaimRewards = CometClaimRewards(codeJar.saveCode(abi.encodePacked(type(CometClaimRewards).creationCode))); - console.log("CometClaimRewards Deployed:", address(cometClaimRewards)); - - cometSupplyMultipleAssetsAndBorrow = CometSupplyMultipleAssetsAndBorrow( - codeJar.saveCode(abi.encodePacked(type(CometSupplyMultipleAssetsAndBorrow).creationCode)) - ); - console.log("CometSupplyMultipleAssetsAndBorrow Deployed:", address(cometSupplyMultipleAssetsAndBorrow)); - - cometRepayAndWithdrawMultipleAssets = CometRepayAndWithdrawMultipleAssets( - codeJar.saveCode(abi.encodePacked(type(CometRepayAndWithdrawMultipleAssets).creationCode)) - ); - console.log("CometRepayAndWithdrawMultipleAssets Deployed:", address(cometRepayAndWithdrawMultipleAssets)); - - console.log("============================================================="); - - vm.stopBroadcast(); - } -} diff --git a/script/deploy-legend-scripts.sh b/script/deploy-legend-scripts.sh deleted file mode 100755 index 040d8a91..00000000 --- a/script/deploy-legend-scripts.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash - -set -exo pipefail - -if [ -n "$RPC_URL" ]; then - rpc_args="--rpc-url $RPC_URL" -else - rpc_args="" -fi - -if [ -n "$DEPLOYER_PK" ]; then - wallet_args="--private-key $DEPLOYER_PK" -else - wallet_args="--unlocked" -fi - -if [ -n "$ETHERSCAN_KEY" ]; then - etherscan_args="--verify --etherscan-api-key $ETHERSCAN_KEY" -else - etherscan_args="" -fi - -forge script --via-ir \ - $rpc_args \ - $wallet_args \ - $etherscan_args \ - $@ \ - script/DeployLegendScripts.s.sol:DeployLegendScripts - -repo_root=$(git rev-parse --show-toplevel) -${repo_root}/script/prepare-release.sh diff --git a/src/legend-scripts/foundry.toml b/src/legend-scripts/foundry.toml deleted file mode 100644 index 25b41379..00000000 --- a/src/legend-scripts/foundry.toml +++ /dev/null @@ -1,9 +0,0 @@ -[profile.default] -solc = "0.8.23" -evm_version = "paris" - -libs = [ "../../lib" ] - -src = "src" -out = "out" -test = "../../test/legend-scripts/" diff --git a/src/legend-scripts/remappings.txt b/src/legend-scripts/remappings.txt deleted file mode 120000 index 9b7661ea..00000000 --- a/src/legend-scripts/remappings.txt +++ /dev/null @@ -1 +0,0 @@ -../../remappings-relative.txt \ No newline at end of file diff --git a/src/legend-scripts/src/GetDrip.sol b/src/legend-scripts/src/GetDrip.sol deleted file mode 100644 index 934ce184..00000000 --- a/src/legend-scripts/src/GetDrip.sol +++ /dev/null @@ -1,19 +0,0 @@ -pragma solidity 0.8.23; - -// To handle loading up new accounts on stage and dev -interface Fauceteer { - function drip(address token) external; - - error BalanceTooLow(); - error RequestedTooFrequently(); - error TransferFailed(); -} - -contract GetDrip { - /** - * @notice Drip tokens from goerli faucet - */ - function drip(address faucet, address token) external { - Fauceteer(faucet).drip(token); - } -} diff --git a/src/legend-scripts/src/LegendErrors.sol b/src/legend-scripts/src/LegendErrors.sol deleted file mode 100644 index ceb846f0..00000000 --- a/src/legend-scripts/src/LegendErrors.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -/** - * @title Legend errors library - * @notice Defines the custom errors that are returned by different Legend scripts - * @author Compound Labs, Inc. - */ -library LegendErrors { - error InvalidInput(); - error TransferFailed(bytes data); - error ApproveAndSwapFailed(bytes data); - error TooMuchSlippage(); -} diff --git a/src/legend-scripts/src/interfaces/IComet.sol b/src/legend-scripts/src/interfaces/IComet.sol deleted file mode 100644 index 49e9bf86..00000000 --- a/src/legend-scripts/src/interfaces/IComet.sol +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -interface IComet { - function getAssetInfo(uint8 i) external view returns (AssetInfo memory); - - function baseToken() external view returns (address); - - function supply(address asset, uint256 amount) external; - - function supplyTo(address dst, address asset, uint256 amount) external; - - function supplyFrom(address from, address dst, address asset, uint256 amount) external; - - function withdraw(address asset, uint256 amount) external; - - function withdrawTo(address to, address asset, uint256 amount) external; - - function withdrawFrom(address src, address to, address asset, uint256 amount) external; - - function balanceOf(address owner) external view returns (uint256); - - function getPrice(address priceFeed) external view returns (uint256); - - function baseTokenPriceFeed() external view returns (address); - - function borrowBalanceOf(address account) external view returns (uint256); - - function collateralBalanceOf(address account, address asset) external view returns (uint128); - - function getAssetInfoByAddress(address asset) external view returns (AssetInfo memory); - - function baseScale() external view returns (uint256); - - function numAssets() external view returns (uint8); - - function allow(address manager, bool isAllowed_) external; -} - -struct AssetInfo { - uint8 offset; - address asset; - address priceFeed; - uint64 scale; - uint64 borrowCollateralFactor; - uint64 liquidateCollateralFactor; - uint64 liquidationFactor; - uint128 supplyCap; -} diff --git a/src/legend-scripts/src/interfaces/ICometRewards.sol b/src/legend-scripts/src/interfaces/ICometRewards.sol deleted file mode 100644 index 68d770fe..00000000 --- a/src/legend-scripts/src/interfaces/ICometRewards.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -interface ICometRewards { - function claim(address comet, address src, bool shouldAccrue) external; -} diff --git a/src/legend-scripts/src/interfaces/IWETH.sol b/src/legend-scripts/src/interfaces/IWETH.sol deleted file mode 100644 index 53560bcd..00000000 --- a/src/legend-scripts/src/interfaces/IWETH.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -interface IWETH { - function deposit() external payable; - function transfer(address to, uint256 value) external returns (bool); - function withdraw(uint256) external; -} diff --git a/test/legend-scripts/ApproveAndSwap.t.sol b/test/legend-scripts/ApproveAndSwap.t.sol deleted file mode 100644 index c7f87dd9..00000000 --- a/test/legend-scripts/ApproveAndSwap.t.sol +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "legend-scripts/src/LegendScript.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {CodeJar} from "codejar/src/CodeJar.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; -import {YulHelper} from "test/lib/YulHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; - -/** - * Tests approve and execute against 0x - */ -contract ApproveAndSwapTest is Test { - QuarkWalletProxyFactory public factory; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant ZEROX_PROXY = 0xDef1C0ded9bec7F1a1670819833240f027b25EfF; - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - // Warp to the block where the quote is valid - 19121945 - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - // Tests a swap from USDC to WETH - function testSwap() public { - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/ApproveAndSwap.json"); - - deal(USDC, address(wallet), 1_000_000e6); - uint256 sellAmount = 1_000e6; - - // buyAmount from 0x API - uint256 expectedBuyAmount = 420691117291334294; - // Encoded data from 0x API - bytes memory data = - hex"415565b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c7a07d1090e00600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e6973776170563300000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c9d984d0e115b0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000023907c05035aa000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd000000000000000000000000100000000000000000000000000000000000001100000000000000000000000000000000e7124dbf3631046b0db2dc1ef9436b46"; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(ApproveAndSwap.run, (ZEROX_PROXY, USDC, sellAmount, WETH, expectedBuyAmount, data)), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.resumeGasMetering(); - - wallet.executeQuarkOperation(op, v, r, s); - - // The swap will always yield the same amount of WETH since we test at a specific block - assertEq(IERC20(WETH).balanceOf(address(wallet)), 420691117291334340); - } - - function testSwapFailsWithNoApproval() public { - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/ApproveAndSwap.json"); - - deal(USDC, address(wallet), 1_000_000e6); - - // By setting this to 0, no amount is approved - uint256 sellAmount = 0; - - // buyAmount from 0x API - uint256 expectedBuyAmount = 420691117291334294; - // Encoded data from 0x API - bytes memory data = - hex"415565b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c7a07d1090e00600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e6973776170563300000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c9d984d0e115b0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000023907c05035aa000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd000000000000000000000000100000000000000000000000000000000000001100000000000000000000000000000000e7124dbf3631046b0db2dc1ef9436b46"; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(ApproveAndSwap.run, (ZEROX_PROXY, USDC, sellAmount, WETH, expectedBuyAmount, data)), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.resumeGasMetering(); - - vm.expectRevert(); - wallet.executeQuarkOperation(op, v, r, s); - - // The swap will always yield the same amount of WETH since we test at a specific block - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0); - } - - function testSwapFailsIfWeExpectedTooMuch() public { - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/ApproveAndSwap.json"); - - deal(USDC, address(wallet), 1_000_000e6); - - // By setting this to 0, no amount is approved - uint256 sellAmount = 1_000e6; - - // buyAmount from 0x API - uint256 expectedBuyAmount = 420691117291334350; - // Encoded data from 0x API - bytes memory data = - hex"415565b0000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c7a07d1090e00600000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000000210000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000320000000000000000000000000000000000000000000000000000000000000032000000000000000000000000000000000000000000000000000000000000002e0000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000012556e6973776170563300000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000005c9d984d0e115b0000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000e592427a0aece92de3edee1f18e0157c0586156400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002ba0b86991c6218b36c1d19d4a2e9eb0ce3606eb480001f4c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000000000000000000000000000000023907c05035aa000000000000000000000000ad01c20d5886137e056775af56915de824c8fce5000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee0000000000000000000000000000000000000000000000000000000000000000869584cd000000000000000000000000100000000000000000000000000000000000001100000000000000000000000000000000e7124dbf3631046b0db2dc1ef9436b46"; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(ApproveAndSwap.run, (ZEROX_PROXY, USDC, sellAmount, WETH, expectedBuyAmount, data)), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.resumeGasMetering(); - - vm.expectRevert(); - wallet.executeQuarkOperation(op, v, r, s); - - // The swap will always yield the same amount of WETH since we test at a specific block - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0); - } -} diff --git a/test/legend-scripts/CometClaimRewards.t.sol b/test/legend-scripts/CometClaimRewards.t.sol deleted file mode 100644 index ee32c443..00000000 --- a/test/legend-scripts/CometClaimRewards.t.sol +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for claiming COMP rewards - */ -contract CometClaimRewardsTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant cometReward = 0x1B0e765F6224C21223AeA2af16c1C46E38885a40; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888; - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - function testClaimComp() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/CometClaimRewards.json"); - - deal(USDC, address(wallet), 1_000_000e6); - - vm.startPrank(address(wallet)); - IERC20(USDC).approve(comet, 1_000_000e6); - IComet(comet).supply(USDC, 1_000_000e6); - vm.stopPrank(); - - // Fastforward 180 days block to accrue COMP - vm.warp(block.timestamp + 180 days); - - address[] memory comets = new address[](1); - comets[0] = comet; - - address[] memory cometRewards = new address[](1); - cometRewards[0] = cometReward; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(CometClaimRewards.claim, (cometRewards, comets, address(wallet))), - ScriptType.ScriptSource - ); - (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/legend-scripts/CometRepayAndWithdrawMultipleAssets.t.sol b/test/legend-scripts/CometRepayAndWithdrawMultipleAssets.t.sol deleted file mode 100644 index 90f76dcc..00000000 --- a/test/legend-scripts/CometRepayAndWithdrawMultipleAssets.t.sol +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for repaying and withdrawing multiple assets from Comet - */ -contract CometRepayAndWithdrawMultipleAssetsTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA; - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - function testRepayAndWithdrawMultipleAssets() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/CometRepayAndWithdrawMultipleAssets.json"); - - deal(WETH, address(wallet), 10 ether); - deal(LINK, address(wallet), 10e18); - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IERC20(LINK).approve(comet, 10e18); - IComet(comet).supply(WETH, 10 ether); - IComet(comet).supply(LINK, 10e18); - IComet(comet).withdraw(USDC, 100e6); - vm.stopPrank(); - - address[] memory assets = new address[](2); - uint256[] memory amounts = new uint256[](2); - assets[0] = WETH; - assets[1] = LINK; - amounts[0] = 10 ether; - amounts[1] = 10e18; - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(CometRepayAndWithdrawMultipleAssets.run, (comet, assets, amounts, USDC, 100e6)), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 0); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 100e6); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 10e18); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 0); - } - - function testInvalidInput() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/CometRepayAndWithdrawMultipleAssets.json"); - - address[] memory assets = new address[](2); - uint256[] memory amounts = new uint256[](1); - assets[0] = WETH; - assets[1] = LINK; - amounts[0] = 10 ether; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeCall(CometRepayAndWithdrawMultipleAssets.run, (comet, assets, amounts, USDC, 100e6)), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.expectRevert(abi.encodeWithSelector(LegendErrors.InvalidInput.selector)); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - } -} diff --git a/test/legend-scripts/CometSupplyActions.t.sol b/test/legend-scripts/CometSupplyActions.t.sol deleted file mode 100644 index 99dcb445..00000000 --- a/test/legend-scripts/CometSupplyActions.t.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; - -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for supplying assets to Comet - */ -contract SupplyActionsTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA; - bytes legendScript = new YulHelper().getCode("LegendScript.sol/CometSupplyActions.json"); - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - function testSupply() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 10 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyActions.supply.selector, comet, WETH, 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - } - - function testSupplyTo() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet wallet2 = QuarkWallet(factory.create(alice, address(wallet), bytes32("2"))); - - deal(WETH, address(wallet), 10 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyActions.supplyTo.selector, comet, address(wallet2), WETH, 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet2), WETH), 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet2), WETH), 10 ether); - } - - function testSupplyFrom() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet wallet2 = QuarkWallet(factory.create(alice, address(wallet), bytes32("2"))); - - deal(WETH, address(wallet2), 10 ether); - vm.startPrank(address(wallet2)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).allow(address(wallet), true); - vm.stopPrank(); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - CometSupplyActions.supplyFrom.selector, comet, address(wallet2), address(wallet), WETH, 10 ether - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet2)), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet2)), 0 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - } - - function testRepayBorrow() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 10 ether); - - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - IComet(comet).withdraw(USDC, 1000e6); - vm.stopPrank(); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyActions.supply.selector, comet, USDC, 1000e6), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IComet(comet).borrowBalanceOf(address(wallet)), 1000e6); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IComet(comet).borrowBalanceOf(address(wallet)), 0e6); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - } - - function testSupplyMultipleCollateral() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 10 ether); - deal(LINK, address(wallet), 10e18); - deal(USDC, address(wallet), 1000e6); - - address[] memory assets = new address[](3); - uint256[] memory amounts = new uint256[](3); - assets[0] = WETH; - assets[1] = LINK; - assets[2] = USDC; - amounts[0] = 10 ether; - amounts[1] = 10e18; - amounts[2] = 1000e6; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyActions.supplyMultipleAssets.selector, comet, assets, amounts), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 10e18); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), LINK), 10e18); - assertApproxEqAbs(IComet(comet).balanceOf(address(wallet)), 1000e6, 1); - } - - function testInvalidInput() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - address[] memory assets = new address[](3); - uint256[] memory amounts = new uint256[](2); - assets[0] = WETH; - assets[1] = LINK; - assets[2] = USDC; - amounts[0] = 10 ether; - amounts[1] = 10e18; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyActions.supplyMultipleAssets.selector, comet, assets, amounts), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.expectRevert(abi.encodeWithSelector(LegendErrors.InvalidInput.selector)); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - } -} diff --git a/test/legend-scripts/CometSupplyMultipleAssetsAndBorrow.t.sol b/test/legend-scripts/CometSupplyMultipleAssetsAndBorrow.t.sol deleted file mode 100644 index 3d52e848..00000000 --- a/test/legend-scripts/CometSupplyMultipleAssetsAndBorrow.t.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for supplying and borrowing multiple assets from Comet - */ -contract CometSupplyMultipleAssetsAndBorrowTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA; - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - function testSupplyMultipleAssetsAndBorrow() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/CometSupplyMultipleAssetsAndBorrow.json"); - - deal(WETH, address(wallet), 10 ether); - deal(LINK, address(wallet), 10e18); - - address[] memory assets = new address[](2); - uint256[] memory amounts = new uint256[](2); - assets[0] = WETH; - assets[1] = LINK; - amounts[0] = 10 ether; - amounts[1] = 10e18; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyMultipleAssetsAndBorrow.run.selector, comet, assets, amounts, USDC, 100e6), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 10e18); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 0e6); - - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), LINK), 10e18); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 100e6); - } - - function testInvalidInput() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - bytes memory legendScript = new YulHelper().getCode("LegendScript.sol/CometSupplyMultipleAssetsAndBorrow.json"); - - address[] memory assets = new address[](2); - uint256[] memory amounts = new uint256[](1); - assets[0] = WETH; - assets[1] = LINK; - amounts[0] = 10 ether; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometSupplyMultipleAssetsAndBorrow.run.selector, comet, assets, amounts, USDC, 100e6), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.expectRevert(abi.encodeWithSelector(LegendErrors.InvalidInput.selector)); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - } -} diff --git a/test/legend-scripts/CometWithdrawActions.t.sol b/test/legend-scripts/CometWithdrawActions.t.sol deleted file mode 100644 index 6db88a5b..00000000 --- a/test/legend-scripts/CometWithdrawActions.t.sol +++ /dev/null @@ -1,245 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for withdrawing assets from Comet - */ -contract WithdrawActionsTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant LINK = 0x514910771AF9Ca656af840dff83E8264EcF986CA; - bytes legendScript = new YulHelper().getCode("LegendScript.sol/CometWithdrawActions.json"); - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - function testWithdraw() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - deal(WETH, address(wallet), 10 ether); - - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - vm.stopPrank(); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometWithdrawActions.withdraw.selector, comet, WETH, 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 0 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - } - - function testWithdrawTo() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet wallet2 = QuarkWallet(factory.create(alice, address(wallet), bytes32("2"))); - deal(WETH, address(wallet), 10 ether); - - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - vm.stopPrank(); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet2)), 0 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometWithdrawActions.withdrawTo.selector, comet, address(wallet2), WETH, 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 0 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet2)), 10 ether); - } - - function testWithdrawFrom() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet wallet2 = QuarkWallet(factory.create(alice, address(wallet), bytes32("2"))); - deal(WETH, address(wallet2), 10 ether); - - vm.startPrank(address(wallet2)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - IComet(comet).allow(address(wallet), true); - vm.stopPrank(); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet2), WETH), 10 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - CometWithdrawActions.withdrawFrom.selector, comet, address(wallet2), address(wallet), WETH, 10 ether - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet2), WETH), 0 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - } - - function testBorrow() public { - // gas: do not meter set-up - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - deal(WETH, address(wallet), 10 ether); - - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - vm.stopPrank(); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometWithdrawActions.withdraw.selector, comet, USDC, 100e6), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(IERC20(USDC).balanceOf(address(wallet)), 0e6); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IComet(comet).borrowBalanceOf(address(wallet)), 0e6); - - // gas: meter execute - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - - assertEq(IERC20(USDC).balanceOf(address(wallet)), 100e6); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IComet(comet).borrowBalanceOf(address(wallet)), 100e6); - } - - function testWithdrawMultipleAssets() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 10 ether); - deal(LINK, address(wallet), 1000e18); - deal(USDC, address(wallet), 1000e6); - - vm.startPrank(address(wallet)); - IERC20(WETH).approve(comet, 10 ether); - IComet(comet).supply(WETH, 10 ether); - IERC20(LINK).approve(comet, 1000e18); - IComet(comet).supply(LINK, 1000e18); - IERC20(USDC).approve(comet, 1000e6); - IComet(comet).supply(USDC, 1000e6); - vm.stopPrank(); - - // Fast forward 1 hour to accrue some interest so we can withdraw the full 1000e6 of SUDC - skip(3600); - - address[] memory assets = new address[](3); - uint256[] memory amounts = new uint256[](3); - assets[0] = WETH; - assets[1] = LINK; - assets[2] = USDC; - amounts[0] = 10 ether; - amounts[1] = 1000e18; - amounts[2] = 1000e6; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometWithdrawActions.withdrawMultipleAssets.selector, comet, assets, amounts), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 10 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), LINK), 1000e18); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 0e18); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 0e6); - - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), WETH), 0 ether); - assertEq(IComet(comet).collateralBalanceOf(address(wallet), LINK), 0e18); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(LINK).balanceOf(address(wallet)), 1000e18); - assertEq(IERC20(USDC).balanceOf(address(wallet)), 1000e6); - } - - function testInvalidInput() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - address[] memory assets = new address[](3); - uint256[] memory amounts = new uint256[](1); - assets[0] = WETH; - assets[1] = LINK; - assets[2] = USDC; - amounts[0] = 10 ether; - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(CometWithdrawActions.withdrawMultipleAssets.selector, comet, assets, amounts), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.expectRevert(abi.encodeWithSelector(LegendErrors.InvalidInput.selector)); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - } -} diff --git a/test/legend-scripts/GetDrip.t.sol b/test/legend-scripts/GetDrip.t.sol deleted file mode 100644 index 4e4b825d..00000000 --- a/test/legend-scripts/GetDrip.t.sol +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "legend-scripts/src/GetDrip.sol"; -import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {CodeJar} from "codejar/src/CodeJar.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; -import {YulHelper} from "test/lib/YulHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; - -/** - * Tests approve and execute against 0x - */ -contract GetDripTest is Test { - QuarkWalletProxyFactory public factory; - uint256 alicePrivateKey = 0xa11cee; - address alice = vm.addr(alicePrivateKey); - address constant USDC = 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238; - address constant fauceteer = 0x68793eA49297eB75DFB4610B68e076D2A5c7646C; - - function setUp() public { - // Fork setup - vm.createSelectFork("https://sepolia.infura.io/v3/531e3eb124194de5a88caec726d10bea"); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - // Tests dripping some usdc - function testDrip() public { - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, alice)); - new YulHelper().deploy("GetDrip.sol/GetDrip.json"); - - bytes memory legendScript = new YulHelper().getCode("GetDrip.sol/GetDrip.json"); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, legendScript, abi.encodeCall(GetDrip.drip, (fauceteer, USDC)), ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - vm.resumeGasMetering(); - - assertEq(IERC20(USDC).balanceOf(address(wallet)), 0); - wallet.executeQuarkOperation(op, v, r, s); - - // The drip always gives this amount - assertNotEq(IERC20(USDC).balanceOf(address(wallet)), 0); - } -} diff --git a/test/legend-scripts/TransferActions.t.sol b/test/legend-scripts/TransferActions.t.sol deleted file mode 100644 index 34eab23c..00000000 --- a/test/legend-scripts/TransferActions.t.sol +++ /dev/null @@ -1,454 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkScript} from "quark-core/src/QuarkScript.sol"; -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {Multicall} from "quark-core-scripts/src/Multicall.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {Counter} from "test/lib/Counter.sol"; -import {EvilReceiver} from "test/lib/EvilReceiver.sol"; -import {VictimERC777} from "test/lib/VictimERC777.sol"; -import {AllowCallbacks} from "test/lib/AllowCallbacks.sol"; -import {ReentrantTransfer} from "test/lib/ReentrantTransfer.sol"; - -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for transferring assets - */ -contract TransferActionsTest is Test { - QuarkWalletProxyFactory public factory; - CodeJar public codeJar; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - uint256 bobPrivateKey = 0xb0b; - address bob = vm.addr(bobPrivateKey); - bytes legendScript = new YulHelper().getCode("LegendScript.sol/TransferActions.json"); - bytes multicall = new YulHelper().getCode("Multicall.sol/Multicall.json"); - bytes allowCallbacks = new YulHelper().getCode("AllowCallbacks.sol/AllowCallbacks.json"); - - // Contracts address on mainnet - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - codeJar = QuarkWallet(payable(factory.walletImplementation())).codeJar(); - } - - function testTransferERC20TokenToEOA() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 10 ether); - - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(WETH).balanceOf(bob), 0 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferERC20Token.selector, WETH, bob, 10 ether), - ScriptType.ScriptSource - ); - - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IERC20(WETH).balanceOf(bob), 10 ether); - } - - function testTransferERC20TokenToQuarkWallet() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet walletBob = QuarkWallet(factory.create(bob, address(0))); - - deal(WETH, address(wallet), 10 ether); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(WETH).balanceOf(bob), 0 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferERC20Token.selector, WETH, address(walletBob), 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(WETH).balanceOf(address(wallet)), 0 ether); - assertEq(IERC20(WETH).balanceOf(address(walletBob)), 10 ether); - } - - function testTransferNativeTokenToEOA() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(address(wallet), 10 ether); - - assertEq(address(wallet).balance, 10 ether); - assertEq(bob.balance, 0 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, bob, 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - // assert on native ETH balance - assertEq(address(wallet).balance, 0 ether); - assertEq(bob.balance, 10 ether); - } - - function testTransferNativeTokenToQuarkWallet() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - QuarkWallet walletBob = QuarkWallet(factory.create(bob, address(0))); - deal(address(wallet), 10 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, address(walletBob), 10 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - assertEq(address(wallet).balance, 10 ether); - assertEq(address(walletBob).balance, 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - // assert on native ETH balance - assertEq(address(wallet).balance, 0 ether); - assertEq(address(walletBob).balance, 10 ether); - } - - function testTransferReentrancyAttackSuccessWithCallbackEnabled() public { - vm.pauseGasMetering(); - bytes memory reentrantTransfer = new YulHelper().getCode("ReentrantTransfer.sol/ReentrantTransfer.json"); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address reentrantTransferAddress = codeJar.saveCode(reentrantTransfer); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 2) - ); - deal(address(wallet), 10 ether); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(reentrantTransferAddress)); - callContracts[1] = reentrantTransferAddress; - callDatas[1] = - abi.encodeWithSelector(ReentrantTransfer.transferNativeToken.selector, address(evilReceiver), 1 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(address(wallet).balance, 7 ether); - assertEq(address(evilReceiver).balance, 3 ether); - } - - function testTransferERC777TokenReentrancyAttackSuccessWithCallbackEnabled() public { - vm.pauseGasMetering(); - bytes memory reentrantTransfer = new YulHelper().getCode("ReentrantTransfer.sol/ReentrantTransfer.json"); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address reentrantTransferAddress = codeJar.saveCode(reentrantTransfer); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 2) - ); - // Create victim ERC777 token - VictimERC777 victimERC777 = new VictimERC777(); - victimERC777.mint(address(wallet), 10 ether); - evilReceiver.setTargetTokenAddress(address(victimERC777)); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(reentrantTransferAddress)); - callContracts[1] = reentrantTransferAddress; - callDatas[1] = abi.encodeWithSelector( - ReentrantTransfer.transferERC20Token.selector, address(victimERC777), address(evilReceiver), 1 ether - ); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - // Attacker successfully transfers 3 eth by exploiting reentrancy in 1eth transfers - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 7 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 3 ether); - } - - function testTransferSuccessWithEvilReceiverWithoutAttackAttempt() public { - vm.pauseGasMetering(); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address legendScriptAddress = codeJar.saveCode(legendScript); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - // Attack maxCalls set to 0, so no attack will be attempted - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 0) - ); - deal(address(wallet), 10 ether); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(legendScriptAddress)); - callContracts[1] = legendScriptAddress; - callDatas[1] = - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, address(evilReceiver), 1 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(address(wallet).balance, 9 ether); - assertEq(address(evilReceiver).balance, 1 ether); - } - - function testTransferERC777SuccessWithEvilReceiverWithoutAttackAttempt() public { - vm.pauseGasMetering(); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address legendScriptAddress = codeJar.saveCode(legendScript); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - // Attack maxCalls set to 0, so no attack will be attempted - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 0) - ); - // Create victim ERC777 token - VictimERC777 victimERC777 = new VictimERC777(); - victimERC777.mint(address(wallet), 10 ether); - evilReceiver.setTargetTokenAddress(address(victimERC777)); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(legendScriptAddress)); - callContracts[1] = legendScriptAddress; - callDatas[1] = abi.encodeWithSelector( - ReentrantTransfer.transferERC20Token.selector, address(victimERC777), address(evilReceiver), 1 ether - ); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 0 ether); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 9 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 1 ether); - } - - function testRevertsForTransferReentrancyAttackWithReentrancyGuard() public { - vm.pauseGasMetering(); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address legendScriptAddress = codeJar.saveCode(legendScript); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 2) - ); - deal(address(wallet), 10 ether); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(legendScriptAddress)); - callContracts[1] = legendScriptAddress; - callDatas[1] = - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, address(evilReceiver), 1 ether); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - vm.resumeGasMetering(); - vm.expectRevert( - abi.encodeWithSelector( - Multicall.MulticallError.selector, - 1, - callContracts[1], - abi.encodeWithSelector( - LegendErrors.TransferFailed.selector, abi.encodeWithSelector(QuarkScript.ReentrantCall.selector) - ) - ) - ); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - } - - function testRevertsForTransferERC777ReentrancyAttackWithReentrancyGuard() public { - vm.pauseGasMetering(); - address allowCallbacksAddress = codeJar.saveCode(allowCallbacks); - address legendScriptAddress = codeJar.saveCode(legendScript); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 2) - ); - // Create victim ERC777 token - VictimERC777 victimERC777 = new VictimERC777(); - victimERC777.mint(address(wallet), 10 ether); - evilReceiver.setTargetTokenAddress(address(victimERC777)); - // Compose array of parameters - address[] memory callContracts = new address[](2); - bytes[] memory callDatas = new bytes[](2); - callContracts[0] = allowCallbacksAddress; - callDatas[0] = abi.encodeWithSelector(AllowCallbacks.run.selector, address(legendScriptAddress)); - callContracts[1] = legendScriptAddress; - callDatas[1] = abi.encodeWithSelector( - ReentrantTransfer.transferERC20Token.selector, address(victimERC777), address(evilReceiver), 1 ether - ); - - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - multicall, - abi.encodeWithSelector(Multicall.run.selector, callContracts, callDatas), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 0 ether); - vm.resumeGasMetering(); - vm.expectRevert( - abi.encodeWithSelector( - Multicall.MulticallError.selector, - 1, - callContracts[1], - abi.encodeWithSelector(QuarkScript.ReentrantCall.selector) - ) - ); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(IERC20(victimERC777).balanceOf(address(wallet)), 10 ether); - assertEq(IERC20(victimERC777).balanceOf(address(evilReceiver)), 0 ether); - } - - function testRevertsForTransferReentrancyAttackWithoutCallbackEnabled() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.REINVOKE_TRANSFER, address(evilReceiver), 1 ether, 2) - ); - deal(address(wallet), 10 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, address(evilReceiver), 1 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - // Reentering into the QuarkWallet fails due to there being no active callback - vm.expectRevert( - abi.encodeWithSelector( - LegendErrors.TransferFailed.selector, abi.encodeWithSelector(QuarkWallet.NoActiveCallback.selector) - ) - ); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - } - - function testRevertsForTransferReentrantAttackWithStolenSignature() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - EvilReceiver evilReceiver = new EvilReceiver(); - evilReceiver.setAttack( - EvilReceiver.ReentryAttack(EvilReceiver.AttackType.STOLEN_SIGNATURE, address(evilReceiver), 1 ether, 2) - ); - deal(address(wallet), 10 ether); - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector(TransferActions.transferNativeToken.selector, address(evilReceiver), 1 ether), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - evilReceiver.stealSignature(EvilReceiver.StolenSignature(op, v, r, s)); - - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - vm.resumeGasMetering(); - // Not replayable signature will blocked by QuarkWallet during executeQuarkOperation - vm.expectRevert( - abi.encodeWithSelector( - LegendErrors.TransferFailed.selector, abi.encodeWithSelector(QuarkStateManager.NonceAlreadySet.selector) - ) - ); - wallet.executeQuarkOperation(op, v, r, s); - // assert on native ETH balance - assertEq(address(wallet).balance, 10 ether); - assertEq(address(evilReceiver).balance, 0 ether); - } -} diff --git a/test/legend-scripts/UniswapSwapActions.t.sol b/test/legend-scripts/UniswapSwapActions.t.sol deleted file mode 100644 index 295b17d4..00000000 --- a/test/legend-scripts/UniswapSwapActions.t.sol +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity 0.8.23; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import "forge-std/StdUtils.sol"; -import "forge-std/StdMath.sol"; - -import {CodeJar} from "codejar/src/CodeJar.sol"; - -import {QuarkWallet} from "quark-core/src/QuarkWallet.sol"; -import {QuarkStateManager} from "quark-core/src/QuarkStateManager.sol"; - -import {QuarkWalletProxyFactory} from "quark-proxy/src/QuarkWalletProxyFactory.sol"; - -import {YulHelper} from "test/lib/YulHelper.sol"; -import {SignatureHelper} from "test/lib/SignatureHelper.sol"; -import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; - -import {Counter} from "test/lib/Counter.sol"; - -import "legend-scripts/src/LegendScript.sol"; - -/** - * Tests for purchasing assets from Uniswap V3 - */ -contract UniswapSwapActionsTest is Test { - QuarkWalletProxyFactory public factory; - Counter public counter; - uint256 alicePrivateKey = 0xa11ce; - address alice = vm.addr(alicePrivateKey); - - // Contracts address on mainnet - address constant comet = 0xc3d688B66703497DAA19211EEdff47f25384cdc3; - address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; - address constant COMP = 0xc00e94Cb662C3520282E6f5717214004A7f26888; - // Uniswap router info on mainnet - address constant uniswapRouter = 0xE592427A0AEce92De3Edee1F18E0157C05861564; - bytes legendScript = new YulHelper().getCode("LegendScript.sol/UniswapSwapActions.json"); - - function setUp() public { - // Fork setup - vm.createSelectFork( - string.concat( - "https://node-provider.compound.finance/ethereum-mainnet/", vm.envString("NODE_PROVIDER_BYPASS_KEY") - ), - 18429607 // 2023-10-25 13:24:00 PST - ); - factory = new QuarkWalletProxyFactory(address(new QuarkWallet(new CodeJar(), new QuarkStateManager()))); - } - - // Usually one stop is sufficient for pairs with high liquidity - function testBuyAssetOneStop() public { - // gas: do not meter set-up - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - deal(USDC, address(wallet), 10000e6); - - // ExactIn: Limit the amount of USDC you want to spend and receive as much WETH as possible - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactIn.selector, - UniswapSwapActions.SwapParamsExactIn({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: USDC, - amount: 2000e6, - amountOutMinimum: 1 ether, - deadline: block.timestamp + 1000, - path: abi.encodePacked(USDC, uint24(500), WETH) // Path: USDC - 0.05% -> WETH - }) - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - // gas: meter execute - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - - uint256 wethBalance = IERC20(WETH).balanceOf(address(wallet)); - uint256 usdcBalance = IERC20(USDC).balanceOf(address(wallet)); - assertGe(wethBalance, 1 ether); - assertEq(usdcBalance, 8000e6); - - vm.pauseGasMetering(); - // ExactOut: Limit the amount of WETH you want to receive and spend as much USDC as necessary - QuarkWallet.QuarkOperation memory op2 = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactOut.selector, - UniswapSwapActions.SwapParamsExactOut({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: USDC, - amount: 1 ether, - amountInMaximum: 2000e6, - deadline: block.timestamp + 1000, - path: abi.encodePacked(WETH, uint24(500), USDC) // Path: WETH - 0.05% -> USDC - }) - ), - ScriptType.ScriptSource - ); - - (uint8 v2, bytes32 r2, bytes32 s2) = new SignatureHelper().signOp(alicePrivateKey, wallet, op2); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op2, v2, r2, s2); - assertEq(IERC20(WETH).balanceOf(address(wallet)), wethBalance + 1 ether); - assertGe(IERC20(USDC).balanceOf(address(wallet)), usdcBalance - 2000e6); - } - - // Lower liquidity asset may require to have two stops (USDC -> ETH -> COMP) - function testBuyAssetTwoStops() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(USDC, address(wallet), 10000e6); - - // ExactIn: Limit the amount of USDC you want to spend and receive as much COMP as possible - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactIn.selector, - UniswapSwapActions.SwapParamsExactIn({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: USDC, - amount: 2000e6, - amountOutMinimum: 40e18, - deadline: block.timestamp + 1000, - path: abi.encodePacked(USDC, uint24(500), WETH, uint24(3000), COMP) // Path: USDC - 0.05% -> WETH - 0.3% -> COMP - }) - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - uint256 compBalance = IERC20(COMP).balanceOf(address(wallet)); - uint256 usdcBalance = IERC20(USDC).balanceOf(address(wallet)); - assertGe(compBalance, 40e18); - assertEq(usdcBalance, 8000e6); - vm.pauseGasMetering(); - - // ExactOut: Limit the amount of COMP you want to receive and spend as much USDC as necessary - QuarkWallet.QuarkOperation memory op2 = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactOut.selector, - UniswapSwapActions.SwapParamsExactOut({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: USDC, - amount: 40e18, - amountInMaximum: 2000e6, - deadline: block.timestamp + 1000, - path: abi.encodePacked(COMP, uint24(3000), WETH, uint24(500), USDC) // Path: COMP - 0.05% -> WETH - 0.3% -> USDC - }) - ), - ScriptType.ScriptSource - ); - (uint8 v2, bytes32 r2, bytes32 s2) = new SignatureHelper().signOp(alicePrivateKey, wallet, op2); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op2, v2, r2, s2); - assertEq(IERC20(COMP).balanceOf(address(wallet)), compBalance + 40e18); - assertGe(IERC20(USDC).balanceOf(address(wallet)), usdcBalance - 2000e6); - } - - // Usually one stop is sufficient for pairs with high liquidity - function testSellAssetOneStop() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(WETH, address(wallet), 2 ether); - - // ExactIn: Limit the amount of WETH you want to spend and receive as much USDC as possible - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactIn.selector, - UniswapSwapActions.SwapParamsExactIn({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: WETH, - amount: 1 ether, - amountOutMinimum: 1000e6, - deadline: block.timestamp + 1000, - path: abi.encodePacked(WETH, uint24(500), USDC) // Path: WETH - 0.05% -> USDC - }) - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - uint256 wethBalance = IERC20(WETH).balanceOf(address(wallet)); - uint256 usdcBalance = IERC20(USDC).balanceOf(address(wallet)); - assertEq(wethBalance, 1 ether); - assertGe(usdcBalance, 1000e6); - vm.pauseGasMetering(); - // ExactOut: Limit the amount of USDC you want to receive and spend as much WETH as necessary - QuarkWallet.QuarkOperation memory op2 = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactOut.selector, - UniswapSwapActions.SwapParamsExactOut({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: WETH, - amount: 1600e6, - amountInMaximum: 1 ether, - deadline: block.timestamp + 1000, - path: abi.encodePacked(USDC, uint24(500), WETH) // Path: USDC - 0.05% -> WETH - }) - ), - ScriptType.ScriptSource - ); - (uint8 v2, bytes32 r2, bytes32 s2) = new SignatureHelper().signOp(alicePrivateKey, wallet, op2); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op2, v2, r2, s2); - assertGe(IERC20(WETH).balanceOf(address(wallet)), 0); - assertEq(IERC20(USDC).balanceOf(address(wallet)), usdcBalance + 1600e6); - } - - // Lower liquidity asset may require to have two stops (COMP -> ETH -> USDC) - function testSellAssetTwoStops() public { - vm.pauseGasMetering(); - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - - deal(COMP, address(wallet), 100e18); - - // ExactIn: Limit the amount of COMP you want to spend and spend as much USDC as possible - QuarkWallet.QuarkOperation memory op = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactIn.selector, - UniswapSwapActions.SwapParamsExactIn({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: COMP, - amount: 50e18, - amountOutMinimum: 1800e6, - deadline: block.timestamp + 1000, - path: abi.encodePacked(COMP, uint24(3000), WETH, uint24(500), USDC) // Path: COMP - 0.05% -> WETH - 0.3% -> USDC - }) - ), - ScriptType.ScriptSource - ); - (uint8 v, bytes32 r, bytes32 s) = new SignatureHelper().signOp(alicePrivateKey, wallet, op); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op, v, r, s); - uint256 compBalance = IERC20(COMP).balanceOf(address(wallet)); - uint256 usdcBalance = IERC20(USDC).balanceOf(address(wallet)); - assertEq(compBalance, 50e18); - assertGe(usdcBalance, 1800e6); - vm.pauseGasMetering(); - // ExactOut: Limit the amount of USDC you want to spend and spend as much COMP as necessary - QuarkWallet.QuarkOperation memory op2 = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactOut.selector, - UniswapSwapActions.SwapParamsExactOut({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: COMP, - amount: 1500e6, - amountInMaximum: 50e18, - deadline: block.timestamp + 1000, - path: abi.encodePacked(USDC, uint24(500), WETH, uint24(3000), COMP) // Path: USDC - 0.05% -> WETH - 0.3% -> COMP - }) - ), - ScriptType.ScriptSource - ); - (uint8 v2, bytes32 r2, bytes32 s2) = new SignatureHelper().signOp(alicePrivateKey, wallet, op2); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op2, v2, r2, s2); - assertGe(IERC20(COMP).balanceOf(address(wallet)), 0); - assertEq(IERC20(USDC).balanceOf(address(wallet)), usdcBalance + 1500e6); - } - - function testApprovalRefund() public { - // gas: do not meter set-up - vm.pauseGasMetering(); - - QuarkWallet wallet = QuarkWallet(factory.create(alice, address(0))); - deal(USDC, address(wallet), 10000e6); - uint256 wethBalance = IERC20(WETH).balanceOf(address(wallet)); - uint256 usdcBalance = IERC20(USDC).balanceOf(address(wallet)); - - // ExactOut: Limit the amount of WETH you want to receive and spend as much USDC as necessary - QuarkWallet.QuarkOperation memory op2 = new QuarkOperationHelper().newBasicOpWithCalldata( - wallet, - legendScript, - abi.encodeWithSelector( - UniswapSwapActions.swapAssetExactOut.selector, - UniswapSwapActions.SwapParamsExactOut({ - uniswapRouter: uniswapRouter, - recipient: address(wallet), - tokenFrom: USDC, - amount: 1 ether, - amountInMaximum: 10000e6, // Give it a high amount to trigger approval refund - deadline: block.timestamp + 1000, - path: abi.encodePacked(WETH, uint24(500), USDC) // Path: WETH - 0.05% -> USDC - }) - ), - ScriptType.ScriptSource - ); - - (uint8 v2, bytes32 r2, bytes32 s2) = new SignatureHelper().signOp(alicePrivateKey, wallet, op2); - vm.resumeGasMetering(); - wallet.executeQuarkOperation(op2, v2, r2, s2); - assertEq(IERC20(WETH).balanceOf(address(wallet)), wethBalance + 1 ether); - assertGe(IERC20(USDC).balanceOf(address(wallet)), usdcBalance - 2000e6); - assertEq(IERC20(USDC).allowance(address(wallet), uniswapRouter), 0); - } -} diff --git a/src/legend-scripts/src/LegendScript.sol b/test/lib/DeFiScripts.sol similarity index 57% rename from src/legend-scripts/src/LegendScript.sol rename to test/lib/DeFiScripts.sol index 11f12a4a..3e1fd1e6 100644 --- a/src/legend-scripts/src/LegendScript.sol +++ b/test/lib/DeFiScripts.sol @@ -5,16 +5,13 @@ 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 {QuarkScript} from "quark-core/src/QuarkScript.sol"; +import {IComet} from "test/quark-core-scripts/interfaces/IComet.sol"; -import {IComet} from "legend-scripts/src/interfaces/IComet.sol"; -import {ICometRewards} from "legend-scripts/src/interfaces/ICometRewards.sol"; -import {LegendErrors} from "legend-scripts/src/LegendErrors.sol"; - -// TODO: Will need to add support for E-Comet once E-Comet has been deployed contract CometSupplyActions { using SafeERC20 for IERC20; + error InvalidInput(); + /** * @notice Supply an asset to Comet * @param comet The Comet address @@ -58,7 +55,7 @@ contract CometSupplyActions { */ function supplyMultipleAssets(address comet, address[] calldata assets, uint256[] calldata amounts) external { if (assets.length != amounts.length) { - revert LegendErrors.InvalidInput(); + revert InvalidInput(); } for (uint256 i = 0; i < assets.length;) { @@ -74,6 +71,8 @@ contract CometSupplyActions { contract CometWithdrawActions { using SafeERC20 for IERC20; + error InvalidInput(); + /** * @notice Withdraw an asset from Comet * @param comet The Comet address @@ -115,7 +114,7 @@ contract CometWithdrawActions { */ function withdrawMultipleAssets(address comet, address[] calldata assets, uint256[] calldata amounts) external { if (assets.length != amounts.length) { - revert LegendErrors.InvalidInput(); + revert InvalidInput(); } for (uint256 i = 0; i < assets.length;) { @@ -193,137 +192,3 @@ contract UniswapSwapActions { } } } - -contract TransferActions is QuarkScript { - using SafeERC20 for IERC20; - - /** - * @notice Transfer ERC20 token - * @param token The token address - * @param recipient The recipient address - * @param amount The amount to transfer - */ - function transferERC20Token(address token, address recipient, uint256 amount) external onlyWallet { - IERC20(token).safeTransfer(recipient, amount); - } - - /** - * @notice Transfer native token (i.e. ETH) - * @param recipient The recipient address - * @param amount The amount to transfer - */ - function transferNativeToken(address recipient, uint256 amount) external onlyWallet { - (bool success, bytes memory data) = payable(recipient).call{value: amount}(""); - if (!success) { - revert LegendErrors.TransferFailed(data); - } - } -} - -contract CometClaimRewards { - /** - * @notice Claim rewards - * @param cometRewards The CometRewards addresses - * @param comets The Comet addresses - * @param recipient The recipient address, that will receive the COMP rewards - */ - function claim(address[] calldata cometRewards, address[] calldata comets, address recipient) external { - if (cometRewards.length != comets.length) { - revert LegendErrors.InvalidInput(); - } - - for (uint256 i = 0; i < cometRewards.length;) { - ICometRewards(cometRewards[i]).claim(comets[i], recipient, true); - unchecked { - ++i; - } - } - } -} - -contract CometSupplyMultipleAssetsAndBorrow { - // To handle non-standard ERC20 tokens (i.e. USDT) - using SafeERC20 for IERC20; - - function run( - address comet, - address[] calldata assets, - uint256[] calldata amounts, - address baseAsset, - uint256 borrow - ) external { - if (assets.length != amounts.length) { - revert LegendErrors.InvalidInput(); - } - - for (uint256 i = 0; i < assets.length;) { - IERC20(assets[i]).forceApprove(comet, amounts[i]); - IComet(comet).supply(assets[i], amounts[i]); - unchecked { - ++i; - } - } - IComet(comet).withdraw(baseAsset, borrow); - } -} - -contract CometRepayAndWithdrawMultipleAssets { - // To handle non-standard ERC20 tokens (i.e. USDT) - using SafeERC20 for IERC20; - - function run(address comet, address[] calldata assets, uint256[] calldata amounts, address baseAsset, uint256 repay) - external - { - if (assets.length != amounts.length) { - revert LegendErrors.InvalidInput(); - } - - IERC20(baseAsset).forceApprove(comet, repay); - IComet(comet).supply(baseAsset, repay); - for (uint256 i = 0; i < assets.length;) { - IComet(comet).withdraw(assets[i], amounts[i]); - unchecked { - ++i; - } - } - } -} - -contract ApproveAndSwap { - // To handle non-standard ERC20 tokens (i.e. USDT) - using SafeERC20 for IERC20; - - /** - * Approve a specified contract for an amount of token and execute the data against it - * @param to The contract address to approve execute on - * @param sellToken The token address to approve - * @param sellAmount The amount to approve - * @param buyToken The token that is being bought - * @param data The data to execute - */ - function run( - address to, - address sellToken, - uint256 sellAmount, - address buyToken, - uint256 expectedBuyAmount, - bytes calldata data - ) external { - IERC20(sellToken).forceApprove(to, sellAmount); - uint256 buyTokenBalanceBefore = IERC20(buyToken).balanceOf(address(this)); - - (bool success, bytes memory returnData) = to.call(data); - if (!success) { - revert LegendErrors.ApproveAndSwapFailed(returnData); - } - - uint256 buyTokenBalanceAfter = IERC20(buyToken).balanceOf(address(this)); - uint256 buyAmount = buyTokenBalanceAfter - buyTokenBalanceBefore; - if (buyAmount < expectedBuyAmount) { - revert LegendErrors.TooMuchSlippage(); - } - - // Approvals to external contracts should always be reset to 0 - IERC20(sellToken).forceApprove(to, 0); - } -} diff --git a/test/lib/EvilReceiver.sol b/test/lib/EvilReceiver.sol index b9a8b927..65b488e1 100644 --- a/test/lib/EvilReceiver.sol +++ b/test/lib/EvilReceiver.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.23; import "quark-core/src/QuarkWallet.sol"; import "quark-core/src/QuarkScript.sol"; -import "legend-scripts/src/LegendScript.sol"; +import {TransferActions} from "test/lib/Transfer.sol"; contract EvilReceiver { enum AttackType { diff --git a/test/lib/Transfer.sol b/test/lib/Transfer.sol new file mode 100644 index 00000000..157ad062 --- /dev/null +++ b/test/lib/Transfer.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.23; + +import {IERC20} from "openzeppelin/token/ERC20/IERC20.sol"; +import {SafeERC20} from "openzeppelin/token/ERC20/utils/SafeERC20.sol"; + +import {QuarkScript} from "quark-core/src/QuarkScript.sol"; + +contract TransferActions is QuarkScript { + using SafeERC20 for IERC20; + + error TransferFailed(bytes data); + + /** + * @notice Transfer ERC20 token + * @param token The token address + * @param recipient The recipient address + * @param amount The amount to transfer + */ + function transferERC20Token(address token, address recipient, uint256 amount) external onlyWallet { + IERC20(token).safeTransfer(recipient, amount); + } + + /** + * @notice Transfer native token (i.e. ETH) + * @param recipient The recipient address + * @param amount The amount to transfer + */ + function transferNativeToken(address recipient, uint256 amount) external onlyWallet { + (bool success, bytes memory data) = payable(recipient).call{value: amount}(""); + if (!success) { + revert TransferFailed(data); + } + } +} diff --git a/test/quark-core-scripts/Multicall.t.sol b/test/quark-core-scripts/Multicall.t.sol index 7648b55a..0ddbee3f 100644 --- a/test/quark-core-scripts/Multicall.t.sol +++ b/test/quark-core-scripts/Multicall.t.sol @@ -21,7 +21,7 @@ import {YulHelper} from "test/lib/YulHelper.sol"; import {SignatureHelper} from "test/lib/SignatureHelper.sol"; import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; -import "legend-scripts/src/LegendScript.sol"; +import {CometSupplyActions, CometWithdrawActions, IComet, IERC20, UniswapSwapActions} from "test/lib/DeFiScripts.sol"; contract MulticallTest is Test { QuarkWalletProxyFactory public factory; @@ -39,11 +39,11 @@ contract MulticallTest is Test { bytes ethcall = new YulHelper().getCode("Ethcall.sol/Ethcall.json"); bytes multicall; - bytes legendCometSupplyScript = new YulHelper().getCode("LegendScript.sol/CometSupplyActions.json"); + bytes legendCometSupplyScript = new YulHelper().getCode("DeFiScripts.sol/CometSupplyActions.json"); - bytes legendCometWithdrawScript = new YulHelper().getCode("LegendScript.sol/CometWithdrawActions.json"); + bytes legendCometWithdrawScript = new YulHelper().getCode("DeFiScripts.sol/CometWithdrawActions.json"); - bytes legendUniswapSwapScript = new YulHelper().getCode("LegendScript.sol/UniswapSwapActions.json"); + bytes legendUniswapSwapScript = new YulHelper().getCode("DeFiScripts.sol/UniswapSwapActions.json"); address ethcallAddress; address multicallAddress; diff --git a/test/quark-core-scripts/Paycall.t.sol b/test/quark-core-scripts/Paycall.t.sol index 6a73cd90..9fca53c0 100644 --- a/test/quark-core-scripts/Paycall.t.sol +++ b/test/quark-core-scripts/Paycall.t.sol @@ -23,7 +23,7 @@ import {SignatureHelper} from "test/lib/SignatureHelper.sol"; import {QuarkOperationHelper, ScriptType} from "test/lib/QuarkOperationHelper.sol"; import "quark-core-scripts/src/vendor/chainlink/AggregatorV3Interface.sol"; -import "legend-scripts/src/LegendScript.sol"; +import {IComet, IERC20} from "test/lib/DeFiScripts.sol"; contract PaycallTest is Test { QuarkWalletProxyFactory public factory; @@ -53,11 +53,11 @@ contract PaycallTest is Test { bytes paycallUSDT; bytes paycallWBTC; - bytes legendCometSupplyScript = new YulHelper().getCode("LegendScript.sol/CometSupplyActions.json"); + bytes legendCometSupplyScript = new YulHelper().getCode("DeFiScripts.sol/CometSupplyActions.json"); - bytes legendCometWithdrawScript = new YulHelper().getCode("LegendScript.sol/CometWithdrawActions.json"); + bytes legendCometWithdrawScript = new YulHelper().getCode("DeFiScripts.sol/CometWithdrawActions.json"); - bytes legendUniswapSwapScript = new YulHelper().getCode("LegendScript.sol/UniswapSwapActions.json"); + bytes legendUniswapSwapScript = new YulHelper().getCode("DeFiScripts.sol/UniswapSwapActions.json"); address ethcallAddress; address multicallAddress;