From 02e78e7dd33cc9c7d19a997bfaad0ac1a810b1f7 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 20 Aug 2021 15:08:13 -1000 Subject: [PATCH 1/2] Tests pass --- .../exchange/UniswapV2ExchangeAdapter.sol | 54 +++++++++++-------- utils/contracts/index.ts | 1 + utils/deploys/deployAdapters.ts | 12 ++++- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/contracts/protocol/integration/exchange/UniswapV2ExchangeAdapter.sol b/contracts/protocol/integration/exchange/UniswapV2ExchangeAdapter.sol index 2ec24c4..9b77801 100644 --- a/contracts/protocol/integration/exchange/UniswapV2ExchangeAdapter.sol +++ b/contracts/protocol/integration/exchange/UniswapV2ExchangeAdapter.sol @@ -34,7 +34,7 @@ contract UniswapV2ExchangeAdapter { // YOUR CODE HERE - + address public immutable router; /* ============= Constructor ============= */ @@ -49,6 +49,9 @@ contract UniswapV2ExchangeAdapter { // YOUR CODE HERE + constructor(address _router) public { + router = _router; + } @@ -75,7 +78,14 @@ contract UniswapV2ExchangeAdapter { * The function will return 3 values: address of the uniswap router, 0 for Call value, trade calldata */ - function getTradeCalldata(/*YOUR CODE HERE*/) external view returns (/*YOUR CODE HERE*/){ + function getTradeCalldata( + address _sourceToken, + address _destinationToken, + address _destinationAddress, + uint256 _sourceQuantity, + uint256 _minDestinationQuantity, + bytes memory _data + ) external view returns (address, uint256, bytes memory){ /* @@ -89,7 +99,7 @@ contract UniswapV2ExchangeAdapter { path = new address[](2); path[0] = _sourceToken; path[1] = _destinationToken; - }else { + } else { path = abi.decode(_data, (address[])); } @@ -99,28 +109,26 @@ contract UniswapV2ExchangeAdapter { * Please see README.md resources for more details on the Uniswap function swapExactTokensForTokens. */ - // YOUR CODE HERE - - - return (/*YOUR CODE HERE*/); - + bytes memory callData = abi.encodeWithSignature( + "swapExactTokensForTokens(uint256,uint256,address[],address,uint256)", + _sourceQuantity, + _minDestinationQuantity, + path, + _destinationAddress, + block.timestamp + ); + return (router, 0, callData); } - -/* -* Write the getSpender() function that will return the address of our set Uniswap router. -* make sure the function is external view -*/ - -/** -* -* Returns the UniSwap contract address. -* @return address -* -*/ - - // YOUR CODE HERE - + /** + * + * Returns the UniSwap contract address. + * @return address + * + */ + function getSpender() external view returns (address) { + return router; + } } diff --git a/utils/contracts/index.ts b/utils/contracts/index.ts index 578c03a..b3b8991 100644 --- a/utils/contracts/index.ts +++ b/utils/contracts/index.ts @@ -95,3 +95,4 @@ export { YearnWrapAdapter } from "../../typechain/YearnWrapAdapter"; export { YearnStrategyMock } from "../../typechain/YearnStrategyMock"; export { ZeroExApiAdapter } from "../../typechain/ZeroExApiAdapter"; export { ZeroExMock } from "../../typechain/ZeroExMock"; +export { UniswapV2ExchangeAdapter } from "../../typechain/UniswapV2ExchangeAdapter"; diff --git a/utils/deploys/deployAdapters.ts b/utils/deploys/deployAdapters.ts index ef18a15..642ef01 100644 --- a/utils/deploys/deployAdapters.ts +++ b/utils/deploys/deployAdapters.ts @@ -15,6 +15,7 @@ import { CompoundWrapAdapter, YearnWrapAdapter, UniswapPairPriceAdapter, + UniswapV2ExchangeAdapter, UniswapV2IndexExchangeAdapter, UniswapV2TransferFeeExchangeAdapter, ZeroExApiAdapter, @@ -45,6 +46,7 @@ import { UniswapV2IndexExchangeAdapter__factory } from "../../typechain/factorie import { SynthetixExchangeAdapter__factory } from "../../typechain/factories/SynthetixExchangeAdapter__factory"; import { CompoundBravoGovernanceAdapter__factory } from "../../typechain/factories/CompoundBravoGovernanceAdapter__factory"; import { CompClaimAdapter__factory } from "../../typechain"; +import { UniswapV2ExchangeAdapter__factory } from "../../typechain/factories/UniswapV2ExchangeAdapter__factory"; export default class DeployAdapters { private _deployerSigner: Signer; @@ -170,4 +172,12 @@ export default class DeployAdapters { synthetixExchangerAddress ); } -} + + public async deployUniswapV2ExchangeAdapter( + router: Address + ): Promise { + return await new UniswapV2ExchangeAdapter__factory(this._deployerSigner).deploy( + router + ); + } +} \ No newline at end of file From 3ebda7fe35b757f0df0f32a2313c41a139220422 Mon Sep 17 00:00:00 2001 From: John Date: Tue, 24 Aug 2021 11:36:14 -1000 Subject: [PATCH 2/2] Quest 2 complete --- .../governance/SnapshotGovernanceAdapter.sol | 61 ++++++++++++++ external/abi/snapshot/DelegateRegistry.json | 8 ++ .../snapshotGovernanceAdapter.spec.ts | 81 +++++++++++++++++++ utils/contracts/index.ts | 2 + utils/deploys/deployAdapters.ts | 10 +++ utils/deploys/deployExternal.ts | 8 ++ 6 files changed, 170 insertions(+) create mode 100644 contracts/protocol/integration/governance/SnapshotGovernanceAdapter.sol create mode 100644 external/abi/snapshot/DelegateRegistry.json create mode 100644 test/protocol/integration/governance/snapshotGovernanceAdapter.spec.ts diff --git a/contracts/protocol/integration/governance/SnapshotGovernanceAdapter.sol b/contracts/protocol/integration/governance/SnapshotGovernanceAdapter.sol new file mode 100644 index 0000000..c5909cc --- /dev/null +++ b/contracts/protocol/integration/governance/SnapshotGovernanceAdapter.sol @@ -0,0 +1,61 @@ +pragma solidity 0.6.10; +pragma experimental "ABIEncoderV2"; + +/** +* @title SnapshotGovernanceAdapter +* @author John Hearn +*/ + +contract SnapshotGovernanceAdapter { + address public immutable delegateRegistry; + + /** + * Set the delegator state variable + * + * @param _delegateRegistry Address of DelegateRegistry contract + */ + constructor(address _delegateRegistry) public { + delegateRegistry = _delegateRegistry; + } + + /** + * Return calldata for DelegateRegistry when calling the setDelegate method + * + * @return address Target contract address + * @return uint256 Call value + * @return bytes Trade calldata + */ + function getDelegateCalldata(address _delegatee) + external + view + returns(address, uint256, bytes memory) + { + bytes memory callData = abi.encodeWithSignature( + "setDelegate(bytes32,address)", + bytes32(0), + _delegatee + ); + + return (delegateRegistry, 0, callData); + } + + /** + * Return calldata for DelegateRegistry when calling the clearDelegate method + * + * @return address Target contract address + * @return uint256 Call value + * @return bytes Trade calldata + */ + function getRevokeCalldata() + external + view + returns (address, uint256, bytes memory) + { + bytes memory callData = abi.encodeWithSignature( + "clearDelegate(bytes32)", + bytes32(0) + ); + + return (delegateRegistry, 0, callData); + } +} \ No newline at end of file diff --git a/external/abi/snapshot/DelegateRegistry.json b/external/abi/snapshot/DelegateRegistry.json new file mode 100644 index 0000000..8e29376 --- /dev/null +++ b/external/abi/snapshot/DelegateRegistry.json @@ -0,0 +1,8 @@ +{ + "contractName": "DelegrateRegistry", + "abi": [{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"ClearDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"id","type":"bytes32"},{"indexed":true,"internalType":"address","name":"delegate","type":"address"}],"name":"SetDelegate","type":"event"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"clearDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"delegation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"},{"internalType":"address","name":"delegate","type":"address"}],"name":"setDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"}], + "bytecode": "608060405234801561001057600080fd5b50610794806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806374c6c45414610046578063bd86e508146100be578063f0bedbe21461010c575b600080fd5b6100926004803603604081101561005c57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061013a565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61010a600480360360408110156100d457600080fd5b8101908080359060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061017c565b005b6101386004803603602081101561012257600080fd5b8101908080359060200190929190505050610538565b005b60006020528160005260406000206020528060005260406000206000915091509054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561021e576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260168152602001807f43616e27742064656c656761746520746f2073656c660000000000000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156102c1576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260158152602001807f43616e27742064656c656761746520746f20307830000000000000000000000081525060200191505060405180910390fd5b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690508073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1614156103ba576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602181526020018061073e6021913960400191505060405180910390fd5b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600085815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16146104d8578073ffffffffffffffffffffffffffffffffffffffff16833373ffffffffffffffffffffffffffffffffffffffff167f9c4f00c4291262731946e308dc2979a56bd22cce8f95906b975065e96cd5a06460405160405180910390a45b8173ffffffffffffffffffffffffffffffffffffffff16833373ffffffffffffffffffffffffffffffffffffffff167fa9a7fd460f56bddb880a465a9c3e9730389c70bc53108148f16d55a87a6c468e60405160405180910390a4505050565b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600083815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16141561064f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600f8152602001807f4e6f2064656c656761746520736574000000000000000000000000000000000081525060200191505060405180910390fd5b60008060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600084815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508073ffffffffffffffffffffffffffffffffffffffff16823373ffffffffffffffffffffffffffffffffffffffff167f9c4f00c4291262731946e308dc2979a56bd22cce8f95906b975065e96cd5a06460405160405180910390a4505056fe416c72656164792064656c65676174656420746f20746869732061646472657373a2646970667358221220b6cd5a8d04426e1189563fbec7dfec4ba70090dc70fe05097a137991fe1b396964736f6c63430007020033", + "deployedBytecode": "", + "linkedReferences": {}, + "deployedLinkReferences": {} +} \ No newline at end of file diff --git a/test/protocol/integration/governance/snapshotGovernanceAdapter.spec.ts b/test/protocol/integration/governance/snapshotGovernanceAdapter.spec.ts new file mode 100644 index 0000000..97d18b3 --- /dev/null +++ b/test/protocol/integration/governance/snapshotGovernanceAdapter.spec.ts @@ -0,0 +1,81 @@ +import "module-alias/register"; + +import { Account } from "@utils/test/types"; +import { Address } from "@utils/types"; +import DeployHelper from "@utils/deploys"; +import { DelegateRegistry, SnapshotGovernanceAdapter } from "@utils/contracts"; +import { addSnapshotBeforeRestoreAfterEach, getAccounts, getWaffleExpect } from "@utils/test/index"; +import { ZERO, ZERO_BYTES } from "@utils/constants"; + +const expect = getWaffleExpect(); + +describe("SnapshotGovernanceAdapter", () => { + let delegate: Account; + let owner: Account; + let delegateRegistry: DelegateRegistry; + let deployer: DeployHelper; + let snapshotGovAdapt: SnapshotGovernanceAdapter; + + before(async() => { + [ + delegate, + owner, + ] = await getAccounts(); + deployer = new DeployHelper(owner.wallet); + }); + + beforeEach(async() => { + delegateRegistry = await deployer.external.deployDelegateRegistry(); + snapshotGovAdapt = await deployer.adapters.deploySnapshotGovernanceAdapter( + delegateRegistry.address + ); + }); + + addSnapshotBeforeRestoreAfterEach(); + + describe("constructor", async() => { + let subjectDelegateRegistry: Address; + beforeEach(async() => { + subjectDelegateRegistry = delegateRegistry.address; + }); + + async function subject(): Promise { + return await deployer.adapters.deploySnapshotGovernanceAdapter(subjectDelegateRegistry); + } + + it("should properly store the delegate registry address in the contructor", async() => { + const deployedAdapter = await subject(); + const delegateAddress = await deployedAdapter.delegateRegistry(); + expect(delegateAddress).to.eq(delegateRegistry.address); + }); + }); + + describe("getDelegateCalldata", async() => { + async function subject(): Promise { + return await snapshotGovAdapt.getDelegateCalldata(delegate.address); + } + + it("should return the right call data", async() => { + const callData = await subject(); + const expectedCallData = delegateRegistry.interface.encodeFunctionData("setDelegate", [ + ZERO_BYTES, + delegate.address, + ]); + expect(JSON.stringify(callData)).to.eq(JSON.stringify([delegateRegistry.address, ZERO, expectedCallData])); + }); + }); + + describe("getRevokeCalldata", async() => { + async function subject(): Promise { + return await snapshotGovAdapt.getRevokeCalldata(); + } + + it("should return the right call data", async() => { + const callData = await subject(); + const expectedCallData = delegateRegistry.interface.encodeFunctionData("clearDelegate", [ + ZERO_BYTES, + ]); + expect(JSON.stringify(callData)).to.eq(JSON.stringify([delegateRegistry.address, ZERO, expectedCallData])); + }); + }); +}); \ No newline at end of file diff --git a/utils/contracts/index.ts b/utils/contracts/index.ts index b3b8991..074c012 100644 --- a/utils/contracts/index.ts +++ b/utils/contracts/index.ts @@ -67,6 +67,7 @@ export { SetToken } from "../../typechain/SetToken"; export { SetTokenCreator } from "../../typechain/SetTokenCreator"; export { SetValuer } from "../../typechain/SetValuer"; export { SingleIndexModule } from "../../typechain/SingleIndexModule"; +export { SnapshotGovernanceAdapter } from "../../typechain/SnapshotGovernanceAdapter"; export { StakingAdapterMock } from "../../typechain/StakingAdapterMock"; export { StakingModule } from "../../typechain/StakingModule"; export { StakingRewards } from "../../typechain/StakingRewards"; @@ -96,3 +97,4 @@ export { YearnStrategyMock } from "../../typechain/YearnStrategyMock"; export { ZeroExApiAdapter } from "../../typechain/ZeroExApiAdapter"; export { ZeroExMock } from "../../typechain/ZeroExMock"; export { UniswapV2ExchangeAdapter } from "../../typechain/UniswapV2ExchangeAdapter"; +export { DelegateRegistry } from "../../typechain/DelegateRegistry"; \ No newline at end of file diff --git a/utils/deploys/deployAdapters.ts b/utils/deploys/deployAdapters.ts index 642ef01..ab9e40d 100644 --- a/utils/deploys/deployAdapters.ts +++ b/utils/deploys/deployAdapters.ts @@ -19,6 +19,7 @@ import { UniswapV2IndexExchangeAdapter, UniswapV2TransferFeeExchangeAdapter, ZeroExApiAdapter, + SnapshotGovernanceAdapter, SynthetixExchangeAdapter, CompoundBravoGovernanceAdapter, CompClaimAdapter, @@ -47,6 +48,7 @@ import { SynthetixExchangeAdapter__factory } from "../../typechain/factories/Syn import { CompoundBravoGovernanceAdapter__factory } from "../../typechain/factories/CompoundBravoGovernanceAdapter__factory"; import { CompClaimAdapter__factory } from "../../typechain"; import { UniswapV2ExchangeAdapter__factory } from "../../typechain/factories/UniswapV2ExchangeAdapter__factory"; +import { SnapshotGovernanceAdapter__factory } from "../../typechain/factories/SnapshotGovernanceAdapter__factory"; export default class DeployAdapters { private _deployerSigner: Signer; @@ -180,4 +182,12 @@ export default class DeployAdapters { router ); } + + public async deploySnapshotGovernanceAdapter( + delegator: Address + ): Promise { + return await new SnapshotGovernanceAdapter__factory(this._deployerSigner).deploy( + delegator + ); + } } \ No newline at end of file diff --git a/utils/deploys/deployExternal.ts b/utils/deploys/deployExternal.ts index 58076ef..20858d8 100644 --- a/utils/deploys/deployExternal.ts +++ b/utils/deploys/deployExternal.ts @@ -18,6 +18,7 @@ import { } from "./../contracts/compound"; import { WETH9, + DelegateRegistry, } from "./../contracts"; import { Address } from "./../types"; @@ -36,6 +37,7 @@ import { Unitroller__factory } from "../../typechain/factories/Unitroller__facto import { WETH9__factory } from "../../typechain/factories/WETH9__factory"; import { WhitePaperInterestRateModel__factory } from "../../typechain/factories/WhitePaperInterestRateModel__factory"; import { LendingPoolAddressesProvider__factory } from "../../typechain/factories/LendingPoolAddressesProvider__factory"; +import { DelegateRegistry__factory } from "../../typechain/factories/DelegateRegistry__factory"; import { AaveGovernanceV2, @@ -591,4 +593,10 @@ export default class DeployExternalContracts { public async deployNFTDescriptor(): Promise { return await new NFTDescriptor__factory(this._deployerSigner).deploy(); } + + public async deployDelegateRegistry( + ): Promise { + return await new DelegateRegistry__factory(this._deployerSigner).deploy(); + } + }