Skip to content

Commit

Permalink
Drop and swap stake router (#59)
Browse files Browse the repository at this point in the history
* update Euphrates

* add DropAndSwapStakeRouter
  • Loading branch information
wangjj9219 authored Sep 2, 2024
1 parent 27de5b6 commit 9f2a39e
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/Euphrates
Submodule Euphrates updated 44 files
+3 −0 .gitmodules
+10 −1 README.md
+ audit/PeckShield-Audit-Report-EuphratesV2-v1.0.pdf
+12 −0 docs/DataTypes.md
+0 −64 docs/ERC1967Proxy.md
+0 −64 docs/ERC1967Upgrade.md
+0 −32 docs/IBeacon.md
+0 −32 docs/IERC1822Proxiable.md
+0 −64 docs/IERC1967.md
+186 −0 docs/IERC20.md
+76 −0 docs/IERC20Permit.md
+713 −0 docs/ILendingPool.md
+501 −0 docs/ILendingPoolAddressesProvider.md
+0 −150 docs/ITransparentUpgradeableProxy.md
+128 −0 docs/LendingPoolDepositConvertor.md
+73 −0 docs/LendingPoolStakeUtil.md
+0 −12 docs/Proxy.md
+12 −0 docs/ReentrancyGuard.md
+12 −0 docs/SafeERC20.md
+12 −0 docs/SafeMath.md
+1 −1 docs/StableAssetStakeUtil.md
+0 −12 docs/StorageSlot.md
+0 −64 docs/TransparentUpgradeableProxy.md
+713 −0 docs/starlay-protocol/contracts/interfaces/ILendingPool.md
+501 −0 docs/starlay-protocol/contracts/interfaces/ILendingPoolAddressesProvider.md
+12 −0 docs/starlay-protocol/contracts/protocol/libraries/types/DataTypes.md
+1 −0 foundry.toml
+1 −0 lib/starlay-protocol
+2 −1 remappings.txt
+145 −0 src/DEXStakeUtil.sol
+8 −26 src/DOT2WTDOTConvertor.sol
+13 −40 src/LCDOT2WTDOTConvertor.sol
+71 −0 src/LendingPoolDepositConvertor.sol
+58 −0 src/LendingPoolStakeUtil.sol
+2 −1 src/StableAssetStakeUtil.sol
+0 −883 src/TransparentUpgradeableProxy.t.sol
+3 −1 src/UpgradeableStakingLST.sol
+1 −1 src/UpgradeableStakingLSTV2.sol
+214 −0 test/DEXStakeUtil.t.sol
+107 −0 test/LendingPoolDepositConvertor.t.sol
+156 −0 test/LendingPoolStakeUtil.t.sol
+202 −0 test/MockDEX.sol
+160 −0 test/MockLendingPool.sol
+1 −10 test/StableAssetStakeUtil.t.sol
78 changes: 78 additions & 0 deletions src/DropAndSwapStakeFactory.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { FeeRegistry } from "./FeeRegistry.sol";
import { DropAndSwapStakeRouter, DropAndSwapStakeInstructions } from "./DropAndSwapStakeRouter.sol";

contract DropAndSwapStakeFactory {
using SafeTransferLib for ERC20;

function deployDropAndSwapStakeRouter(
FeeRegistry fees,
DropAndSwapStakeInstructions memory inst,
uint256 dropAmount
) public returns (DropAndSwapStakeRouter) {
require(
inst.recipient != address(0) && inst.feeReceiver != address(0) && address(inst.dropToken) != address(0)
&& inst.euphrates != address(0),
"invalid inst"
);

// no need to use salt as we want to keep the router address the same for the same fees &instructions
bytes32 salt;

DropAndSwapStakeRouter router;
try new DropAndSwapStakeRouter{salt: salt}(fees, inst) returns (DropAndSwapStakeRouter router_) {
router = router_;
} catch {
router = DropAndSwapStakeRouter(
address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
salt,
keccak256(
abi.encodePacked(
type(DropAndSwapStakeRouter).creationCode, abi.encode(fees, inst)
)
)
)
)
)
)
)
);
}

if (dropAmount > 0) {
inst.dropToken.safeTransferFrom(msg.sender, address(router), dropAmount);
}

return router;
}

function deployDropAndSwapStakeRouterAndRoute(
FeeRegistry fees,
DropAndSwapStakeInstructions memory inst,
ERC20 token,
uint256 dropAmount
) public {
DropAndSwapStakeRouter router = deployDropAndSwapStakeRouter(fees, inst, dropAmount);
router.route(token, msg.sender);
}

