├── .gitignore ├── contracts ├── CallAccess.sol ├── Timelock.sol ├── TradeVault1.sol ├── TradeVault2.sol ├── WithdrawAccessControl.sol ├── aave │ ├── AAVEAccessControl.sol │ └── interfaces │ │ └── IAAVEPool.sol ├── kyberswap │ ├── KyberswapAccessControl.sol │ └── interfaces │ │ └── IMetaAggregationRouterV2.sol ├── uniswap │ ├── NonfungiblePositionManagerAccessControl.sol │ └── interfaces │ │ └── INonfungiblePositionManager.sol └── venus │ ├── VenusAccessControl.sol │ └── interfaces │ └── IVToken.sol └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .deps 4 | 5 | **/artifacts 6 | # Hardhat files 7 | /cache 8 | /artifacts 9 | 10 | # TypeChain files 11 | /typechain 12 | /typechain-types 13 | 14 | # solidity-coverage files 15 | /coverage 16 | /coverage.json 17 | 18 | # Hardhat Ignition default folder for deployments against a local node 19 | ignition/deployments/chain-31337 20 | package-lock.json 21 | 22 | contracts/**/artifacts 23 | -------------------------------------------------------------------------------- /contracts/CallAccess.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {AccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol"; 5 | 6 | abstract contract CallAccess is AccessControlEnumerable { 7 | 8 | //调用者角色 9 | bytes32 constant public CALL_ROLE = keccak256("CALL_ROLE"); 10 | 11 | constructor() { 12 | } 13 | 14 | function _grantCallRole (address account) internal { 15 | _grantRole(CALL_ROLE, account); 16 | } 17 | 18 | // 调用者权限 19 | modifier callable() { 20 | require(hasRole(CALL_ROLE, msg.sender), "Caller role is not set"); 21 | _; 22 | } 23 | 24 | 25 | } -------------------------------------------------------------------------------- /contracts/Timelock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/governance/TimelockController.sol"; 5 | 6 | contract Timelock is TimelockController { 7 | constructor(uint256 minDelay, address multiSignAddress) 8 | TimelockController( 9 | minDelay, 10 | _constructArray(multiSignAddress), 11 | _constructArray(multiSignAddress), 12 | address(0) 13 | ) 14 | { 15 | 16 | } 17 | 18 | // Helper function to create an array with a single address 19 | function _constructArray(address addr) 20 | private 21 | pure 22 | returns (address[] memory) 23 | { 24 | address[] memory array = new address[](1); 25 | array[0] = addr; 26 | return array; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /contracts/TradeVault1.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {VenusAccessControl} from "./venus/VenusAccessControl.sol"; 5 | import {AAVEAccessControl} from "./aave/AAVEAccessControl.sol"; 6 | import {WithdrawAccessControl} from "./WithdrawAccessControl.sol"; 7 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 8 | import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 9 | import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol"; 10 | 11 | contract TradeVault1 is 12 | VenusAccessControl, 13 | AAVEAccessControl, 14 | WithdrawAccessControl, 15 | Multicall 16 | { 17 | using SafeERC20 for IERC20; 18 | 19 | constructor(address _traderAddress) { 20 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 21 | _grantCallRole(_traderAddress); 22 | } 23 | 24 | receive() external payable { 25 | // revert("no receive ETH"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /contracts/TradeVault2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {KyberswapAccessControl} from "./kyberswap/KyberswapAccessControl.sol"; 5 | import {VenusAccessControl} from "./venus/VenusAccessControl.sol"; 6 | import {AAVEAccessControl} from "./aave/AAVEAccessControl.sol"; 7 | import {NonfungiblePositionManagerAccessControl} from "./uniswap/NonfungiblePositionManagerAccessControl.sol"; 8 | import {WithdrawAccessControl} from "./WithdrawAccessControl.sol"; 9 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 10 | import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 11 | import {Multicall} from "@openzeppelin/contracts/utils/Multicall.sol"; 12 | 13 | contract TradeVault2 is 14 | KyberswapAccessControl, 15 | VenusAccessControl, 16 | AAVEAccessControl, 17 | NonfungiblePositionManagerAccessControl, 18 | WithdrawAccessControl, 19 | Multicall 20 | { 21 | using SafeERC20 for IERC20; 22 | 23 | constructor(address _traderAddress) { 24 | _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); 25 | _grantCallRole(_traderAddress); 26 | } 27 | 28 | receive() external payable { 29 | // revert("no receive ETH"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/WithdrawAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 5 | import {IERC721Enumerable } from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; 6 | import {CallAccess} from "./CallAccess.sol"; 7 | 8 | abstract contract WithdrawAccessControl is CallAccess { 9 | using SafeERC20 for IERC20; 10 | 11 | //代币接收者角色 12 | bytes32 constant public TOKEN_RECIPIENT_ROLE = keccak256("TOKEN_RECIPIENT_ROLE"); 13 | 14 | event WithdrawFTTokenToRecipientRole(address indexed _tokenAddress, address indexed _to, uint256 _amount); 15 | event AdminWithdrawFTToken(address indexed _tokenAddress, address indexed _to, uint256 _amount); 16 | event AdminWithdrawERC721(address indexed _tokenAddress, address indexed _to, uint256[] _tokenIdList); 17 | 18 | constructor() { 19 | } 20 | 21 | function withdrawFTTokenToRecipientRole(address _tokenAddress, address _to, uint256 _amount) external callable { 22 | _checkRole(TOKEN_RECIPIENT_ROLE, _to); 23 | uint256 _realAmount; 24 | if(_tokenAddress == address(0)){ 25 | _realAmount = _withdrawETH(_to, _amount); 26 | }else { 27 | _realAmount = _withdrawERC20(_tokenAddress, _to, _amount); 28 | } 29 | emit WithdrawFTTokenToRecipientRole(_tokenAddress, _to, _realAmount); 30 | } 31 | 32 | function _withdrawERC20(address _tokenAddress, address _to, uint256 _amount) private returns(uint256 _realAmount){ 33 | IERC20 _token = IERC20(_tokenAddress); 34 | if (_amount == 0) { 35 | _amount = _token.balanceOf(address(this)); 36 | } 37 | if(_amount > 0){ 38 | _token.safeTransfer(_to, _amount); 39 | } 40 | _realAmount = _amount; 41 | } 42 | 43 | function _withdrawETH(address _to, uint256 _amount) private returns(uint256 _realAmount){ 44 | if (_amount == 0) { 45 | _amount = address(this).balance; 46 | } 47 | if(_amount > 0){ 48 | (bool _suc, ) = _to.call{gas: 23000, value: _amount}(""); 49 | require(_suc, "ETH transfer fail"); 50 | } 51 | _realAmount = _amount; 52 | } 53 | 54 | function adminWithdrawFTToken(address _tokenAddress, address _to, uint256 _amount) external onlyRole(DEFAULT_ADMIN_ROLE) { 55 | uint256 _realAmount; 56 | if(_tokenAddress == address(0)){ 57 | _realAmount = _withdrawETH(_to, _amount); 58 | }else { 59 | _realAmount = _withdrawERC20(_tokenAddress, _to, _amount); 60 | } 61 | emit AdminWithdrawFTToken(_tokenAddress, _to, _realAmount); 62 | } 63 | 64 | function adminWithdrawERC721 (address _tokenAddress, address _to, uint256[] calldata _tokenIdList) external onlyRole(DEFAULT_ADMIN_ROLE) { 65 | IERC721Enumerable _token = IERC721Enumerable(_tokenAddress); 66 | uint256[] memory _transferTokenIdList = _tokenIdList; 67 | if (_tokenIdList.length == 0){ 68 | uint256 _bal = _token.balanceOf(address(this)); 69 | _transferTokenIdList = new uint256[](_bal); 70 | for (uint i = 0; i < _bal; i++){ 71 | _transferTokenIdList[i] = _token.tokenOfOwnerByIndex(address(this), i); 72 | } 73 | } 74 | for (uint i = 0; i < _transferTokenIdList.length; i++){ 75 | _token.safeTransferFrom(address(this), _to, _transferTokenIdList[i]); 76 | } 77 | emit AdminWithdrawERC721(_tokenAddress, _to, _transferTokenIdList); 78 | } 79 | 80 | 81 | } -------------------------------------------------------------------------------- /contracts/aave/AAVEAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {IAAVEPool} from "./interfaces/IAAVEPool.sol"; 5 | import {CallAccess} from "../CallAccess.sol"; 6 | import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | abstract contract AAVEAccessControl is CallAccess { 10 | using EnumerableSet for EnumerableSet.AddressSet; 11 | 12 | IAAVEPool constant aavePool = 13 | IAAVEPool(0x6807dc923806fE8Fd134338EABCA509979a7e0cB); 14 | // 支持的代币地址列表 15 | EnumerableSet.AddressSet aaveTokenList; 16 | 17 | error notAllowAAVEToken(address); 18 | 19 | constructor() { 20 | } 21 | 22 | function batchAddAAVEToken (address[] calldata tokens) external onlyRole(DEFAULT_ADMIN_ROLE) { 23 | for (uint256 i = 0; i < tokens.length; i++) { 24 | _addAAVEToken(tokens[i]); 25 | } 26 | } 27 | 28 | function _addAAVEToken(address token) internal { 29 | require(!aaveTokenList.contains(token), "already exist"); 30 | bool addSuc = aaveTokenList.add(token); 31 | require(addSuc, "add token fail"); 32 | // 授权代币合约 33 | IERC20(token).approve(address(aavePool), type(uint).max); 34 | } 35 | 36 | function batchRemoveAAVEToken(address[] calldata tokens) external onlyRole(DEFAULT_ADMIN_ROLE) { 37 | for (uint256 i = 0; i < tokens.length; i++) { 38 | _removeAAVEToken(tokens[i]); 39 | } 40 | } 41 | 42 | function _removeAAVEToken(address token) internal { 43 | require(aaveTokenList.contains(token), "not exist"); 44 | bool removeSuc = aaveTokenList.remove(token); 45 | require(removeSuc, "remove token fail"); 46 | // 取消授权代币合约 47 | IERC20(token).approve(address(aavePool), 0); 48 | } 49 | 50 | function getAAVETokenList() public view returns (address[] memory) { 51 | return aaveTokenList.values(); 52 | } 53 | 54 | function getAAVEokenListLength() public view returns (uint256) { 55 | return aaveTokenList.length(); 56 | } 57 | 58 | function getAAVETokenAt(uint256 index) public view returns (address) { 59 | return aaveTokenList.at(index); 60 | } 61 | 62 | function containsAAVEToken(address token) external view returns (bool) { 63 | return aaveTokenList.contains(token); 64 | } 65 | 66 | function requireAllowAAVEToken(address _token) public view { 67 | if (!aaveTokenList.contains(_token)) revert notAllowAAVEToken(_token); 68 | } 69 | 70 | function aaveSupply( 71 | address asset, 72 | uint256 amount 73 | ) external callable { 74 | // 检查是否支持代币 75 | requireAllowAAVEToken(asset); 76 | aavePool.supply(asset, amount, address(this), 0); 77 | } 78 | 79 | function aaveWithdraw( 80 | address asset, 81 | uint256 amount 82 | ) external callable returns (uint256) { 83 | // 检查是否支持代币 84 | requireAllowAAVEToken(asset); 85 | return aavePool.withdraw(asset, amount, address(this)); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /contracts/aave/interfaces/IAAVEPool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IAAVEPool { 5 | function supply( 6 | address asset, 7 | uint256 amount, 8 | address onBehalfOf, 9 | uint16 referralCode 10 | ) external; 11 | 12 | function withdraw( 13 | address asset, 14 | uint256 amount, 15 | address to 16 | ) external returns (uint256); 17 | } 18 | -------------------------------------------------------------------------------- /contracts/kyberswap/KyberswapAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {IMetaAggregationRouterV2} from "./interfaces/IMetaAggregationRouterV2.sol"; 5 | import {CallAccess} from "../CallAccess.sol"; 6 | import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | abstract contract KyberswapAccessControl is CallAccess { 10 | using EnumerableSet for EnumerableSet.AddressSet; 11 | 12 | IMetaAggregationRouterV2 constant metaAggregationRouterV2 = 13 | IMetaAggregationRouterV2(0x6131B5fae19EA4f9D964eAc0408E4408b66337b5); 14 | // 支持的代币地址列表 15 | EnumerableSet.AddressSet swapTokenList; 16 | 17 | error notAllowSwapToken(address); 18 | 19 | constructor() { 20 | } 21 | 22 | function batchAddSwapToken (address[] calldata tokens) external onlyRole(DEFAULT_ADMIN_ROLE) { 23 | for (uint256 i = 0; i < tokens.length; i++) { 24 | _addSwapToken(tokens[i]); 25 | } 26 | } 27 | 28 | function _addSwapToken(address token) internal { 29 | require(!swapTokenList.contains(token), "already exist"); 30 | bool addSuc = swapTokenList.add(token); 31 | require(addSuc, "add token fail"); 32 | // 授权代币合约 33 | IERC20(token).approve(address(metaAggregationRouterV2), type(uint).max); 34 | } 35 | 36 | function batchRemoveSwapToken(address[] calldata tokens) external onlyRole(DEFAULT_ADMIN_ROLE) { 37 | for (uint256 i = 0; i < tokens.length; i++) { 38 | _removeSwapToken(tokens[i]); 39 | } 40 | } 41 | 42 | function _removeSwapToken(address token) internal { 43 | require(swapTokenList.contains(token), "not exist"); 44 | bool removeSuc = swapTokenList.remove(token); 45 | require(removeSuc, "remove token fail"); 46 | // 取消授权代币合约 47 | IERC20(token).approve(address(metaAggregationRouterV2), 0); 48 | } 49 | 50 | function getSwapTokenList() public view returns (address[] memory) { 51 | return swapTokenList.values(); 52 | } 53 | 54 | function getSwapTokenListLength() public view returns (uint256) { 55 | return swapTokenList.length(); 56 | } 57 | 58 | function getSwapTokenAt(uint256 index) public view returns (address) { 59 | return swapTokenList.at(index); 60 | } 61 | 62 | function containsSwapToken(address token) external view returns (bool) { 63 | return swapTokenList.contains(token); 64 | } 65 | 66 | function requireAllowSwapToken(address _token) public view { 67 | if (!swapTokenList.contains(_token)) revert notAllowSwapToken(_token); 68 | } 69 | 70 | function swap( 71 | IMetaAggregationRouterV2.SwapExecutionParams calldata execution 72 | ) external callable returns (uint256 returnAmount, uint256 gasUsed) { 73 | // 检查是否支持代币 74 | requireAllowSwapToken(address(execution.desc.srcToken)); 75 | requireAllowSwapToken(address(execution.desc.dstToken)); 76 | require(execution.desc.dstReceiver == address(this), "invalid dstReceiver"); 77 | 78 | return metaAggregationRouterV2.swap(execution); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /contracts/kyberswap/interfaces/IMetaAggregationRouterV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IAggregationExecutor { 7 | function callBytes(bytes calldata data) external payable; // 0xd9c45357 8 | 9 | // callbytes per swap sequence 10 | function swapSingleSequence(bytes calldata data) external; 11 | 12 | function finalTransactionProcessing( 13 | address tokenIn, 14 | address tokenOut, 15 | address to, 16 | bytes calldata destTokenFeeData 17 | ) external; 18 | } 19 | 20 | interface IAggregationExecutor1Inch { 21 | function callBytes(address msgSender, bytes calldata data) external payable; // 0x2636f7f8 22 | } 23 | 24 | interface IAggregationRouter1InchV4 { 25 | function swap( 26 | IAggregationExecutor1Inch caller, 27 | SwapDescription1Inch calldata desc, 28 | bytes calldata data 29 | ) external payable returns (uint256 returnAmount, uint256 gasLeft); 30 | } 31 | 32 | struct SwapDescription1Inch { 33 | IERC20 srcToken; 34 | IERC20 dstToken; 35 | address payable srcReceiver; 36 | address payable dstReceiver; 37 | uint256 amount; 38 | uint256 minReturnAmount; 39 | uint256 flags; 40 | bytes permit; 41 | } 42 | 43 | struct SwapDescriptionExecutor1Inch { 44 | IERC20 srcToken; 45 | IERC20 dstToken; 46 | address payable srcReceiver1Inch; 47 | address payable dstReceiver; 48 | address[] srcReceivers; 49 | uint256[] srcAmounts; 50 | uint256 amount; 51 | uint256 minReturnAmount; 52 | uint256 flags; 53 | bytes permit; 54 | } 55 | 56 | interface IMetaAggregationRouterV2 { 57 | 58 | struct SwapDescriptionV2 { 59 | IERC20 srcToken; 60 | IERC20 dstToken; 61 | address[] srcReceivers; // transfer src token to these addresses, default 62 | uint256[] srcAmounts; 63 | address[] feeReceivers; 64 | uint256[] feeAmounts; 65 | address dstReceiver; 66 | uint256 amount; 67 | uint256 minReturnAmount; 68 | uint256 flags; 69 | bytes permit; 70 | } 71 | 72 | /// @dev use for swapGeneric and swap to avoid stack too deep 73 | struct SwapExecutionParams { 74 | address callTarget; // call this address 75 | address approveTarget; // approve this address if _APPROVE_FUND set 76 | bytes targetData; 77 | SwapDescriptionV2 desc; 78 | bytes clientData; 79 | } 80 | 81 | struct SimpleSwapData { 82 | address[] firstPools; 83 | uint256[] firstSwapAmounts; 84 | bytes[] swapDatas; 85 | uint256 deadline; 86 | bytes destTokenFeeData; 87 | } 88 | 89 | function swapGeneric(SwapExecutionParams calldata execution) 90 | external 91 | payable 92 | returns (uint256 returnAmount, uint256 gasUsed); 93 | 94 | function swap(SwapExecutionParams calldata execution) 95 | external 96 | payable 97 | returns (uint256 returnAmount, uint256 gasUsed); 98 | function swapSimpleMode( 99 | IAggregationExecutor caller, 100 | SwapDescriptionV2 memory desc, 101 | bytes calldata executorData, 102 | bytes calldata clientData 103 | ) external returns (uint256 returnAmount, uint256 gasUsed); 104 | } -------------------------------------------------------------------------------- /contracts/uniswap/NonfungiblePositionManagerAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {INonfungiblePositionManager} from "./interfaces/INonfungiblePositionManager.sol"; 5 | import {CallAccess} from "../CallAccess.sol"; 6 | import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol"; 9 | 10 | abstract contract NonfungiblePositionManagerAccessControl is 11 | CallAccess, 12 | ERC721Holder 13 | { 14 | using EnumerableSet for EnumerableSet.AddressSet; 15 | 16 | INonfungiblePositionManager constant nonfungiblePositionManager = 17 | INonfungiblePositionManager(0x46A15B0b27311cedF172AB29E4f4766fbE7F4364); 18 | // 支持的代币地址列表 19 | EnumerableSet.AddressSet lpTokenList; 20 | 21 | error notAllowLPToken(address); 22 | 23 | constructor() {} 24 | 25 | function batchAddLPToken(address[] calldata tokens) 26 | external 27 | onlyRole(DEFAULT_ADMIN_ROLE) 28 | { 29 | for (uint256 i = 0; i < tokens.length; i++) { 30 | _addLPToken(tokens[i]); 31 | } 32 | } 33 | 34 | function _addLPToken(address token) internal { 35 | require(!lpTokenList.contains(token), "already exist"); 36 | bool addSuc = lpTokenList.add(token); 37 | require(addSuc, "add token fail"); 38 | // 授权代币合约 39 | IERC20(token).approve( 40 | address(nonfungiblePositionManager), 41 | type(uint256).max 42 | ); 43 | } 44 | 45 | function batchRemoveLPToken(address[] calldata tokens) 46 | external 47 | onlyRole(DEFAULT_ADMIN_ROLE) 48 | { 49 | for (uint256 i = 0; i < tokens.length; i++) { 50 | _removeLPToken(tokens[i]); 51 | } 52 | } 53 | 54 | function _removeLPToken(address token) internal { 55 | require(lpTokenList.contains(token), "not exist"); 56 | bool removeSuc = lpTokenList.remove(token); 57 | require(removeSuc, "remove token fail"); 58 | // 取消授权代币合约 59 | IERC20(token).approve(address(nonfungiblePositionManager), 0); 60 | } 61 | 62 | function getLPTokenList() public view returns (address[] memory) { 63 | return lpTokenList.values(); 64 | } 65 | 66 | function getLPTokenListLength() public view returns (uint256) { 67 | return lpTokenList.length(); 68 | } 69 | 70 | function getLPTokenAt(uint256 index) public view returns (address) { 71 | return lpTokenList.at(index); 72 | } 73 | 74 | function containsLPToken(address token) external view returns (bool) { 75 | return lpTokenList.contains(token); 76 | } 77 | 78 | function requireAllowLPToken(address _token) public view { 79 | if (!lpTokenList.contains(_token)) revert notAllowLPToken(_token); 80 | } 81 | 82 | function uniV3LPMint( 83 | INonfungiblePositionManager.MintParams calldata mintParams 84 | ) 85 | external 86 | callable 87 | returns ( 88 | uint256 tokenId, 89 | uint128 liquidity, 90 | uint256 amount0, 91 | uint256 amount1 92 | ) 93 | { 94 | // 检查是否支持代币 95 | requireAllowLPToken(mintParams.token0); 96 | requireAllowLPToken(mintParams.token1); 97 | require(mintParams.recipient == address(this), "invalid recipient"); 98 | return nonfungiblePositionManager.mint(mintParams); 99 | } 100 | 101 | function uniV3LPDecrease( 102 | INonfungiblePositionManager.DecreaseLiquidityParams 103 | calldata decreaseParams 104 | ) external callable returns (uint256 amount0, uint256 amount1) { 105 | return nonfungiblePositionManager.decreaseLiquidity(decreaseParams); 106 | } 107 | 108 | function uniV3LPCollect( 109 | INonfungiblePositionManager.CollectParams calldata params 110 | ) external callable returns (uint256 amount0, uint256 amount1) { 111 | require(params.recipient == address(this), "invalid recipient"); 112 | return nonfungiblePositionManager.collect(params); 113 | } 114 | 115 | function uniV3LPNFTBurn(uint256 tokenId) external callable { 116 | nonfungiblePositionManager.burn(tokenId); 117 | } 118 | 119 | function unwrapWETH9(uint256 amountMinimum, address recipient) external callable { 120 | require(recipient == address(this), "invalid recipient"); 121 | nonfungiblePositionManager.unwrapWETH9(amountMinimum, recipient); 122 | } 123 | 124 | function refundETH() external callable { 125 | nonfungiblePositionManager.refundETH(); 126 | } 127 | 128 | function sweepToken( 129 | address token, 130 | uint256 amountMinimum, 131 | address recipient 132 | ) external callable { 133 | requireAllowLPToken(token); 134 | require(recipient == address(this), "invalid recipient"); 135 | nonfungiblePositionManager.sweepToken(token, amountMinimum, recipient); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /contracts/uniswap/interfaces/INonfungiblePositionManager.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface INonfungiblePositionManager { 5 | struct MintParams { 6 | address token0; 7 | address token1; 8 | uint24 fee; 9 | int24 tickLower; 10 | int24 tickUpper; 11 | uint256 amount0Desired; 12 | uint256 amount1Desired; 13 | uint256 amount0Min; 14 | uint256 amount1Min; 15 | address recipient; 16 | uint256 deadline; 17 | } 18 | struct IncreaseLiquidityParams { 19 | uint256 tokenId; 20 | uint256 amount0Desired; 21 | uint256 amount1Desired; 22 | uint256 amount0Min; 23 | uint256 amount1Min; 24 | uint256 deadline; 25 | } 26 | struct DecreaseLiquidityParams { 27 | uint256 tokenId; 28 | uint128 liquidity; 29 | uint256 amount0Min; 30 | uint256 amount1Min; 31 | uint256 deadline; 32 | } 33 | struct CollectParams { 34 | uint256 tokenId; 35 | address recipient; 36 | uint128 amount0Max; 37 | uint128 amount1Max; 38 | } 39 | 40 | function factory() view external returns(address); 41 | function positions(uint256 tokenId) 42 | external 43 | view 44 | returns ( 45 | uint96 nonce, 46 | address operator, 47 | address token0, 48 | address token1, 49 | uint24 fee, 50 | int24 tickLower, 51 | int24 tickUpper, 52 | uint128 liquidity, 53 | uint256 feeGrowthInside0LastX128, 54 | uint256 feeGrowthInside1LastX128, 55 | uint128 tokensOwed0, 56 | uint128 tokensOwed1 57 | ); 58 | function mint(MintParams calldata params) 59 | external 60 | payable 61 | returns ( 62 | uint256 tokenId, 63 | uint128 liquidity, 64 | uint256 amount0, 65 | uint256 amount1 66 | ); 67 | function increaseLiquidity(IncreaseLiquidityParams calldata params) 68 | external 69 | payable 70 | returns ( 71 | uint128 liquidity, 72 | uint256 amount0, 73 | uint256 amount1 74 | ); 75 | function decreaseLiquidity(DecreaseLiquidityParams calldata params) 76 | external 77 | payable 78 | returns (uint256 amount0, uint256 amount1); 79 | function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1); 80 | function burn(uint256 tokenId) external payable; 81 | /// @notice Unwraps the contract's WETH9 balance and sends it to recipient as ETH. 82 | /// @dev The amountMinimum parameter prevents malicious contracts from stealing WETH9 from users. 83 | /// @param amountMinimum The minimum amount of WETH9 to unwrap 84 | /// @param recipient The address receiving ETH 85 | function unwrapWETH9(uint256 amountMinimum, address recipient) external payable; 86 | 87 | /// @notice Refunds any ETH balance held by this contract to the `msg.sender` 88 | /// @dev Useful for bundling with mint or increase liquidity that uses ether, or exact output swaps 89 | /// that use ether for the input amount. And in PancakeSwap Router, this would be called 90 | /// at the very end of swap 91 | function refundETH() external payable; 92 | 93 | /// @notice Transfers the full amount of a token held by this contract to recipient 94 | /// @dev The amountMinimum parameter prevents malicious contracts from stealing the token from users 95 | /// @param token The contract address of the token which will be transferred to `recipient` 96 | /// @param amountMinimum The minimum amount of token required for a transfer 97 | /// @param recipient The destination address of the token 98 | function sweepToken( 99 | address token, 100 | uint256 amountMinimum, 101 | address recipient 102 | ) external payable; 103 | } -------------------------------------------------------------------------------- /contracts/venus/VenusAccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import {IVToken} from "./interfaces/IVToken.sol"; 5 | import {CallAccess} from "../CallAccess.sol"; 6 | import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; 7 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 8 | 9 | abstract contract VenusAccessControl is CallAccess { 10 | using EnumerableSet for EnumerableSet.AddressSet; 11 | // 支持的vToken地址列表 12 | EnumerableSet.AddressSet vTokenList; 13 | 14 | error notAllowVToken(address); 15 | error notAllowFunction(bytes4); 16 | 17 | constructor() {} 18 | 19 | function batchAddVToken(address[] calldata vtokens) 20 | external 21 | onlyRole(DEFAULT_ADMIN_ROLE) 22 | { 23 | for (uint256 i = 0; i < vtokens.length; i++) { 24 | _addVToken(vtokens[i]); 25 | } 26 | } 27 | 28 | function _addVToken(address vtoken) internal { 29 | require(!vTokenList.contains(vtoken), "already exist"); 30 | bool addSuc = vTokenList.add(vtoken); 31 | require(addSuc, "add token fail"); 32 | // 授权代币合约 33 | IVToken vtokenContract = IVToken(vtoken); 34 | require(vtokenContract.isVToken(), "invalid vToken"); 35 | address baseToken = vtokenContract.underlying(); 36 | IERC20(baseToken).approve(vtoken, type(uint256).max); 37 | } 38 | 39 | function batchRemoveVToken(address[] calldata vtokens) 40 | external 41 | onlyRole(DEFAULT_ADMIN_ROLE) 42 | { 43 | for (uint256 i = 0; i < vtokens.length; i++) { 44 | _removeVToken(vtokens[i]); 45 | } 46 | } 47 | 48 | function _removeVToken(address vtoken) internal { 49 | require(vTokenList.contains(vtoken), "not exist"); 50 | bool removeSuc = vTokenList.remove(vtoken); 51 | require(removeSuc, "remove token fail"); 52 | // 取消授权代币合约 53 | IVToken vtokenContract = IVToken(vtoken); 54 | address baseToken = vtokenContract.underlying(); 55 | IERC20(baseToken).approve(vtoken, 0); 56 | } 57 | 58 | function getVTokenList() public view returns (address[] memory) { 59 | return vTokenList.values(); 60 | } 61 | 62 | function getVTokenListLength() public view returns (uint256) { 63 | return vTokenList.length(); 64 | } 65 | 66 | function getVTokenAt(uint256 index) public view returns (address) { 67 | return vTokenList.at(index); 68 | } 69 | 70 | function containsVToken(address _vtoken) external view returns (bool) { 71 | return vTokenList.contains(_vtoken); 72 | } 73 | 74 | function requireAllowVToken(address _vtoken) public view { 75 | if (!vTokenList.contains(_vtoken)) revert notAllowVToken(_vtoken); 76 | } 77 | 78 | function requireAllowVenusFunction(bytes4 functionSelector) public pure { 79 | if ( 80 | !(functionSelector == IVToken.mint.selector || 81 | functionSelector == IVToken.redeem.selector || 82 | functionSelector == IVToken.redeemUnderlying.selector) 83 | ) { 84 | revert notAllowFunction(functionSelector); 85 | } 86 | } 87 | 88 | function callVenus(address vTokenAddress, bytes calldata data) 89 | external 90 | callable 91 | returns (bytes memory result) 92 | { 93 | requireAllowVToken(vTokenAddress); 94 | requireAllowVenusFunction(bytes4(data[0:4])); 95 | bool _succ; 96 | (_succ, result) = vTokenAddress.call(data); 97 | require(_succ, "Call failed"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/venus/interfaces/IVToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | interface IVToken { 5 | 6 | /*** User Interface ***/ 7 | 8 | function mint(uint mintAmount) external returns (uint); 9 | function redeem(uint redeemTokens) external returns (uint); 10 | function redeemUnderlying(uint redeemAmount) external returns (uint); 11 | function borrow(uint borrowAmount) external returns (uint); 12 | function repayBorrow(uint repayAmount) external returns (uint); 13 | function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); 14 | 15 | 16 | /*** Admin Functions ***/ 17 | 18 | function _addReserves(uint addAmount) external returns (uint); 19 | 20 | function isVToken() external view returns(bool); 21 | function underlying() external view returns(address); 22 | } 23 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # MiSon Protocol 2 | 3 | 4 | 5 | MiSon Protocol is a decentralized digital asset intelligent aggregation service protocol running on public blockchains such as Ethereum, Binance Chain, and Solana. 6 | 7 | 8 | It maximizes the use of existing third-party applications in the underlying DeFi field. 9 | 10 | Through its unique "AI Intelligent Strategy Core Component," it intelligently allocates funds among applications such as DEX trading arbitrage, lending contracts, and liquidity provisioning. 11 | 12 | 13 | 14 | 15 | It achieves intelligent distribution and asset interaction, obtaining the most optimal way to execute transactions and significantly improving the efficiency of fund utilization while avoiding risks and impermanent losses. 16 | 17 | Through innovative Token finance models, it creates "insurance protocol" service contracts that can resist 99.9% of users' risk losses in extremely special market conditions, fully realizing the value of expected yield and generating additional returns for users! 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | --------------------------------------------------------------------------------