Skip to content

Commit

Permalink
fix: UniV3 handler
Browse files Browse the repository at this point in the history
  • Loading branch information
toparc077 committed Oct 2, 2023
1 parent f42da27 commit 021a6df
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 39 deletions.
45 changes: 6 additions & 39 deletions defi-strategies/contracts/handlers/uniV3/UniV3H.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,45 +44,21 @@ contract UniV3Handler is BaseHandler, ReentrancyGuard {
}

function deposit(uint256 amount) external nonReentrant {
console.log("Sender", msg.sender);
IERC20(USDC).safeTransferFrom(msg.sender, address(this), amount);
IERC20(USDC).safeApprove(address(swapRouter), amount);
uint256 balance = IERC20(USDC).balanceOf(address(this));

console.log("Balance", balance);
depositInfo[msg.sender] += amount;
totalDepositedAmount += amount;
if (!isDepositUser[msg.sender]) {
++totalDepositedUser;
}
isDepositUser[msg.sender] = true;
}

function withdraw(uint256 amount) external nonReentrant {
_requireMsg(
depositInfo[msg.sender] >= amount,
"withdraw",
"You don't have amount to withdraw"
);

depositInfo[msg.sender] -= amount;
IERC20(USDC).safeTransfer(msg.sender, depositInfo[msg.sender]);
}

function compound() external {
_tokenApprove(USDC, address(swapRouter), amount);
IUniswapRouterV3.ExactInputSingleParams memory params = IUniswapRouterV3
.ExactInputSingleParams({
tokenIn: USDC,
tokenOut: WETH9,
fee: FEE_TIER,
recipient: address(this),
deadline: block.timestamp,
amountIn: depositInfo[msg.sender],
amountIn: amount,
amountOutMinimum: 0,
sqrtPriceLimitX96: 0
});
uint256 amountOut = swapRouter.exactInputSingle(params);
IERC20(WETH9).safeApprove(address(sushiRouter), amountOut);

_tokenApprove(WETH9, address(sushiRouter), amount);

address[] memory swapPath = new address[](2);
swapPath[0] = WETH9;
Expand All @@ -94,22 +70,13 @@ contract UniV3Handler is BaseHandler, ReentrancyGuard {
address(this),
block.timestamp + 100
);
uint256 profit = amountsOut[amountsOut.length - 1] -
depositInfo[msg.sender];
totalProfitAmount += profit;
devFeeAmount += (profit * devFee) / 10000;
depositInfo[msg.sender] =
amountsOut[amountsOut.length - 1] -
((profit * devFee) / 10000);

console.log("USDC Amount Out:", amountsOut[amountsOut.length - 1]);
}

/* solhint-disable no-empty-blocks */
function execStrategy(bytes memory data) public payable {}

function getAPY() public view returns (uint256) {
return (totalProfitAmount * 10000) / totalDepositedAmount;
}

function getContractName() public pure override returns (string memory) {
return "UniswapV3 Handler";
}
Expand Down
172 changes: 172 additions & 0 deletions defi-strategies/test/UniV3.Module.specs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { expect } from "chai";
import { Contract } from "ethers";
import { decodeError } from "ethers-decode-error";
import hardhat, { ethers, deployments, waffle } from "hardhat";
import { buildEcdsaModuleAuthorizedStrategyTx } from "./utils/execution";
import { makeEcdsaModuleUserOp } from "./utils/userOp";
import {
SUSHISWAP_ROUTER,
UNISWAPV3_ROUTER,
USDC_TOKEN,
WRAPPED_NATIVE_TOKEN

Check failure on line 11 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Insert `,`
} from "./utils/constants_eth";
import { MAX_UINT256 } from "./utils/constants";

Check warning on line 13 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

'MAX_UINT256' is defined but never used

import {
getEntryPoint,
getEcdsaOwnershipRegistryModule,
getSmartAccountWithModule,
getStrategyModule,
} from "./utils/setupHelper";
import { getTokenProvider } from "./utils/providers";