function deployDropAndSwapStakeRouterAndRouteNoFee(
FeeRegistry fees,
DropAndSwapStakeInstructions memory inst,
ERC20 token,
uint256 dropAmount
) public {
DropAndSwapStakeRouter router = deployDropAndSwapStakeRouter(fees, inst, dropAmount);
router.routeNoFee(token);
}
}
92 changes: 92 additions & 0 deletions src/DropAndSwapStakeRouter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.17;

import { ERC20 } from "solmate/tokens/ERC20.sol";
import { SafeTransferLib } from "solmate/utils/SafeTransferLib.sol";
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
import { IDEX } from "@acala-network/contracts/dex/IDEX.sol";
import { IStakingTo } from "euphrates/IStaking.sol";

import { BaseRouter } from "./BaseRouter.sol";
import { FeeRegistry } from "./FeeRegistry.sol";

struct DropAndSwapStakeInstructions {
address recipient;
uint256 dropFee;
address feeReceiver;
ERC20 dropToken;
address dex;
uint256 swapAmount;
address[] path;
address euphrates;
uint256 poolId;
uint256 minShareAmount;
}

contract DropAndSwapStakeRouter is BaseRouter {
using SafeTransferLib for ERC20;

DropAndSwapStakeInstructions private _instructions;

constructor(FeeRegistry fees, DropAndSwapStakeInstructions memory instructions) BaseRouter(fees) {
_instructions = instructions;
}

function routeImpl(ERC20 token) internal override {
if (token.balanceOf(address(this)) < _instructions.dropFee) {
revert("DropAndStakeByDEXUtilRouter: cannot afford drop fee");
}
token.safeTransfer(_instructions.feeReceiver, _instructions.dropFee);

require(
_instructions.path.length >= 2 && _instructions.path[0] == address(token),
"DropAndStakeByDEXUtilRouter: invalid path"
);
address targetToken = _instructions.path[_instructions.path.length - 1];
address lpToken = IDEX(_instructions.dex).getLiquidityTokenAddress(address(token), address(targetToken));

require(lpToken != address(0), "DropAndStakeByDEXUtilRouter: liquidity pool not exist");
require(
lpToken == address(IStakingTo(_instructions.euphrates).shareTypes(_instructions.poolId)),
"DropAndStakeByDEXUtilRouter: share token is not matched the lp token"
);

token.safeApprove(_instructions.dex, _instructions.swapAmount);
bool swapResult = IDEX(_instructions.dex).swapWithExactSupply(_instructions.path, _instructions.swapAmount, 0);
require(swapResult, "DropAndStakeByDEXUtilRouter: swap failed");

// no need to approve, IDEX transfer from directly
bool addLiquidityResult = IDEX(_instructions.dex).addLiquidity(
address(token),
targetToken,
token.balanceOf(address(this)),
ERC20(targetToken).balanceOf(address(this)),
_instructions.minShareAmount
);
require(addLiquidityResult, "DropAndStakeByDEXUtilRouter: addLiquidity failed");

ERC20(lpToken).safeApprove(_instructions.euphrates, ERC20(lpToken).balanceOf(address(this)));
bool stakeResult = IStakingTo(_instructions.euphrates).stakeTo(
_instructions.poolId, ERC20(lpToken).balanceOf(address(this)), _instructions.recipient
);
require(stakeResult, "DropAndStakeByDEXUtilRouter: stake failed");

// return remain assets
token.safeTransfer(_instructions.recipient, token.balanceOf(address(this)));
ERC20(targetToken).safeTransfer(_instructions.recipient, ERC20(targetToken).balanceOf(address(this)));

// transfer all dropToken to recipient
_instructions.dropToken.safeTransfer(_instructions.recipient, _instructions.dropToken.balanceOf(address(this)));
}

function rescure(ERC20 token) public {
// transfer dropFee to feeReceiver if possible
token.safeTransfer(_instructions.feeReceiver, Math.min(_instructions.dropFee, token.balanceOf(address(this))));

// transfer it to recipient to avoid it stuck in this contract
token.safeTransfer(_instructions.recipient, token.balanceOf(address(this)));

// transfer all dropToken to recipient
_instructions.dropToken.safeTransfer(_instructions.recipient, _instructions.dropToken.balanceOf(address(this)));
}
}

0 comments on commit 9f2a39e

Please sign in to comment.