diff --git a/contracts/adapters/dehive/ClusterTokenAdapter.sol b/contracts/adapters/dehive/ClusterTokenAdapter.sol new file mode 100644 index 00000000..d8fc2bac --- /dev/null +++ b/contracts/adapters/dehive/ClusterTokenAdapter.sol @@ -0,0 +1,49 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAdapter } from "../TokenAdapter.sol"; +import { Component } from "../../shared/Structs.sol"; +import { IClusterToken } from "../../interfaces/IClusterToken.sol"; + +/** + * @title Token adapter for ClusterTokens. + * @dev Implementation of TokenAdapter abstract contract. + */ +contract ClusterTokenAdapter is TokenAdapter { + /** + * @return Array of Component structs with underlying tokens rates for the given token. + * @dev Implementation of TokenAdapter abstract contract function. + * @param token Cluster address + */ + function getComponents(address token) external override view returns (Component[] memory) { + address[] memory underlyings = IClusterToken(token).getUnderlyings(); + uint256[] memory underlyingsShares = IClusterToken(token).getUnderlyingInCluster(); + + Component[] memory components = new Component[](underlyings.length); + for(uint256 i = 0; i < underlyings.length; i++) { + components[i] = Component({ + token: underlyings[i], + rate: int256(underlyingsShares[i] * 1e18 / 1e6) + }); + } + + return components; + } +} diff --git a/contracts/adapters/dehive/DeHiveProtocolAdapter.sol b/contracts/adapters/dehive/DeHiveProtocolAdapter.sol new file mode 100644 index 00000000..2a85d326 --- /dev/null +++ b/contracts/adapters/dehive/DeHiveProtocolAdapter.sol @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { ProtocolAdapter } from "../ProtocolAdapter.sol"; + +/** + * @title Cluster adapter for DeHive protocol. + * @dev Implementation of ProtocolAdapter abstract contract. + */ +contract DeHiveProtocolAdapter is ProtocolAdapter { + /** + * @dev MUST return amount and type of the given token + * locked on the protocol by the given account. + */ + function getBalance(address token, address account) public override view returns (int256) { + return int256(ERC20(token).balanceOf(account)); + } +} \ No newline at end of file diff --git a/contracts/adapters/dehive/StakingDHVAdapter.sol b/contracts/adapters/dehive/StakingDHVAdapter.sol new file mode 100644 index 00000000..60019b39 --- /dev/null +++ b/contracts/adapters/dehive/StakingDHVAdapter.sol @@ -0,0 +1,51 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { ProtocolAdapter } from "../ProtocolAdapter.sol"; +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title DHV Staking adapter for DeHive protocol. + * @dev Implementation of ProtocolAdapter abstract contract. + */ +contract StakingDHVAdapter is ProtocolAdapter { + /** + * @dev MUST return amount and type of the given token + * locked on the protocol by the given account. + * @param token DHV address + * @param account User's account + */ + function getBalance(address token, address account) public override view returns (int256) { + address STAKING_DHV_ETH = address(0x04595f9010F79422a9b411ef963e4dd1F7107704); + address DHV_TOKEN_ETH = address(0x62Dc4817588d53a056cBbD18231d91ffCcd34b2A); + + address STAKING_DHV_POLY = address(0x88cFC1bc9aEb80f6C8f5d310d6C3761c2a646Df7); + address DHV_TOKEN_POLY = address(0x5fCB9de282Af6122ce3518CDe28B7089c9F97b26); + + uint256 amount = 0; + if (token == DHV_TOKEN_ETH) { + amount = IStakingPools(STAKING_DHV_ETH).userInfo(0, account); + } + else if (token == DHV_TOKEN_POLY) { + amount = IStakingPools(STAKING_DHV_POLY).userInfo(0, account); + } + return int256(amount); + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterBSC.sol b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterBSC.sol new file mode 100644 index 00000000..13da055f --- /dev/null +++ b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterBSC.sol @@ -0,0 +1,103 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { DeHiveProtocolAdapter } from "../../adapters/dehive/DeHiveProtocolAdapter.sol"; + +import { IExternalAdapter } from "../../interfaces/IExternalAdapter.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract ClusterTokenInteractiveAdapterBSC is InteractiveAdapter, DeHiveProtocolAdapter { + using SafeERC20 for ERC20; + + address internal constant EXTERNAL_ADAPTER = address(0x92450c9Dc4c709F4169f9196E908772744D89C8c); + /** + * @notice Deposits tokens to the DeHive ClusterToken. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ETH address, ETH amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - clusterToken - ClusterToken address. + * @return tokensToBeWithdrawn Array with two elements - ETH and ClusterToken address. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be one token[1]"); + require(tokenAmounts[0].token == ETH, "DeHive: should be ETH[2]"); + + address clusterToken = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](2); + tokensToBeWithdrawn[0] = clusterToken; + tokensToBeWithdrawn[1] = ETH; + + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).deposit{value: amount}(clusterToken) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive protocol. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ClusterToken address, ClusterToken amount to be redeemed, and amount type. + * @return tokensToBeWithdrawn Array with one element - ETH address. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be 1 tokenAmount[3]"); + + address clusterToken = tokenAmounts[0].token; + uint256 amount = getAbsoluteAmountWithdraw(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](1); + tokensToBeWithdrawn[0] = ETH; + + ERC20(clusterToken).safeApprove(EXTERNAL_ADAPTER, amount, "DeHive"); + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).withdraw(clusterToken, amount) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: withdraw fail"); + } + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterETH.sol b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterETH.sol new file mode 100644 index 00000000..43bd20dc --- /dev/null +++ b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterETH.sol @@ -0,0 +1,103 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { DeHiveProtocolAdapter } from "../../adapters/dehive/DeHiveProtocolAdapter.sol"; + +import { IExternalAdapter } from "../../interfaces/IExternalAdapter.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract ClusterTokenInteractiveAdapterETH is InteractiveAdapter, DeHiveProtocolAdapter { + using SafeERC20 for ERC20; + + address internal constant EXTERNAL_ADAPTER = address(0xE45713a2D7d87bd7A55d84da55aEb9EC21067870); + /** + * @notice Deposits tokens to the DeHive ClusterToken. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ETH address, ETH amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - clusterToken - ClusterToken address. + * @return tokensToBeWithdrawn Array with two elements - ETH and ClusterToken address. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be one token[1]"); + require(tokenAmounts[0].token == ETH, "DeHive: should be ETH[2]"); + + address clusterToken = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](2); + tokensToBeWithdrawn[0] = clusterToken; + tokensToBeWithdrawn[1] = ETH; + + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).deposit{value: amount}(clusterToken) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive protocol. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ClusterToken address, ClusterToken amount to be redeemed, and amount type. + * @return tokensToBeWithdrawn Array with one element - ETH address. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be 1 tokenAmount[3]"); + + address clusterToken = tokenAmounts[0].token; + uint256 amount = getAbsoluteAmountWithdraw(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](1); + tokensToBeWithdrawn[0] = ETH; + + ERC20(clusterToken).safeApprove(EXTERNAL_ADAPTER, amount, "DeHive"); + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).withdraw(clusterToken, amount) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: withdraw fail"); + } + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterPoly.sol b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterPoly.sol new file mode 100644 index 00000000..be3da0eb --- /dev/null +++ b/contracts/interactiveAdapters/dehive/ClusterTokenInteractiveAdapterPoly.sol @@ -0,0 +1,103 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { DeHiveProtocolAdapter } from "../../adapters/dehive/DeHiveProtocolAdapter.sol"; + +import { IExternalAdapter } from "../../interfaces/IExternalAdapter.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract ClusterTokenInteractiveAdapterPoly is InteractiveAdapter, DeHiveProtocolAdapter { + using SafeERC20 for ERC20; + + address internal constant EXTERNAL_ADAPTER = address(0xaf63F200148fe942280C10220aCD0780006BBA91); + /** + * @notice Deposits tokens to the DeHive ClusterToken. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ETH address, ETH amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - clusterToken - ClusterToken address. + * @return tokensToBeWithdrawn Array with two elements - ETH and ClusterToken address. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be one token[1]"); + require(tokenAmounts[0].token == ETH, "DeHive: should be ETH[2]"); + + address clusterToken = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](2); + tokensToBeWithdrawn[0] = clusterToken; + tokensToBeWithdrawn[1] = ETH; + + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).deposit{value: amount}(clusterToken) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive protocol. + * @param tokenAmounts Array with one element - TokenAmount struct with + * ClusterToken address, ClusterToken amount to be redeemed, and amount type. + * @return tokensToBeWithdrawn Array with one element - ETH address. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "DeHive: should be 1 tokenAmount[3]"); + + address clusterToken = tokenAmounts[0].token; + uint256 amount = getAbsoluteAmountWithdraw(tokenAmounts[0]); + + tokensToBeWithdrawn = new address[](1); + tokensToBeWithdrawn[0] = ETH; + + ERC20(clusterToken).safeApprove(EXTERNAL_ADAPTER, amount, "DeHive"); + // solhint-disable-next-line no-empty-blocks + try IExternalAdapter(EXTERNAL_ADAPTER).withdraw(clusterToken, amount) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("DeHive: withdraw fail"); + } + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterBSC.sol b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterBSC.sol new file mode 100644 index 00000000..ad28946d --- /dev/null +++ b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterBSC.sol @@ -0,0 +1,86 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { StakingDHVAdapter } from "../../adapters/dehive/StakingDHVAdapter.sol"; + +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract StakingDHVInteractiveAdapterBSC is InteractiveAdapter, StakingDHVAdapter { + using SafeERC20 for ERC20; + + address internal constant STAKING_DHV = address(0x35f28aA0B2F34eFF17d2830135312ab2a777De36); + address internal constant DHV_TOKEN = address(0x58759Dd469ae5631C42cf8a473992335575b58D7); + /** + * @notice Deposits tokens to the DeHive StakingDHV. + * @param tokenAmounts Array with one element - TokenAmount struct with + * DHV address, DHV amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - userAddress - Address of user address. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "StakingDHV: should be one token[1]"); + require(tokenAmounts[0].token == DHV_TOKEN, "StakingDHV: should be DHV[2]"); + + address userAddress = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + ERC20(tokenAmounts[0].token).safeApprove(STAKING_DHV, amount, "DHV"); + // solhint-disable-next-line no-empty-blocks + try IStakingPools(STAKING_DHV).depositFor(0, amount, userAddress) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("StakingDHV: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive StakingDHV. + * @param tokenAmounts Empty array. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + revert("StakingDHV: Can't withdraw"); + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterEth.sol b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterEth.sol new file mode 100644 index 00000000..3e843ea9 --- /dev/null +++ b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterEth.sol @@ -0,0 +1,87 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { StakingDHVAdapter } from "../../adapters/dehive/StakingDHVAdapter.sol"; + +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract StakingDHVInteractiveAdapterEth is InteractiveAdapter, StakingDHVAdapter { + using SafeERC20 for ERC20; + + address internal constant STAKING_DHV = address(0x04595f9010F79422a9b411ef963e4dd1F7107704); + address internal constant DHV_TOKEN = address(0x62Dc4817588d53a056cBbD18231d91ffCcd34b2A); + /** + * @notice Deposits tokens to the DeHive StakingDHV. + * @param tokenAmounts Array with one element - TokenAmount struct with + * DHV address, DHV amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - userAddress - Address of user address. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "StakingDHV: should be one token[1]"); + require(tokenAmounts[0].token == DHV_TOKEN, "StakingDHV: should be DHV[2]"); + + address userAddress = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + + ERC20(tokenAmounts[0].token).safeApprove(STAKING_DHV, amount, "DHV"); + // solhint-disable-next-line no-empty-blocks + try IStakingPools(STAKING_DHV).depositFor(0, amount, userAddress) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("StakingDHV: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive StakingDHV. + * @param tokenAmounts Empty array. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + revert("StakingDHV: Can't withdraw"); + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterPoly.sol b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterPoly.sol new file mode 100644 index 00000000..be91ea03 --- /dev/null +++ b/contracts/interactiveAdapters/dehive/StakingDHVInteractiveAdapterPoly.sol @@ -0,0 +1,87 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { StakingDHVAdapter } from "../../adapters/dehive/StakingDHVAdapter.sol"; + +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract StakingDHVInteractiveAdapterPoly is InteractiveAdapter, StakingDHVAdapter { + using SafeERC20 for ERC20; + + address internal constant STAKING_DHV = address(0x88cFC1bc9aEb80f6C8f5d310d6C3761c2a646Df7); + address internal constant DHV_TOKEN = address(0x5fCB9de282Af6122ce3518CDe28B7089c9F97b26); + /** + * @notice Deposits tokens to the DeHive StakingDHV. + * @param tokenAmounts Array with one element - TokenAmount struct with + * DHV address, DHV amount to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + * - userAddress - Address of user address. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "StakingDHV: should be one token[1]"); + require(tokenAmounts[0].token == DHV_TOKEN, "StakingDHV: should be DHV[2]"); + + address userAddress = abi.decode(data, (address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + ERC20(tokenAmounts[0].token).safeApprove(STAKING_DHV, amount, "DHV"); + // solhint-disable-next-line no-empty-blocks + try IStakingPools(STAKING_DHV).depositFor(0, amount, userAddress) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("StakingDHV: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive StakingDHV. + * Should be performed through the platform + * @param tokenAmounts Empty array. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + revert("StakingDHV: Can't withdraw"); + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterBSC.sol b/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterBSC.sol new file mode 100644 index 00000000..5ceec659 --- /dev/null +++ b/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterBSC.sol @@ -0,0 +1,85 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { StakingDHVAdapter } from "../../adapters/dehive/StakingDHVAdapter.sol"; + +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract StakingPoolsInteractiveAdapterBSC is InteractiveAdapter, StakingDHVAdapter { + using SafeERC20 for ERC20; + + address internal constant STAKING_POOLS = address(0xF2e8CD1c40C766FEe73f56607fDffa526Ba8fa6c); + /** + * @notice Deposits tokens to the DeHive StakingPools. + * @param tokenAmounts Array with one element - TokenAmount struct with + * staked token address,staked token to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + - poolId - ID of pool for staked token in StakingPools. + * - userAddress - Address of user address. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "StakingPools: should be one token[1]"); + + (uint256 poolId, address userAddress) = abi.decode(data, (uint256, address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + ERC20(tokenAmounts[0].token).safeApprove(STAKING_POOLS, amount, "DHV"); + // solhint-disable-next-line no-empty-blocks + try IStakingPools(STAKING_POOLS).depositFor(poolId, amount, userAddress) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("StakingPools: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive StakingPools. + * @param tokenAmounts Empty array. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + revert("StakingPools: Can't withdraw"); + } +} \ No newline at end of file diff --git a/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterEth.sol b/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterEth.sol new file mode 100644 index 00000000..dad1bb33 --- /dev/null +++ b/contracts/interactiveAdapters/dehive/StakingPoolsInteractiveAdapterEth.sol @@ -0,0 +1,86 @@ +// Copyright (C) 2020 Zerion Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: LGPL-3.0-only + +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +import { TokenAmount } from "../../shared/Structs.sol"; +import { InteractiveAdapter } from "../InteractiveAdapter.sol"; +import { ERC20 } from "../../interfaces/ERC20.sol"; +import { SafeERC20 } from "../../shared/SafeERC20.sol"; + +import { StakingDHVAdapter } from "../../adapters/dehive/StakingDHVAdapter.sol"; + +import { IStakingPools } from "../../interfaces/IStakingPools.sol"; + +/** + * @title Interactive adapter for DeHive protocol. + * @dev Implementation of InteractiveAdapter abstract contract. + */ + +contract StakingPoolsInteractiveAdapterEth is InteractiveAdapter, StakingDHVAdapter { + using SafeERC20 for ERC20; + + address internal constant STAKING_POOLS = address(0x4964B3B599B82C3FdDC56e3A9Ffd77d48c6AF0f0); + /** + * @notice Deposits tokens to the DeHive StakingPools. + * @param tokenAmounts Array with one element - TokenAmount struct with + * staked token address,staked token to be deposited, and amount type. + * @param data ABI-encoded additional parameters: + - poolId - ID of pool for staked token in StakingPools. + * - userAddress - Address of user address. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function deposit(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + require(tokenAmounts.length == 1, "StakingPools: should be one token[1]"); + + (uint256 poolId, address userAddress) = abi.decode(data, (uint256, address)); + uint256 amount = getAbsoluteAmountDeposit(tokenAmounts[0]); + + ERC20(tokenAmounts[0].token).safeApprove(STAKING_POOLS, amount, "DHV"); + // solhint-disable-next-line no-empty-blocks + try IStakingPools(STAKING_POOLS).depositFor(poolId, amount, userAddress) {} catch Error(string memory reason) { + revert(reason); + } catch { + revert("StakingPools: deposit fail"); + } + } + + /** + * @notice Withdraws tokens from the DeHive StakingPools. + * Should be performed through the platform + * @param tokenAmounts Empty array. + * @return tokensToBeWithdrawn Empty array. + * @dev Implementation of InteractiveAdapter function. + */ + + function withdraw(TokenAmount[] calldata tokenAmounts, bytes calldata data) + external + payable + override + returns (address[] memory tokensToBeWithdrawn) + { + revert("StakingPools: Can't withdraw"); + } +} \ No newline at end of file diff --git a/contracts/interfaces/IClusterToken.sol b/contracts/interfaces/IClusterToken.sol new file mode 100644 index 00000000..129e6f2d --- /dev/null +++ b/contracts/interfaces/IClusterToken.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; + +interface IClusterToken { + function assemble(uint256 clusterAmount, bool coverDhvWithEth) external payable returns (uint256); + + function disassemble(uint256 indexAmount, bool coverDhvWithEth) external; + + function withdrawToAccumulation(uint256 _clusterAmount) external; + + function refundFromAccumulation(uint256 _clusterAmount) external; + + function returnDebtFromAccumulation(uint256[] calldata _amounts, uint256 _clusterAmount) external; + + function optimizeProportion(uint256[] memory updatedShares) external returns (uint256[] memory debt); + + function getUnderlyingInCluster() external view returns (uint256[] calldata); + + function getUnderlyings() external view returns (address[] calldata); + + function getUnderlyingBalance(address _underlying) external view returns (uint256); + + function getUnderlyingsAmountsFromClusterAmount(uint256 _clusterAmount) external view returns (uint256[] calldata); + + function clusterTokenLock() external view returns (uint256); + + function clusterLock(address _token) external view returns (uint256); + + function controllerChange(address) external; + + function assembleByAdapter(uint256 _clusterAmount) external; + + function disassembleByAdapter(uint256 _clusterAmount) external; +} diff --git a/contracts/interfaces/IExternalAdapter.sol b/contracts/interfaces/IExternalAdapter.sol new file mode 100644 index 00000000..d75a62a9 --- /dev/null +++ b/contracts/interfaces/IExternalAdapter.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; + +interface IExternalAdapter { + function getTotalClusterBalance(address _asset, address _user) external returns (uint256); + + function deposit(address _asset) external payable returns (uint256); + + function withdraw(address _asset, uint256 _amount) external returns (uint256); +} diff --git a/contracts/interfaces/IStakingPools.sol b/contracts/interfaces/IStakingPools.sol new file mode 100644 index 00000000..b6689d71 --- /dev/null +++ b/contracts/interfaces/IStakingPools.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.7.6; +pragma experimental ABIEncoderV2; + +interface IStakingPools { + struct UserInfo { + uint256 amount; // How many ASSET tokensens the user has provided. + uint256[] rewardsDebts; // Order like in AssetInfo rewardsTokens + } + struct PoolInfo { + address assetToken; // Address of LP token contract. + uint256 lastRewardBlock; // Last block number that DHVs distribution occurs. + uint256[] accumulatedPerShare; // Accumulated token per share, times token decimals. See below. + address[] rewardsTokens; // Must be constant. + uint256[] rewardsPerBlock; // Tokens to distribute per block. + uint256[] accuracy; // Tokens accuracy. + uint256 poolSupply; // Total amount of deposits by users. + bool paused; + } + + function poolInfo(uint256 _pid) external view returns (PoolInfo memory); + + function userInfo(uint256 _pid, address _user) external view returns (uint256); + + function addPool( + uint256 _pid, + address _assetAddress, + address[] calldata _rewardsTokens, + uint256[] calldata _rewardsPerBlock + ) external; + + function updatePoolSettings( + uint256 _pid, + uint256[] calldata _rewardsPerBlock, + bool _withUpdate + ) external; + + function setOnPause(address _assetAddress, bool _paused) external; + + function pendingRewards(uint256 _pid, address _user) external view returns (uint256[] memory amounts); + + function updatePool(uint256 _pid) external; + + function deposit(uint256 _pid, uint256 _amount) external; + + function depositFor(uint256 _pid, uint256 _amount, address _user) external; + + function withdraw(uint256 _pid, uint256 _amount) external; + + function claimRewards(uint256 _pid) external; + + function poolExist(uint256 _pid) external view returns (bool); +} diff --git a/contracts/interfaces/UniswapV2Router02.sol b/contracts/interfaces/UniswapV2Router02.sol index 4772993e..eb36c699 100644 --- a/contracts/interfaces/UniswapV2Router02.sol +++ b/contracts/interfaces/UniswapV2Router02.sol @@ -40,4 +40,21 @@ interface UniswapV2Router02 { address to, uint256 deadline ) external returns (uint256[] memory amounts); + + function addLiquidity( + address tokenA, + address tokenB, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline + ) + external + returns ( + uint256 amountA, + uint256 amountB, + uint256 liquidity + ); } diff --git a/test/adapters/ClusterTokenAdapter.js b/test/adapters/ClusterTokenAdapter.js new file mode 100644 index 00000000..918feae6 --- /dev/null +++ b/test/adapters/ClusterTokenAdapter.js @@ -0,0 +1,43 @@ +const BN = web3.utils.BN; + +const TokenAdapter = artifacts.require('ClusterTokenAdapter'); +const IClusterToken = artifacts.require('IClusterToken'); + +contract('ClusterTokenAdapter', () => { + const polyClusterAddress = '0x4964B3B599B82C3FdDC56e3A9Ffd77d48c6AF0f0'; + let accounts; + let tokenAdapter; + let clusterToken; + const DPOL = [ + 'Polycluster', + 'DPOL', + '18' + ]; + + before(async() => { + accounts = await web3.eth.getAccounts(); + tokenAdapter = await TokenAdapter.new(); + clusterToken = await IClusterToken.at(polyClusterAddress); + }); + + it('Should return correct components', async() => { + const underlyingsFromCluster = await clusterToken.getUnderlyings(); + const proportions = await clusterToken.getUnderlyingInCluster(); + + const components = await tokenAdapter.getComponents(polyClusterAddress); + for(let i = 0; i < underlyingsFromCluster.length; i++) { + assert.equal(underlyingsFromCluster[i], components[i].token); + assert.equal((proportions[i].mul( + new BN('1000000000000000000') + ).div( + new BN('1000000') + ) + ).toString(), components[i].rate); + } + }); + + it('Should return correct metadata', async() => { + const metadata = await tokenAdapter.getMetadata(polyClusterAddress); + assert.deepEqual(DPOL, metadata); + }) +}); \ No newline at end of file diff --git a/test/adapters/DeHiveProtocolAdapter.js b/test/adapters/DeHiveProtocolAdapter.js new file mode 100644 index 00000000..5d281bb5 --- /dev/null +++ b/test/adapters/DeHiveProtocolAdapter.js @@ -0,0 +1,29 @@ +const BN = web3.utils.BN; + +const ProtocolAdapter = artifacts.require('DeHiveProtocolAdapter'); +const IClusterToken = artifacts.require('IClusterToken'); + +contract('DeHiveProtocolAdapter', () => { + const polyClusterAddress = '0x4964B3B599B82C3FdDC56e3A9Ffd77d48c6AF0f0'; + let accounts; + let protocolAdapter; + let clusterToken; + + before(async() => { + accounts = await web3.eth.getAccounts(); + protocolAdapter = await ProtocolAdapter.new({from: accounts[0]}); + clusterToken = await IClusterToken.at(polyClusterAddress); + }); + + it('Should return correct balance', async() => { + let bal = await protocolAdapter.getBalance(polyClusterAddress, accounts[0]); + assert.equal(bal, 0); + + let cl = new BN('1000000000000000000'); + let eth = await web3.utils.toWei('3000', 'ether'); + await clusterToken.assemble(cl, true, {from: accounts[0], value: eth}); + + assert.equal(cl.toString(), + (await protocolAdapter.getBalance(polyClusterAddress, accounts[0])).toString()); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/ClusterTokenInteractiveAdapterBSC.js b/test/interactiveAdapters/ClusterTokenInteractiveAdapterBSC.js new file mode 100644 index 00000000..1257bff9 --- /dev/null +++ b/test/interactiveAdapters/ClusterTokenInteractiveAdapterBSC.js @@ -0,0 +1,312 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const CLUSTER_TOKEN_ADAPTER = convertToBytes32('Cluster Token BSC'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./ClusterTokenInteractiveAdapterBSC'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const ClusterExternalAdapter = artifacts.require('./IExternalAdapter'); + + +contract('ClusterTokenInteractiveAdapter', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const bscClusterAddress = '0x0a684421ef48b431803BFd75F38675EAb1e38Ed5'; + const busdAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let clusterTokenInstance; + let clusterExternalAdapter; + + beforeEach(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', bscClusterAddress); + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + CLUSTER_TOKEN_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(bscClusterAddress) + .then((result) => { + clusterTokenInstance = result.contract; + }); + }); + + describe('ETH <-> BscCluster', async() => { + it('Should not be correct ETH -> BscCluster if 2 tokens', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`BscCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should not be correct BUSD -> BscCluster', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`BscCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [busdAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH -> BscCluster deposit', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`BscCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`BscCluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + + it('Should not be correct ETH <- BscCluster withdraw if 2 tokens', async() => { + let BscClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + BscClusterAmount = result; + console.log(`BscCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (BscClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [bscClusterAddress, BscClusterAmount, AMOUNT_ABSOLUTE], + [bscClusterAddress, BscClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [bscClusterAddress, BscClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH <- BscCluster withdraw', async() => { + let BscClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + BscClusterAmount = result; + console.log(`BscCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (BscClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [bscClusterAddress, BscClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [bscClusterAddress, BscClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`BscCluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/ClusterTokenInteractiveAdapterEth.js b/test/interactiveAdapters/ClusterTokenInteractiveAdapterEth.js new file mode 100644 index 00000000..1f922bc3 --- /dev/null +++ b/test/interactiveAdapters/ClusterTokenInteractiveAdapterEth.js @@ -0,0 +1,311 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const CLUSTER_TOKEN_ADAPTER = convertToBytes32('Cluster Token'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./ClusterTokenInteractiveAdapterETH'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const ClusterExternalAdapter = artifacts.require('./IExternalAdapter'); + + +contract('ClusterTokenInteractiveAdapter', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const decrClusterAddress = '0x6Bc3F65Fc50E49060e21eD6996be96ee4B404752'; + const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let clusterTokenInstance; + + beforeEach(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', decrClusterAddress); + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + CLUSTER_TOKEN_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(decrClusterAddress) + .then((result) => { + clusterTokenInstance = result.contract; + }); + }); + + describe('ETH <-> decrCluster', async() => { + it('Should not be correct ETH -> decrCluster if 2 tokens', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`decrCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should not be correct DAI -> decrCluster', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`decrCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [daiAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH -> decrCluster deposit', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`decrCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`decrCluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + + it('Should not be correct ETH <- decrCluster withdraw if 2 tokens', async() => { + let decrClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + decrClusterAmount = result; + console.log(`decrCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (decrClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [decrClusterAddress, decrClusterAmount, AMOUNT_ABSOLUTE], + [decrClusterAddress, decrClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [decrClusterAddress, decrClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH <- decrCluster withdraw', async() => { + let decrClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + decrClusterAmount = result; + console.log(`decrCluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (decrClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [decrClusterAddress, decrClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [decrClusterAddress, decrClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`decrCluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/ClusterTokenInteractiveAdapterPoly.js b/test/interactiveAdapters/ClusterTokenInteractiveAdapterPoly.js new file mode 100644 index 00000000..9d7267ea --- /dev/null +++ b/test/interactiveAdapters/ClusterTokenInteractiveAdapterPoly.js @@ -0,0 +1,312 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const CLUSTER_TOKEN_ADAPTER = convertToBytes32('Cluster Token'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./ClusterTokenInteractiveAdapterPoly'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const ClusterExternalAdapter = artifacts.require('./IExternalAdapter'); + + +contract('ClusterTokenInteractiveAdapter', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const polyClusterAddress = '0x4964B3B599B82C3FdDC56e3A9Ffd77d48c6AF0f0'; + const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let clusterTokenInstance; + let clusterExternalAdapter; + + beforeEach(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', polyClusterAddress); + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + CLUSTER_TOKEN_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(polyClusterAddress) + .then((result) => { + clusterTokenInstance = result.contract; + }); + }); + + describe('ETH <-> PolyCluster', async() => { + it('Should not be correct ETH -> PolyCluster if 2 tokens', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`polycluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should not be correct DAI -> PolyCluster', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`polycluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [daiAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH -> PolyCluster deposit', async() => { + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`polycluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_DEPOSIT, + [ + [ethAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`polycluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + + it('Should not be correct ETH <- Polycluster withdraw if 2 tokens', async() => { + let polyClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + polyClusterAmount = result; + console.log(`polycluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (polyClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [polyClusterAddress, polyClusterAmount, AMOUNT_ABSOLUTE], + [polyClusterAddress, polyClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [polyClusterAddress, polyClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + })); + }); + + it('Should be correct ETH <- PolyCluster withdraw', async() => { + let polyClusterAmount; + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + polyClusterAmount = result; + console.log(`polycluster amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods.approve(router.options.address, (polyClusterAmount * 2).toString()) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + CLUSTER_TOKEN_ADAPTER, + ACTION_WITHDRAW, + [ + [polyClusterAddress, polyClusterAmount, AMOUNT_ABSOLUTE] + ], + EMPTY_BYTES, + ], + ], + [ + [ + [polyClusterAddress, polyClusterAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0], + value: web3.utils.toWei('1', 'ether'), + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`polycluster amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await web3.eth.getBalance(accounts[0]) + .then((result) => { + console.log(` eth amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + await clusterTokenInstance.methods['balanceOf(address)'](core.options.address) + .call() + .then((result) => { + assert.equal(result, 0); + }); + await web3.eth.getBalance(core.options.address) + .then((result) => { + assert.equal(result, 0); + }); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/StakingDHVInteractiveAdapterBSC.js b/test/interactiveAdapters/StakingDHVInteractiveAdapterBSC.js new file mode 100644 index 00000000..f9d8bfce --- /dev/null +++ b/test/interactiveAdapters/StakingDHVInteractiveAdapterBSC.js @@ -0,0 +1,221 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const DHV_STAKING_ADAPTER = convertToBytes32('DHV Staking'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./StakingDHVInteractiveAdapterBSC'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const UniswapRouter = artifacts.require('./UniswapV2Router02'); +const StakingPools = artifacts.require('./IStakingPools'); +const StakingDHVAdapter = artifacts.require('./StakingDHVAdapter'); + + +contract('StakingDHVInteractiveAdapterEth', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const dhvAddress = '0x58759dd469ae5631c42cf8a473992335575b58d7'; + const busdAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; + const stakingDHVAddress = '0x35f28aA0B2F34eFF17d2830135312ab2a777De36'; + const wbnb = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let stakingPools; + let dhvToken; + let uniSwapRouter; + let stakingAdapter; + + let protocol; + + before(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', accounts[0]); + + stakingAdapter = await StakingDHVAdapter.new({from: accounts[0]}); + + uniSwapRouter = await UniswapRouter.at("0x10ED43C718714eb63d5aA57B78B54704E256024E"); + let ethValue = await web3.utils.toWei('1', 'ether'); + await uniSwapRouter.swapExactETHForTokens(0, [wbnb, busdAddress, dhvAddress], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + protocol = result.contract; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + DHV_STAKING_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(dhvAddress) + .then((result) => { + dhvToken = result.contract; + }); + }); + + describe('DHV <-> StakingDHV', async() => { + it('Should not be correct DHV -> DHV Staking if 2 tokens', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + // let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + // console.log(`staking dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should not be correct BUSD -> DHV Staking', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [busdAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should be correct DHV -> DHV Staking deposit', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE] + ], + encodedData, + ], + ], + [ + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + console.log(`staking dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/StakingDHVInteractiveAdapterEth.js b/test/interactiveAdapters/StakingDHVInteractiveAdapterEth.js new file mode 100644 index 00000000..0c82684a --- /dev/null +++ b/test/interactiveAdapters/StakingDHVInteractiveAdapterEth.js @@ -0,0 +1,221 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const DHV_STAKING_ADAPTER = convertToBytes32('DHV Staking'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./StakingDHVInteractiveAdapterEth'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const UniswapRouter = artifacts.require('./UniswapV2Router02'); +const StakingPools = artifacts.require('./IStakingPools'); +const StakingDHVAdapter = artifacts.require('./StakingDHVAdapter'); + + +contract('StakingDHVInteractiveAdapterEth', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const dhvAddress = '0x62Dc4817588d53a056cBbD18231d91ffCcd34b2A'; + const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + const stakingDHVAddress = '0x04595f9010F79422a9b411ef963e4dd1F7107704'; + const weth = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let stakingPools; + let dhvToken; + let uniSwapRouter; + let stakingAdapter; + + let protocol; + + before(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', accounts[0]); + + stakingAdapter = await StakingDHVAdapter.new({from: accounts[0]}); + + uniSwapRouter = await UniswapRouter.at("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"); + let ethValue = await web3.utils.toWei('1', 'ether'); + await uniSwapRouter.swapExactETHForTokens(0, [weth, dhvAddress], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + protocol = result.contract; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + DHV_STAKING_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(dhvAddress) + .then((result) => { + dhvToken = result.contract; + }); + }); + + describe('DHV <-> StakingDHV', async() => { + it('Should not be correct DHV -> DHV Staking if 2 tokens', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + // let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + // console.log(`staking dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should not be correct DAI -> DHV Staking', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [daiAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should be correct DHV -> DHV Staking deposit', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE] + ], + encodedData, + ], + ], + [ + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + console.log(`staking dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/StakingDHVInteractiveAdapterPoly.js b/test/interactiveAdapters/StakingDHVInteractiveAdapterPoly.js new file mode 100644 index 00000000..609c5f62 --- /dev/null +++ b/test/interactiveAdapters/StakingDHVInteractiveAdapterPoly.js @@ -0,0 +1,220 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const DHV_STAKING_ADAPTER = convertToBytes32('DHV Staking'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./StakingDHVInteractiveAdapterPoly'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const UniswapRouter = artifacts.require('./UniswapV2Router02'); +const StakingPools = artifacts.require('./IStakingPools'); +const StakingDHVAdapter = artifacts.require('./StakingDHVAdapter'); + + +contract('StakingDHVInteractiveAdapterPoly', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const dhvAddress = '0x5fCB9de282Af6122ce3518CDe28B7089c9F97b26'; + const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + const stakingDHVAddress = '0x88cFC1bc9aEb80f6C8f5d310d6C3761c2a646Df7'; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let stakingPools; + let dhvToken; + let uniSwapRouter; + let stakingAdapter; + + let protocol; + + before(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameter('address', accounts[0]); + + stakingAdapter = await StakingDHVAdapter.new({from: accounts[0]}); + + uniSwapRouter = await UniswapRouter.at("0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff"); + let ethValue = await web3.utils.toWei('1', 'ether'); + await uniSwapRouter.swapExactETHForTokens(0, ["0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", "0x831753DD7087CaC61aB5644b308642cc1c33Dc13", dhvAddress], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + protocol = result.contract; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + DHV_STAKING_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(dhvAddress) + .then((result) => { + dhvToken = result.contract; + }); + }); + + describe('DHV <-> StakingDHV', async() => { + it('Should not be correct DHV -> DHV Staking if 2 tokens', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + // let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + // console.log(`staking dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, convertToShare(1), AMOUNT_RELATIVE], + [ethAddress, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should not be correct DAI -> DHV Staking', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [daiAddress, convertToShare(1), AMOUNT_RELATIVE] + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should be correct DHV -> DHV Staking deposit', async() => { + let dhvAmount; + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + }); + await dhvToken.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + DHV_STAKING_ADAPTER, + ACTION_DEPOSIT, + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE] + ], + encodedData, + ], + ], + [ + [ + [dhvAddress, dhvAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await dhvToken.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + console.log(`staking dhv amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/StakingPoolsInteractiveAdapterBSC.js b/test/interactiveAdapters/StakingPoolsInteractiveAdapterBSC.js new file mode 100644 index 00000000..bb0ee767 --- /dev/null +++ b/test/interactiveAdapters/StakingPoolsInteractiveAdapterBSC.js @@ -0,0 +1,175 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const STAKING_POOLS_ADAPTER = convertToBytes32('Staking pools'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./StakingPoolsInteractiveAdapterBSC'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const UniswapRouter = artifacts.require('./UniswapV2Router02'); +const WETH = artifacts.require('./WETH9'); + + + +contract('StakingPoolsInteractiveAdapterBSC', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const busdAddress = '0xe9e7cea3dedca5984780bafc599bd69add087d56'; + const busdDhvAddress = '0x72ba008b631d9fd5a8e8013023cb3c05e19a7ca9'; + const wbnb = '0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c'; + const dhv = '0x58759dd469ae5631c42cf8a473992335575b58d7'; + const uniswapRouterAddress = '0x10ED43C718714eb63d5aA57B78B54704E256024E'; + const stakingPoolsAddress = '0xF2e8CD1c40C766FEe73f56607fDffa526Ba8fa6c'; + const WETH_DHV_PID = 0; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let dhvToken; + let uniSwapRouter; + let busdERC20; + let busdDhvLp; + + let protocol; + + before(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameters(['uint', 'address'], [WETH_DHV_PID, accounts[0]); + + uniSwapRouter = await UniswapRouter.at(uniswapRouterAddress); + busdDhvLp = await ERC20.at(busdDhvAddress); + dhvToken = await ERC20.at(dhv); + busdERC20 = await ERC20.at(busdAddress); + let ethValue = await web3.utils.toWei('1', 'ether'); + await uniSwapRouter.swapExactETHForTokens(0, [wbnb, busdAddress, dhv], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + await uniSwapRouter.swapExactETHForTokens(0, [wbnb, busdAddress], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + let dhv_bal = await dhvToken.balanceOf(accounts[0]); + let busd_bal = await busdERC20.balanceOf(accounts[0]); + await dhvToken.approve(uniswapRouterAddress, dhv_bal, {from: accounts[0]}); + await busdERC20.approve(uniswapRouterAddress, busd_bal, {from: accounts[0]}); + await uniSwapRouter.addLiquidity(busdAddress, dhv, busd_bal, dhv_bal, 0, 0, accounts[0], '10000000000', {from: accounts[0]}); + + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + protocol = result.contract; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + STAKING_POOLS_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(dhv) + .then((result) => { + dhvToken = result.contract; + }); + }); + + describe('DHV-BUSD LP <-> StakingPools', async() => { + it('Should not be correct DHV-BUSD LP -> StakingPools if 2 tokens', async() => { + let dhvAmount = await busdDhvLp.balanceOf(accounts[0]); + console.log(`DHV-BUSD lp amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + + await busdDhvLp.approve(router.options.address, dhvAmount, {from: accounts[0]}); + await expectRevert(router.methods.execute( + [ + [ + STAKING_POOLS_ADAPTER, + ACTION_DEPOSIT, + [ + [busdDhvAddress, convertToShare(1), AMOUNT_ABSOLUTE], + [wbnb, convertToShare(1), AMOUNT_ABSOLUTE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should be correct DHV-BUSD LP -> StakingPools deposit', async() => { + let dhvAmount = await busdDhvLp.balanceOf(accounts[0]); + console.log(`DHV-BUSD lp amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + + await busdDhvLp.approve(router.options.address, dhvAmount, {from: accounts[0]}); + await router.methods.execute( + [ + [ + STAKING_POOLS_ADAPTER, + ACTION_DEPOSIT, + [ + [busdDhvAddress, dhvAmount, AMOUNT_ABSOLUTE] + ], + encodedData, + ], + ], + [ + [ + [busdDhvAddress, dhvAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + dhvAmount = await busdDhvLp.balanceOf(accounts[0]); + console.log(`DHV-BUSD lp amount after is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + let result = await protocol.methods['getBalance(address,address)'](stakingPoolsAddress, accounts[0]).call(); + console.log(`staking pools busd-dhv lp amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + }); +}); \ No newline at end of file diff --git a/test/interactiveAdapters/StakingPoolsInteractiveAdapterEth.js b/test/interactiveAdapters/StakingPoolsInteractiveAdapterEth.js new file mode 100644 index 00000000..bc9359b5 --- /dev/null +++ b/test/interactiveAdapters/StakingPoolsInteractiveAdapterEth.js @@ -0,0 +1,198 @@ +import expectRevert from '../helpers/expectRevert'; +import convertToShare from '../helpers/convertToShare'; +import convertToBytes32 from '../helpers/convertToBytes32'; + +const BN = web3.utils.BN; + +const STAKING_POOLS_ADAPTER = convertToBytes32('Staking pools'); + +const ACTION_DEPOSIT = 1; +const ACTION_WITHDRAW = 2; +const AMOUNT_RELATIVE = 1; +const AMOUNT_ABSOLUTE = 2; +const EMPTY_BYTES = '0x'; + +const ZERO = '0x0000000000000000000000000000000000000000'; + +const ProtocolAdapterRegistry = artifacts.require('./ProtocolAdapterRegistry'); +const InteractiveAdapter = artifacts.require('./StakingPoolsInteractiveAdapterEth'); +const Core = artifacts.require('./Core'); +const Router = artifacts.require('./Router'); +const ERC20 = artifacts.require('./ERC20'); +const UniswapRouter = artifacts.require('./UniswapV2Router02'); +const WETH = artifacts.require('./WETH9'); + + + +contract('StakingPoolsInteractiveAdapterEth', () => { + const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; + const daiAddress = '0x6B175474E89094C44Da98b954EedeAC495271d0F'; + const wethDhvLpAddress = '0x60c5BF43140d6341bebFE13293567FafBe01D65b'; + const weth = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; + const dhv = '0x62Dc4817588d53a056cBbD18231d91ffCcd34b2A'; + const uniswapRouterAddress = '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D'; + const stakingPoolsAddress = '0x4964B3B599B82C3FdDC56e3A9Ffd77d48c6AF0f0'; + const WETH_DHV_PID = 0; + let encodedData; + + let accounts; + let core; + let router; + let protocolAdapterRegistry; + let protocolAdapterAddress; + let dhvToken; + let uniSwapRouter; + let wethProtocol, wethERC20; + let wethDhvLp; + + let protocol; + + before(async () => { + accounts = await web3.eth.getAccounts(); + encodedData = web3.eth.abi.encodeParameters(['uint', 'address', 'address[]'], [WETH_DHV_PID, accounts[0], [dhv]]); + + uniSwapRouter = await UniswapRouter.at(uniswapRouterAddress); + wethProtocol = await WETH.at(weth); + wethERC20 = await ERC20.at(weth); + wethDhvLp = await ERC20.at(wethDhvLpAddress); + dhvToken = ERC20.at(dhv); + let ethValue = await web3.utils.toWei('1', 'ether'); + await uniSwapRouter.swapExactETHForTokens(0, [weth, dhv], accounts[0], '10000000000', {from: accounts[0], value: ethValue}); + await wethProtocol.deposit({from: accounts[0], value: ethValue}); + let dhv_bal = await dhvToken.balanceOf(accounts[0]); + let weth_bal = await wethERC20.balanceOf(accounts[0]); + await dhvToken.approve(uniswapRouterAddress, dhv_bal, {from: accounts[0]}); + await wethERC20.approve(uniswapRouterAddress, weth_bal, {from: accounts[0]}); + await uniSwapRouter.addLiquidity(weth, dhv, await weth_bal, dhv_bal, 0, 0, accounts[0], '10000000000', {from: accounts[0]}); + + await InteractiveAdapter.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterAddress = result.address; + protocol = result.contract; + }); + await ProtocolAdapterRegistry.new({ from: accounts[0] }) + .then((result) => { + protocolAdapterRegistry = result.contract; + }); + await protocolAdapterRegistry.methods.addProtocolAdapters( + [ + STAKING_POOLS_ADAPTER, + ], + [ + protocolAdapterAddress, + ], + [[]], + ) + .send({ + from: accounts[0], + gas: '1000000', + }); + await Core.new( + protocolAdapterRegistry.options.address, + { from: accounts[0] }, + ) + .then((result) => { + core = result.contract; + }); + await Router.new( + core.options.address, + { from: accounts[0] }, + ) + .then((result) => { + router = result.contract; + }); + await ERC20.at(dhvAddress) + .then((result) => { + dhvToken = result.contract; + }); + }); + + describe('DHV-WETH LP <-> StakingPools', async() => { + it('Should not be correct DHV-WETH LP -> StakingPools if 2 tokens', async() => { + let dhvAmount; + await wethDhvLp.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv-weth lp amount before is ${web3.utils.fromWei(result, 'ether')}`); + }); + // let result = await protocol.methods['getBalance(address,address)'](stakingDHVAddress, accounts[0]).call(); + // console.log(`staking dhv amount before is ${web3.utils.fromWei(result, 'ether')}`); + + await wethDhvLp.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await expectRevert(router.methods.execute( + [ + [ + STAKING_POOLS_ADAPTER, + ACTION_DEPOSIT, + [ + [wethDhvLpAddress, convertToShare(1), AMOUNT_RELATIVE], + [weth, convertToShare(1), AMOUNT_RELATIVE], + ], + encodedData, + ], + ], + [], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + })); + }); + + it('Should be correct DHV-WETH LP -> StakingPools deposit', async() => { + let dhvAmount; + await wethDhvLp.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + dhvAmount = result; + console.log(`dhv-weth lp amount before is ${web3.utils.fromWei(dhvAmount, 'ether')}`); + }); + await wethDhvLp.methods.approve(router.options.address, dhvAmount) + .send({ + gas: 10000000, + from: accounts[0], + }); + await router.methods.execute( + [ + [ + STAKING_POOLS_ADAPTER, + ACTION_DEPOSIT, + [ + [wethDhvLp, dhvAmount, AMOUNT_ABSOLUTE] + ], + encodedData, + ], + ], + [ + [ + [wethDhvLp, dhvAmount, AMOUNT_ABSOLUTE], + [0, EMPTY_BYTES] + ] + ], + [0, ZERO], + [], + ) + .send({ + gas: 10000000, + from: accounts[0] + }) + .then((receipt) => { + console.log(`called router for ${receipt.cumulativeGasUsed} gas`); + }); + await wethDhvLp.methods['balanceOf(address)'](accounts[0]) + .call() + .then((result) => { + console.log(`dhv-weth lp amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + let result = await protocol.methods['getBalance(address,address)'](stakingPoolsAddress, accounts[0]).call(); + console.log(`staking pools weth-dhv lp amount after is ${web3.utils.fromWei(result, 'ether')}`); + }); + }); +}); \ No newline at end of file