From e31abde1f8065c94ace9e5e0a12ac6c7402f9ca8 Mon Sep 17 00:00:00 2001 From: Thrilok Kumar Date: Wed, 23 Jun 2021 00:56:44 +0530 Subject: [PATCH 1/4] Added instadapp governance connector --- contracts/mainnet/connectors/INST/events.sol | 6 ++ contracts/mainnet/connectors/INST/helpers.sol | 18 ++++++ .../mainnet/connectors/INST/interface.sol | 10 ++++ contracts/mainnet/connectors/INST/main.sol | 60 +++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 contracts/mainnet/connectors/INST/events.sol create mode 100644 contracts/mainnet/connectors/INST/helpers.sol create mode 100644 contracts/mainnet/connectors/INST/interface.sol create mode 100644 contracts/mainnet/connectors/INST/main.sol diff --git a/contracts/mainnet/connectors/INST/events.sol b/contracts/mainnet/connectors/INST/events.sol new file mode 100644 index 00000000..042c635a --- /dev/null +++ b/contracts/mainnet/connectors/INST/events.sol @@ -0,0 +1,6 @@ +pragma solidity ^0.7.0; + +contract Events { + event LogVoteCast(uint256 proposalId, uint256 support, string reason); + event LogDelegate(address delegatee); +} diff --git a/contracts/mainnet/connectors/INST/helpers.sol b/contracts/mainnet/connectors/INST/helpers.sol new file mode 100644 index 00000000..7bdab7a1 --- /dev/null +++ b/contracts/mainnet/connectors/INST/helpers.sol @@ -0,0 +1,18 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { InstaTokenInterface, InstaGovernorInterface } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev InstaGovernorBravo + */ + InstaGovernorInterface internal constant instaGovernor = InstaGovernorInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); + + /** + * @dev INST Token + */ + InstaTokenInterface internal constant instToken = InstaTokenInterface(0xc00e94Cb662C3520282E6f5717214004A7f26888); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/INST/interface.sol b/contracts/mainnet/connectors/INST/interface.sol new file mode 100644 index 00000000..eacb53ae --- /dev/null +++ b/contracts/mainnet/connectors/INST/interface.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.7.0; + +interface InstaGovernorInterface { + function castVoteWithReason(uint proposalId, uint8 support, string calldata reason) external; +} + +interface InstaTokenInterface { + function delegate(address delegatee) external; + function delegates(address) external view returns(address); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/INST/main.sol b/contracts/mainnet/connectors/INST/main.sol new file mode 100644 index 00000000..05329dee --- /dev/null +++ b/contracts/mainnet/connectors/INST/main.sol @@ -0,0 +1,60 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title Instadapp Governance. + * @dev Governance. + */ +import { TokenInterface } from "../../common/interfaces.sol"; +import { Stores } from "../../common/stores.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract Resolver is Events, Helpers { + + /** + * @dev Delegate votes. + * @notice Delegating votes to delegatee. + * @param delegatee The address to delegate the votes. + */ + function delegate(address delegatee) external payable returns (string memory _eventName, bytes memory _eventParam) { + require(instToken.delegates(address(this)) != delegatee, "Already delegated to same delegatee."); + + instToken.delegate(delegatee); + + _eventName = "LogDelegate(address)"; + _eventParam = abi.encode(delegatee); + } + + + /** + * @dev Cast vote. + * @notice Casting vote for a proposal + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + */ + function voteCast(uint256 proposalId, uint256 support) external payable returns (string memory _eventName, bytes memory _eventParam) { + instaGovernor.castVoteWithReason(proposalId, uint8(support), ""); + + _eventName = "LogVoteCast(uint256,uint256,string)"; + _eventParam = abi.encode(proposalId, support, ""); + } + + /** + * @dev Cast vote with reason. + * @notice Casting vote for a proposal + * @param proposalId The id of the proposal to vote on + * @param support The support value for the vote. 0=against, 1=for, 2=abstain + * @param reason The reason given for the vote + */ + function voteCastWithReason(uint256 proposalId, uint256 support, string calldata reason) external payable returns (string memory _eventName, bytes memory _eventParam) { + instaGovernor.castVoteWithReason(proposalId, uint8(support), reason); + + _eventName = "LogVoteCast(uint256,uint256,string)"; + _eventParam = abi.encode(proposalId, support, reason); + } +} + +contract ConnectV2InstadappGovernanceBravo is Resolver { + string public constant name = "Instadapp-governance-bravo-v1"; +} From e64b517e2ae74d5520dd7340354c218f3e45b457 Mon Sep 17 00:00:00 2001 From: Thrilok Kumar Date: Wed, 23 Jun 2021 15:23:35 +0530 Subject: [PATCH 2/4] Updated contract addresses --- contracts/mainnet/connectors/INST/helpers.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/mainnet/connectors/INST/helpers.sol b/contracts/mainnet/connectors/INST/helpers.sol index 7bdab7a1..f923efdf 100644 --- a/contracts/mainnet/connectors/INST/helpers.sol +++ b/contracts/mainnet/connectors/INST/helpers.sol @@ -9,10 +9,10 @@ abstract contract Helpers is DSMath, Basic { /** * @dev InstaGovernorBravo */ - InstaGovernorInterface internal constant instaGovernor = InstaGovernorInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); + InstaGovernorInterface internal constant instaGovernor = InstaGovernorInterface(0x0204Cd037B2ec03605CFdFe482D8e257C765fA1B); /** * @dev INST Token */ - InstaTokenInterface internal constant instToken = InstaTokenInterface(0xc00e94Cb662C3520282E6f5717214004A7f26888); + InstaTokenInterface internal constant instToken = InstaTokenInterface(0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb); } \ No newline at end of file From 2dc6eaa4bca7e92d1098977022e76ea97b107da0 Mon Sep 17 00:00:00 2001 From: Thrilok Kumar Date: Wed, 23 Jun 2021 15:48:32 +0530 Subject: [PATCH 3/4] Updated connector address --- docs/connectors.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/connectors.json b/docs/connectors.json index 1f2634a7..6acb543d 100644 --- a/docs/connectors.json +++ b/docs/connectors.json @@ -19,7 +19,8 @@ "COMPOUND-IMPORT-B": "0xdA101870ca6136539628F28041E1B55baf4EB6C0", "INSTAPOOL-A": "0x5806Af7AB22E2916fA582Ff05731Bf7C682387B2", "MAKERDAO-CLAIM-A": "0x2f8cBE650af98602a215b6482F2aD60893C5A4E8", - "WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3" + "WETH-A": "0x22075fa719eFb02Ca3cF298AFa9C974B7465E5D3", + "INST-A": "0x52C2C4a0db049255fF345EB9D3Fb1f555b7a924A" }, "137" : { "AAVE-V2-A": "0xE84d8010Afc3663919F44685cB53ED88866da3eE", From 1cce1fffeada8401bb45b59e725f5f9f7fdf3c88 Mon Sep 17 00:00:00 2001 From: Thrilok Kumar Date: Thu, 24 Jun 2021 16:58:01 +0530 Subject: [PATCH 4/4] Added flashloan testcases --- hardhat.config.js | 2 +- .../constant/abi/connectors/instapool.json | 1 + scripts/constant/abis.js | 1 + scripts/constant/addresses.js | 5 +- scripts/encodeFlashcastData.js | 16 +++ test/instapool/instapool.test.js | 109 ++++++++++++++++++ 6 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 scripts/constant/abi/connectors/instapool.json create mode 100644 scripts/encodeFlashcastData.js create mode 100644 test/instapool/instapool.test.js diff --git a/hardhat.config.js b/hardhat.config.js index 45b17f9d..a3d99c31 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -52,7 +52,7 @@ module.exports = { hardhat: { forking: { url: `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_ID}`, - blockNumber: 12433781, + blockNumber: 12696000, }, blockGasLimit: 12000000, }, diff --git a/scripts/constant/abi/connectors/instapool.json b/scripts/constant/abi/connectors/instapool.json new file mode 100644 index 00000000..75a04f42 --- /dev/null +++ b/scripts/constant/abi/connectors/instapool.json @@ -0,0 +1 @@ +[{"type":"event","name":"LogFlashBorrow","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiBorrow","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiPayback","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashPayback","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashBorrowAndCast","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiBorrowAndCast","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiPayback","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256[]","name":"getId","internalType":"uint256[]"},{"type":"uint256[]","name":"setId","internalType":"uint256[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashPayback","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"getId","internalType":"uint256"},{"type":"uint256","name":"setId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract InstaFlashV2Interface"}],"name":"instaPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]}] \ No newline at end of file diff --git a/scripts/constant/abis.js b/scripts/constant/abis.js index c25bd350..3ed03f80 100644 --- a/scripts/constant/abis.js +++ b/scripts/constant/abis.js @@ -6,6 +6,7 @@ module.exports = { connectors: { basic: require("./abi/connectors/basic.json"), auth: require("./abi/connectors/auth.json"), + "INSTAPOOL-A": require("./abi/connectors/instapool.json"), }, basic: { erc20: require("./abi/basics/erc20.json"), diff --git a/scripts/constant/addresses.js b/scripts/constant/addresses.js index ead09c40..a1cb7d45 100644 --- a/scripts/constant/addresses.js +++ b/scripts/constant/addresses.js @@ -2,10 +2,11 @@ module.exports = { connectors: { basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6", auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9", + "INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2" }, core: { - connectorsV2: "0xFE2390DAD597594439f218190fC2De40f9Cf1179", - instaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723" + connectorsV2: "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11", + instaIndex: "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723", } }; \ No newline at end of file diff --git a/scripts/encodeFlashcastData.js b/scripts/encodeFlashcastData.js new file mode 100644 index 00000000..a674c1af --- /dev/null +++ b/scripts/encodeFlashcastData.js @@ -0,0 +1,16 @@ +const abis = require("./constant/abis"); +const addresses = require("./constant/addresses"); +const { web3 } = hre; + +const encodeSpells = require("./encodeSpells.js") + + +module.exports = function (spells) { + const encodeSpellsData = encodeSpells(spells); + const targetType = "string[]"; + let argTypes = [targetType, "bytes[]"]; + return web3.eth.abi.encodeParameters(argTypes, [ + encodeSpellsData[0], + encodeSpellsData[1], + ]); +}; diff --git a/test/instapool/instapool.test.js b/test/instapool/instapool.test.js new file mode 100644 index 00000000..c0a0fb81 --- /dev/null +++ b/test/instapool/instapool.test.js @@ -0,0 +1,109 @@ +const { expect } = require("chai"); +const hre = require("hardhat"); +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle + +const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js") +const buildDSAv2 = require("../../scripts/buildDSAv2") +const encodeSpells = require("../../scripts/encodeSpells.js") +const encodeFlashcastData = require("../../scripts/encodeFlashcastData.js") +const getMasterSigner = require("../../scripts/getMasterSigner") + +const addresses = require("../../scripts/constant/addresses"); +const abis = require("../../scripts/constant/abis"); +const constants = require("../../scripts/constant/constant"); +const tokens = require("../../scripts/constant/tokens"); + +const connectV2CompoundArtifacts = require("../../artifacts/contracts/mainnet/connectors/compound/main.sol/ConnectV2Compound.json") + +describe("Instapool", function () { + const connectorName = "COMPOUND-TEST-A" + + let dsaWallet0 + let masterSigner; + let instaConnectorsV2; + let connector; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + masterSigner = await getMasterSigner(wallet3) + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: connectV2CompoundArtifacts, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("Should deposit ETH in Compound", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const flashloanAmount = ethers.utils.parseEther("100") // 100 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + const IdOne = "2878734423" + const IdTwo = "783243246" + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", flashloanAmount, 0, IdOne] + }, + { + connector: connectorName, + method: "withdraw", + args: ["ETH-A", amount, IdOne, IdTwo] + }, + { + connector: "INSTAPOOL-A", + method: "flashPayback", + args: [ethAddress, flashloanAmount, IdTwo, 0], + } + ] + + const calldata = encodeFlashcastData(spells); + + const spells2 = [ + { + connector: "INSTAPOOL-A", + method: "flashBorrowAndCast", + args: [ + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + flashloanAmount, + 0, // route + calldata, + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells2), wallet1.address) + const receipt = await tx.wait() + }); + }) +})