describe("Strategy Module (UniV3)", async () => {
const chainId = hardhat.network.config.chainId;
if (chainId === 1 || chainId === 137) {
// This test supports to run on these chains.
} else {
return;
}
const [deployer, smartAccountOwner, alice, developer] = waffle.provider.getWallets();

Check failure on line 30 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Insert `⏎···`
let strategyModule: Contract;
let uniV3Handler: Contract;
let wrappedETH: Contract;
let usdcToken: Contract;
let usdcProviderAddress: any;

Check warning on line 35 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Unexpected any. Specify a different type
let fee: any;

Check warning on line 36 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Unexpected any. Specify a different type

const setupTests = deployments.createFixture(async ({ deployments }) => {
await deployments.fixture();

const errAbi = [
{
inputs: [
{
internalType: "uint256",
name: "",
type: "uint256",
},
],
name: "RevertEstimation",
type: "error",
},
];

usdcToken = await (
await ethers.getContractFactory("MockToken")
).attach(USDC_TOKEN);

wrappedETH = await (

Check warning on line 59 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

'wrappedETH' is assigned a value but never used
await ethers.getContractFactory("MockToken")
).attach(WRAPPED_NATIVE_TOKEN);


Check failure on line 63 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Delete `⏎`
usdcProviderAddress = await getTokenProvider(usdcToken.address);
usdcProviderAddress = await ethers.getSigner(usdcProviderAddress);

Check failure on line 66 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Delete `··`
const entryPoint = await getEntryPoint();
const ecdsaModule = await getEcdsaOwnershipRegistryModule();
const EcdsaOwnershipRegistryModule = await ethers.getContractFactory(
"EcdsaOwnershipRegistryModule"
);

const ecdsaOwnershipSetupData =
EcdsaOwnershipRegistryModule.interface.encodeFunctionData(
"initForSmartAccount",
[await smartAccountOwner.getAddress()]
);
const smartAccountDeploymentIndex = 0;
const userSA = await getSmartAccountWithModule(
ecdsaModule.address,
ecdsaOwnershipSetupData,
smartAccountDeploymentIndex
);

// send funds to userSA and mint tokens
await deployer.sendTransaction({
to: userSA.address,
value: ethers.utils.parseEther("10"),
});

uniV3Handler = await (
await ethers.getContractFactory("UniV3Handler")
).deploy(UNISWAPV3_ROUTER, SUSHISWAP_ROUTER, "100", developer.address);

strategyModule = await getStrategyModule(
alice.address,
uniV3Handler.address,
smartAccountDeploymentIndex
);

const userOp = await makeEcdsaModuleUserOp(
"enableModule",
[strategyModule.address],
userSA.address,
smartAccountOwner,
entryPoint,
ecdsaModule.address
);

await entryPoint.handleOps([userOp], alice.address);

return {
ecdsaModule: ecdsaModule,
userSA: userSA,
errAbi: errAbi,
};
});

it("Module is enabled", async () => {
const { userSA } = await setupTests();
expect(await userSA.isModuleEnabled(strategyModule.address)).to.equal(true);
});

describe("Deposit", () => {
it("deposit token normal", async () => {
const { userSA, ecdsaModule, errAbi } = await setupTests();
const value = ethers.utils.parseUnits("100", 6);

const handler = uniV3Handler.address;

const data = (
await ethers.getContractFactory("UniV3Handler")
).interface.encodeFunctionData("deposit(uint256)", [

Check failure on line 133 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Replace `⏎········value,⏎······` with `value`
value,
]);

await usdcToken.connect(usdcProviderAddress).transfer(userSA.address, value);

Check failure on line 137 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Replace `.connect(usdcProviderAddress)` with `⏎········.connect(usdcProviderAddress)⏎········`

const { transaction, signature } =
await buildEcdsaModuleAuthorizedStrategyTx(
handler,
data,
userSA,
smartAccountOwner,
ecdsaModule.address,
strategyModule,
0
);

try {
await strategyModule.requiredTxFee(userSA.address, transaction);
} catch (error) {
fee = decodeError(error, errAbi).args;
fee = fee[0];
}

try {
await strategyModule.execStrategy(
userSA.address,
transaction,
signature,
{ value: fee }
);
} catch(error) {

Check failure on line 164 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Insert `·`
console.log(error)

Check failure on line 165 in defi-strategies/test/UniV3.Module.specs.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Insert `;`
}
const afterExecBalance = await usdcToken.balanceOf(userSA.address);

expect(afterExecBalance).to.be.eq(value);
});
});
});
4 changes: 4 additions & 0 deletions defi-strategies/test/utils/constants_eth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ export const DAI_TOKEN = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
export const ADAI_V2 = "0x028171bCA77440897B824Ca71D1c56caC55b68A3";

export const USDC_TOKEN = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";

export const UNISWAPV3_ROUTER = "0xE592427A0AEce92De3Edee1F18E0157C05861564";

export const SUSHISWAP_ROUTER = "0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F";

Check failure on line 20 in defi-strategies/test/utils/constants_eth.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Insert `⏎`

0 comments on commit 021a6df

Please sign in to comment.