From af38703f01cbf70be0c4af12666d6eaa6d7483fc Mon Sep 17 00:00:00 2001 From: alleycatdev Date: Wed, 10 Mar 2021 19:44:55 -0500 Subject: [PATCH 1/4] adds amm compatability and calls sync() on all liquidity pools after join --- src/core/ElasticDAO.sol | 46 ++++++++++++- src/core/ElasticDAOFactory.sol | 2 +- src/interfaces/IUniswapV2Pair.sol | 96 +++++++++++++++++++++++++++ src/models/DAO.sol | 2 +- src/models/Ecosystem.sol | 2 +- src/models/Token.sol | 2 +- src/models/TokenHolder.sol | 2 +- src/tokens/ElasticGovernanceToken.sol | 2 +- test/elasticDAOTests.js | 25 +++++++ 9 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 src/interfaces/IUniswapV2Pair.sol diff --git a/src/core/ElasticDAO.sol b/src/core/ElasticDAO.sol index 9bf2396..86f7ea8 100644 --- a/src/core/ElasticDAO.sol +++ b/src/core/ElasticDAO.sol @@ -2,13 +2,15 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; +import '../interfaces/IUniswapV2Pair.sol'; + import '../libraries/ElasticMath.sol'; import '../models/DAO.sol'; import '../models/Ecosystem.sol'; import '../models/Token.sol'; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@pie-dao/proxy/contracts/PProxy.sol'; @@ -23,6 +25,7 @@ contract ElasticDAO is ReentrancyGuard { address public ecosystemModelAddress; address public controller; address[] public summoners; + address[] public liquidityPools; bool public initialized; event ElasticGovernanceTokenDeployed(address indexed tokenAddress); @@ -35,6 +38,8 @@ contract ElasticDAO is ReentrancyGuard { uint256 actualAmount ); event JoinDAO(address indexed memberAddress, uint256 shareAmount, uint256 ethAmount); + event LiquidityPoolAdded(address indexed poolAddress); + event LiquidityPoolRemoved(address indexed poolAddress); event SeedDAO(address indexed summonerAddress, uint256 amount); event SummonedDAO(address indexed summonedBy); @@ -129,6 +134,17 @@ contract ElasticDAO is ReentrancyGuard { require(success, 'ElasticDAO: Build DAO Failed'); } + function addLiquidityPool(address _poolAddress) + external + onlyController + nonReentrant + returns (bool) + { + liquidityPools.push(_poolAddress); + + emit LiquidityPoolAdded(_poolAddress); + } + /** * @notice initializes the token of the DAO * @@ -199,6 +215,14 @@ contract ElasticDAO is ReentrancyGuard { emit ExitDAO(msg.sender, _deltaLambda, ethToBeTransfered); } + /** + * @notice this function returns the length of the liquidity pools array + * + */ + function getLiquidityPoolCount() public view returns (uint256) { + return liquidityPools.length; + } + /** * @notice this function is used to join the DAO after it has been summoned * Joining the DAO is syntactically equal to minting _deltaLambda for the function caller. @@ -253,6 +277,10 @@ contract ElasticDAO is ReentrancyGuard { bool success = tokenContract.mintShares(msg.sender, token.maxLambdaPurchase); require(success, 'ElasticDAO: Mint Shares Failed during Join'); + for (uint256 i = 0; i < liquidityPools.length; i += 1) { + IUniswapV2Pair(liquidityPools[i]).sync(); + } + // return extra ETH if (success && msg.value > deltaE) { (success, ) = msg.sender.call{ value: SafeMath.sub(msg.value, deltaE) }(''); @@ -298,6 +326,22 @@ contract ElasticDAO is ReentrancyGuard { } } + function removeLiquidityPool(address _poolAddress) + external + onlyController + nonReentrant + returns (bool) + { + for (uint256 i = 0; i < liquidityPools.length; i += 1) { + if (liquidityPools[i] == _poolAddress) { + liquidityPools[i] = liquidityPools[liquidityPools.length - 1]; + liquidityPools.pop(); + } + } + + emit LiquidityPoolRemoved(_poolAddress); + } + /** * @notice rewards @param _addresess with @param _amounts respectively * diff --git a/src/core/ElasticDAOFactory.sol b/src/core/ElasticDAOFactory.sol index 1b157c2..ca5dd8e 100644 --- a/src/core/ElasticDAOFactory.sol +++ b/src/core/ElasticDAOFactory.sol @@ -5,7 +5,7 @@ pragma experimental ABIEncoderV2; import './ElasticDAO.sol'; import '../models/Ecosystem.sol'; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@pie-dao/proxy/contracts/PProxy.sol'; diff --git a/src/interfaces/IUniswapV2Pair.sol b/src/interfaces/IUniswapV2Pair.sol new file mode 100644 index 0000000..df83aeb --- /dev/null +++ b/src/interfaces/IUniswapV2Pair.sol @@ -0,0 +1,96 @@ +pragma solidity 0.7.2; + +interface IUniswapV2Pair { + event Approval(address indexed owner, address indexed spender, uint256 value); + event Transfer(address indexed from, address indexed to, uint256 value); + + function name() external pure returns (string memory); + + function symbol() external pure returns (string memory); + + function decimals() external pure returns (uint8); + + function totalSupply() external view returns (uint256); + + function balanceOf(address owner) external view returns (uint256); + + function allowance(address owner, address spender) external view returns (uint256); + + function approve(address spender, uint256 value) external returns (bool); + + function transfer(address to, uint256 value) external returns (bool); + + function transferFrom( + address from, + address to, + uint256 value + ) external returns (bool); + + function DOMAIN_SEPARATOR() external view returns (bytes32); + + function PERMIT_TYPEHASH() external pure returns (bytes32); + + function nonces(address owner) external view returns (uint256); + + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, + address indexed to + ); + event Sync(uint112 reserve0, uint112 reserve1); + + function MINIMUM_LIQUIDITY() external pure returns (uint256); + + function factory() external view returns (address); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function getReserves() + external + view + returns ( + uint112 reserve0, + uint112 reserve1, + uint32 blockTimestampLast + ); + + function price0CumulativeLast() external view returns (uint256); + + function price1CumulativeLast() external view returns (uint256); + + function kLast() external view returns (uint256); + + function mint(address to) external returns (uint256 liquidity); + + function burn(address to) external returns (uint256 amount0, uint256 amount1); + + function swap( + uint256 amount0Out, + uint256 amount1Out, + address to, + bytes calldata data + ) external; + + function skim(address to) external; + + function sync() external; + + function initialize(address, address) external; +} diff --git a/src/models/DAO.sol b/src/models/DAO.sol index 293a095..ff59f73 100644 --- a/src/models/DAO.sol +++ b/src/models/DAO.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import './Ecosystem.sol'; import './EternalModel.sol'; diff --git a/src/models/Ecosystem.sol b/src/models/Ecosystem.sol index 45c33e4..9dde4b6 100644 --- a/src/models/Ecosystem.sol +++ b/src/models/Ecosystem.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import './EternalModel.sol'; diff --git a/src/models/Token.sol b/src/models/Token.sol index 6fb7c1d..e671023 100644 --- a/src/models/Token.sol +++ b/src/models/Token.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPLv3 pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import './Ecosystem.sol'; import './EternalModel.sol'; diff --git a/src/models/TokenHolder.sol b/src/models/TokenHolder.sol index 3518045..7c27550 100644 --- a/src/models/TokenHolder.sol +++ b/src/models/TokenHolder.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import './Ecosystem.sol'; import './EternalModel.sol'; diff --git a/src/tokens/ElasticGovernanceToken.sol b/src/tokens/ElasticGovernanceToken.sol index e1847cf..772d7aa 100644 --- a/src/tokens/ElasticGovernanceToken.sol +++ b/src/tokens/ElasticGovernanceToken.sol @@ -12,7 +12,7 @@ import '../models/Ecosystem.sol'; import '../models/Token.sol'; import '../models/TokenHolder.sol'; -import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; /** * @dev ElasticGovernanceToken contract outlines and defines all the functionality diff --git a/test/elasticDAOTests.js b/test/elasticDAOTests.js index 21f18ca..abfa136 100644 --- a/test/elasticDAOTests.js +++ b/test/elasticDAOTests.js @@ -296,6 +296,31 @@ describe('ElasticDAO: Core', () => { expect(controller).to.equal(summoner1.address); }); + it('Should allow the controller to add a liquidity pool', async () => { + const { agent } = await signers(); + dao.sdk.changeSigner(agent); + + await dao.elasticDAO.contract.addLiquidityPool('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'); + + const liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); + expect(liquidityPoolsCount).to.equal(1); + }); + + it('Should allow the controller to remove a liquidity pool', async () => { + const { agent } = await signers(); + dao.sdk.changeSigner(agent); + + await dao.elasticDAO.contract.addLiquidityPool('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'); + let liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); + expect(liquidityPoolsCount).to.equal(1); + + await dao.elasticDAO.contract.removeLiquidityPool( + '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F', + ); + liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); + expect(liquidityPoolsCount).to.equal(0); + }); + it('Should allow the controller to setMaxVotingLambda', async () => { const { agent } = await signers(); dao.sdk.changeSigner(agent); From 86ee410021e19c0538745e662ca2e90a984a4d6c Mon Sep 17 00:00:00 2001 From: alleycatdev Date: Sat, 13 Mar 2021 16:13:26 -0500 Subject: [PATCH 2/4] WIP --- src/core/ElasticDAO.sol | 7 ++- src/interfaces/IAMMPair.sol | 6 ++ src/interfaces/IUniswapV2Pair.sol | 96 ------------------------------- test/elasticDAOTests.js | 2 +- 4 files changed, 12 insertions(+), 99 deletions(-) create mode 100644 src/interfaces/IAMMPair.sol delete mode 100644 src/interfaces/IUniswapV2Pair.sol diff --git a/src/core/ElasticDAO.sol b/src/core/ElasticDAO.sol index 86f7ea8..d87d702 100644 --- a/src/core/ElasticDAO.sol +++ b/src/core/ElasticDAO.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import '../interfaces/IUniswapV2Pair.sol'; +import '../interfaces/IAMMPair.sol'; import '../libraries/ElasticMath.sol'; @@ -13,6 +13,7 @@ import '../models/Token.sol'; import '@openzeppelin/contracts/utils/ReentrancyGuard.sol'; import '@pie-dao/proxy/contracts/PProxy.sol'; +import 'hardhat/console.sol'; /** * @dev The ElasticDAO contract outlines and defines all the functionality @@ -140,6 +141,8 @@ contract ElasticDAO is ReentrancyGuard { nonReentrant returns (bool) { + IAMMPair(_poolAddress).sync(); + liquidityPools.push(_poolAddress); emit LiquidityPoolAdded(_poolAddress); @@ -278,7 +281,7 @@ contract ElasticDAO is ReentrancyGuard { require(success, 'ElasticDAO: Mint Shares Failed during Join'); for (uint256 i = 0; i < liquidityPools.length; i += 1) { - IUniswapV2Pair(liquidityPools[i]).sync(); + IAMMPair(liquidityPools[i]).sync(); } // return extra ETH diff --git a/src/interfaces/IAMMPair.sol b/src/interfaces/IAMMPair.sol new file mode 100644 index 0000000..0997a26 --- /dev/null +++ b/src/interfaces/IAMMPair.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPLv3 +pragma solidity 0.7.2; + +interface IAMMPair { + function sync() external; +} diff --git a/src/interfaces/IUniswapV2Pair.sol b/src/interfaces/IUniswapV2Pair.sol deleted file mode 100644 index df83aeb..0000000 --- a/src/interfaces/IUniswapV2Pair.sol +++ /dev/null @@ -1,96 +0,0 @@ -pragma solidity 0.7.2; - -interface IUniswapV2Pair { - event Approval(address indexed owner, address indexed spender, uint256 value); - event Transfer(address indexed from, address indexed to, uint256 value); - - function name() external pure returns (string memory); - - function symbol() external pure returns (string memory); - - function decimals() external pure returns (uint8); - - function totalSupply() external view returns (uint256); - - function balanceOf(address owner) external view returns (uint256); - - function allowance(address owner, address spender) external view returns (uint256); - - function approve(address spender, uint256 value) external returns (bool); - - function transfer(address to, uint256 value) external returns (bool); - - function transferFrom( - address from, - address to, - uint256 value - ) external returns (bool); - - function DOMAIN_SEPARATOR() external view returns (bytes32); - - function PERMIT_TYPEHASH() external pure returns (bytes32); - - function nonces(address owner) external view returns (uint256); - - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external; - - event Mint(address indexed sender, uint256 amount0, uint256 amount1); - event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); - event Swap( - address indexed sender, - uint256 amount0In, - uint256 amount1In, - uint256 amount0Out, - uint256 amount1Out, - address indexed to - ); - event Sync(uint112 reserve0, uint112 reserve1); - - function MINIMUM_LIQUIDITY() external pure returns (uint256); - - function factory() external view returns (address); - - function token0() external view returns (address); - - function token1() external view returns (address); - - function getReserves() - external - view - returns ( - uint112 reserve0, - uint112 reserve1, - uint32 blockTimestampLast - ); - - function price0CumulativeLast() external view returns (uint256); - - function price1CumulativeLast() external view returns (uint256); - - function kLast() external view returns (uint256); - - function mint(address to) external returns (uint256 liquidity); - - function burn(address to) external returns (uint256 amount0, uint256 amount1); - - function swap( - uint256 amount0Out, - uint256 amount1Out, - address to, - bytes calldata data - ) external; - - function skim(address to) external; - - function sync() external; - - function initialize(address, address) external; -} diff --git a/test/elasticDAOTests.js b/test/elasticDAOTests.js index abfa136..3af36a8 100644 --- a/test/elasticDAOTests.js +++ b/test/elasticDAOTests.js @@ -286,7 +286,7 @@ describe('ElasticDAO: Core', () => { ); }); - it('Should allow the controller to setController', async () => { + it.only('Should allow the controller to setController', async () => { const { summoner1, agent } = await signers(); dao.sdk.changeSigner(agent); From 0e68f5e7246c16e911271defc444baab2455c0bd Mon Sep 17 00:00:00 2001 From: alleycatdev Date: Sat, 13 Mar 2021 18:18:34 -0500 Subject: [PATCH 3/4] Uniswap Sync --- src/core/ElasticDAO.sol | 6 ++--- .../{IAMMPair.sol => IUniswapV2Pair.sol} | 2 +- test/elasticDAOTests.js | 27 +------------------ 3 files changed, 5 insertions(+), 30 deletions(-) rename src/interfaces/{IAMMPair.sol => IUniswapV2Pair.sol} (76%) diff --git a/src/core/ElasticDAO.sol b/src/core/ElasticDAO.sol index d87d702..0ff09f2 100644 --- a/src/core/ElasticDAO.sol +++ b/src/core/ElasticDAO.sol @@ -2,7 +2,7 @@ pragma solidity 0.7.2; pragma experimental ABIEncoderV2; -import '../interfaces/IAMMPair.sol'; +import '../interfaces/IUniswapV2Pair.sol'; import '../libraries/ElasticMath.sol'; @@ -141,7 +141,7 @@ contract ElasticDAO is ReentrancyGuard { nonReentrant returns (bool) { - IAMMPair(_poolAddress).sync(); + IUniswapV2Pair(_poolAddress).sync(); liquidityPools.push(_poolAddress); @@ -281,7 +281,7 @@ contract ElasticDAO is ReentrancyGuard { require(success, 'ElasticDAO: Mint Shares Failed during Join'); for (uint256 i = 0; i < liquidityPools.length; i += 1) { - IAMMPair(liquidityPools[i]).sync(); + IUniswapV2Pair(liquidityPools[i]).sync(); } // return extra ETH diff --git a/src/interfaces/IAMMPair.sol b/src/interfaces/IUniswapV2Pair.sol similarity index 76% rename from src/interfaces/IAMMPair.sol rename to src/interfaces/IUniswapV2Pair.sol index 0997a26..cf29237 100644 --- a/src/interfaces/IAMMPair.sol +++ b/src/interfaces/IUniswapV2Pair.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPLv3 pragma solidity 0.7.2; -interface IAMMPair { +interface IUniswapV2Pair { function sync() external; } diff --git a/test/elasticDAOTests.js b/test/elasticDAOTests.js index 3af36a8..21f18ca 100644 --- a/test/elasticDAOTests.js +++ b/test/elasticDAOTests.js @@ -286,7 +286,7 @@ describe('ElasticDAO: Core', () => { ); }); - it.only('Should allow the controller to setController', async () => { + it('Should allow the controller to setController', async () => { const { summoner1, agent } = await signers(); dao.sdk.changeSigner(agent); @@ -296,31 +296,6 @@ describe('ElasticDAO: Core', () => { expect(controller).to.equal(summoner1.address); }); - it('Should allow the controller to add a liquidity pool', async () => { - const { agent } = await signers(); - dao.sdk.changeSigner(agent); - - await dao.elasticDAO.contract.addLiquidityPool('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'); - - const liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); - expect(liquidityPoolsCount).to.equal(1); - }); - - it('Should allow the controller to remove a liquidity pool', async () => { - const { agent } = await signers(); - dao.sdk.changeSigner(agent); - - await dao.elasticDAO.contract.addLiquidityPool('0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F'); - let liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); - expect(liquidityPoolsCount).to.equal(1); - - await dao.elasticDAO.contract.removeLiquidityPool( - '0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F', - ); - liquidityPoolsCount = await dao.elasticDAO.contract.getLiquidityPoolCount(); - expect(liquidityPoolsCount).to.equal(0); - }); - it('Should allow the controller to setMaxVotingLambda', async () => { const { agent } = await signers(); dao.sdk.changeSigner(agent); From 744eb0ba100098a2e6b3f8f5d5fa08a314c4ff41 Mon Sep 17 00:00:00 2001 From: Dan Matthews Date: Sun, 14 Mar 2021 02:14:27 +0200 Subject: [PATCH 4/4] Update ElasticDAO.sol --- src/core/ElasticDAO.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/ElasticDAO.sol b/src/core/ElasticDAO.sol index 0ff09f2..07acb24 100644 --- a/src/core/ElasticDAO.sol +++ b/src/core/ElasticDAO.sol @@ -141,8 +141,6 @@ contract ElasticDAO is ReentrancyGuard { nonReentrant returns (bool) { - IUniswapV2Pair(_poolAddress).sync(); - liquidityPools.push(_poolAddress); emit LiquidityPoolAdded(_poolAddress);