diff --git a/contracts/adapters/stake-dao/StakeDAOTokenAdapter.sol b/contracts/adapters/stake-dao/StakeDAOTokenAdapter.sol new file mode 100644 index 00000000..15f038ff --- /dev/null +++ b/contracts/adapters/stake-dao/StakeDAOTokenAdapter.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: None + +pragma solidity 0.6.5; +pragma experimental ABIEncoderV2; + +import {ERC20} from "../../ERC20.sol"; +import {TokenMetadata, Component} from "../../Structs.sol"; +import {TokenAdapter} from "../TokenAdapter.sol"; + +interface Vault { + function token() external view returns (address); + + function getPricePerFullShare() external view returns (uint256); +} + +/** + * @title Token adapter for Stake DAO Vaults. + * @dev Implementation of TokenAdapter abstract contract. + * @author Elephant memory/strength + */ +contract StakeDaoTokenAdapter is TokenAdapter { + address internal constant SD_VECRV = 0x478bBC744811eE8310B461514BDc29D03739084D; + address internal constant CRV = 0xD533a949740bb3306d119CC777fa900bA034cd52; + + /** + * @return TokenMetadata struct with ERC20-style token info. + * @dev Implementation of TokenAdapter interface function. + */ + function getMetadata(address token) + external + view + override + returns (TokenMetadata memory) + { + return + TokenMetadata({ + token: token, + name: ERC20(token).name(), + symbol: ERC20(token).symbol(), + decimals: ERC20(token).decimals() + }); + } + + /** + * @return Array of Component structs with underlying tokens rates for the given token. + * @dev Implementation of TokenAdapter abstract contract function. + */ + function getComponents(address token) + external + view + override + returns (Component[] memory) + { + Component[] memory components = new Component[](1); + + if (token == SD_VECRV) { + components[0] = Component({ + token: CRV, + tokenType: "ERC20", + rate: 1e18 + }); + } else { + components[0] = Component({ + token: Vault(token).token(), + tokenType: "ERC20", + rate: Vault(token).getPricePerFullShare() + }); + } + + return components; + } +} diff --git a/contracts/adapters/stake-dao/StakeDAOVaultAdapter.sol b/contracts/adapters/stake-dao/StakeDAOVaultAdapter.sol new file mode 100644 index 00000000..5920b4d4 --- /dev/null +++ b/contracts/adapters/stake-dao/StakeDAOVaultAdapter.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: None + +pragma solidity 0.6.5; +pragma experimental ABIEncoderV2; + +import {ERC20} from "../../ERC20.sol"; +import {ProtocolAdapter} from "../ProtocolAdapter.sol"; + +/** + * @title Adapter for Yearn Token Vaults. + * @dev Implementation of ProtocolAdapter interface. + * @author Igor Sobolev + */ +contract StakeDAOVaultAdapter is ProtocolAdapter { + string public constant override adapterType = "Asset"; + + string public constant override tokenType = "Stake DAO Vault"; + + /** + * @return Amount of Stake DAO Vault Tokens owned by the given account. + * @param token Address of the vault token. + * @dev Implementation of ProtocolAdapter interface function. + */ + function getBalance(address token, address account) + external + view + override + returns (uint256) + { + return ERC20(token).balanceOf(account); + } +} diff --git a/test/adapters/StakeDaoTokenAdapter.js b/test/adapters/StakeDaoTokenAdapter.js new file mode 100644 index 00000000..fa405c2b --- /dev/null +++ b/test/adapters/StakeDaoTokenAdapter.js @@ -0,0 +1,63 @@ +const TokenAdapter = artifacts.require("StakeDaoTokenAdapter"); + +contract("StakeDaoTokenAdapter", () => { + const sbtcVaultAddress = "0x24129B935AfF071c4f0554882C0D9573F4975fEd"; + const sbtcCrv = "0x075b1bb99792c9E1041bA13afEf80C91a1e70fB3"; + + const sdveCrvAddress = "0x478bBC744811eE8310B461514BDc29D03739084D"; + const CRV = '0xD533a949740bb3306d119CC777fa900bA034cd52'; + + let accounts; + let tokenAdapter; + const sbtcVault = [ + sbtcVaultAddress, + "stake dao Curve.fi renBTC/wBTC/sBTC", + "sdcrvRenWSBTC", + "18", + ]; + + const sdveCrvVault = [sdveCrvAddress, "veCRV Stake DAO", "sdveCRV-DAO", "18"]; + + beforeEach(async () => { + accounts = await web3.eth.getAccounts(); + + await TokenAdapter.new({ from: accounts[0] }).then((result) => { + tokenAdapter = result.contract; + }); + }); + + it("should return correct components for sBTC, 3Pool, Eurs vaults", async () => { + await tokenAdapter.methods["getComponents(address)"](sbtcVaultAddress) + .call() + .then((result) => { + assert.equal(result[0][0], sbtcCrv); + assert.equal(result[0][1], "ERC20"); + }); + }); + + it("should return correct metadata for sBTC, 3Pool, Eurs vaults", async () => { + await tokenAdapter.methods["getMetadata(address)"](sbtcVaultAddress) + .call() + .then((result) => { + assert.deepEqual(result, sbtcVault); + }); + }); + + it("should return correct components for perpetual passive strategy vault", async () => { + await tokenAdapter.methods["getComponents(address)"](sdveCrvAddress) + .call() + .then((result) => { + assert.equal(result[0][0], CRV); + assert.equal(result[0][1], "ERC20"); + assert.equal(result[0][2], "1000000000000000000"); + }); + }); + + it("should return correct metadata for perpetual passive strategy vaults", async () => { + await tokenAdapter.methods["getMetadata(address)"](sdveCrvAddress) + .call() + .then((result) => { + assert.deepEqual(result, sdveCrvVault); + }); + }); +});