From 0aac765efd798a607a8f6482f1822248c03fa4a9 Mon Sep 17 00:00:00 2001 From: mcchan1 Date: Mon, 28 Jun 2021 15:42:52 -0400 Subject: [PATCH 1/4] A more generic chainlink financing Goal is to accommodate any 8 digit (eg. ETH/USD) or 18 digit priceFeed from Chainlink (e.g. USDC/ETH). - made priceFeed public - create decimalsChainlink = priceFeed.decimals() - checks in processProposal depending on whether its 8 or 18 digits decimals --- contracts/adapters/FinancingChainlink.sol | 46 ++++++++++++++++++----- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/contracts/adapters/FinancingChainlink.sol b/contracts/adapters/FinancingChainlink.sol index 7592f14bc..42b37bf57 100644 --- a/contracts/adapters/FinancingChainlink.sol +++ b/contracts/adapters/FinancingChainlink.sol @@ -41,7 +41,10 @@ contract FinancingChainlinkContract is MemberGuard, AdapterGuard { - AggregatorV3Interface internal _priceFeed; + //move from internal to public + AggregatorV3Interface public priceFeed; + //decimals from Chainlink priceFeed.decimals(); + uint8 public decimalsChainlink; /** * @notice choose a priceFeed contract, see https://docs.chain.link/docs/ethereum-addresses @@ -51,8 +54,17 @@ contract FinancingChainlinkContract is * Mainnet Contract Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 */ - constructor(address feedAddress) { - _priceFeed = AggregatorV3Interface(feedAddress); + // constructor(address feedAddress) { + // _priceFeed = AggregatorV3Interface(feedAddress); + // } + + function configureDao(DaoRegistry dao, address _priceFeed) + external + onlyAdapter(dao) + { + priceFeed = AggregatorV3Interface(_priceFeed); + decimalsChainlink = priceFeed.decimals(); + //add require here in case chainlink issues a pricefeed does not have 8 or 18 decimals? } struct ProposalDetails { @@ -146,11 +158,27 @@ contract FinancingChainlinkContract is dao.processProposal(proposalId); BankExtension bank = BankExtension(dao.getExtensionAddress(BANK)); - //enter US Dollar = details.amount to convert it into ETH - uint256 amount = _usdToEth(details.amount); - - bank.subtractFromBalance(GUILD, details.token, amount); - bank.addToBalance(details.applicant, details.token, amount); + //if - elif - else block to check chainlink pricefeeds + + //if decimalsChainlink = 8 decimals, than use conversion + if (decimalsChainlink == 8) { + //enter US Dollar = details.amount to convert with _usdToETh + uint256 amount = _usdToEth(details.amount); + bank.subtractFromBalance(GUILD, details.token, amount); + bank.addToBalance(details.applicant, details.token, amount); + } + //else if decimalsChainlink = 18, then use the standard financing + else if (decimalsChainlink == 18) { + uint256 amount = details.amount; + bank.subtractFromBalance(GUILD, details.token, amount); + bank.addToBalance(details.applicant, details.token, amount); + } + //if decimalsChainlink does equal 8 or 18, then revert + else { + revert( + "chainlink pricefeed is not compatible, must be 8 or 18 decimals" + ); + } } /** @@ -165,7 +193,7 @@ contract FinancingChainlinkContract is uint256 startedAt, uint256 updatedAt, uint80 answeredInRound - ) = _priceFeed.latestRoundData(); + ) = priceFeed.latestRoundData(); return answer; } From 026e077839d1fc2d26a4429303938956b9c4241e Mon Sep 17 00:00:00 2001 From: Felipe Forbeck Date: Mon, 28 Jun 2021 18:05:47 -0300 Subject: [PATCH 2/4] updated the configureDao function and processProposal --- contracts/adapters/FinancingChainlink.sol | 87 +++++++++++------------ 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/contracts/adapters/FinancingChainlink.sol b/contracts/adapters/FinancingChainlink.sol index 42b37bf57..00862c876 100644 --- a/contracts/adapters/FinancingChainlink.sol +++ b/contracts/adapters/FinancingChainlink.sol @@ -42,9 +42,10 @@ contract FinancingChainlinkContract is AdapterGuard { //move from internal to public - AggregatorV3Interface public priceFeed; + bytes32 internal constant _PRICE_FEED_ADDRESS_CONFIG = + keccak256("finance-chainlink:price-feed:address"); + //decimals from Chainlink priceFeed.decimals(); - uint8 public decimalsChainlink; /** * @notice choose a priceFeed contract, see https://docs.chain.link/docs/ethereum-addresses @@ -57,16 +58,6 @@ contract FinancingChainlinkContract is // constructor(address feedAddress) { // _priceFeed = AggregatorV3Interface(feedAddress); // } - - function configureDao(DaoRegistry dao, address _priceFeed) - external - onlyAdapter(dao) - { - priceFeed = AggregatorV3Interface(_priceFeed); - decimalsChainlink = priceFeed.decimals(); - //add require here in case chainlink issues a pricefeed does not have 8 or 18 decimals? - } - struct ProposalDetails { address applicant; // the proposal applicant address, can not be a reserved address uint256 amount; // the amount requested for funding @@ -83,6 +74,19 @@ contract FinancingChainlinkContract is revert("fallback revert"); } + function configureDao(DaoRegistry dao, address priceFeedAddr) + external + onlyAdapter(dao) + { + AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddr); + uint8 decimalsChainlink = priceFeed.decimals(); + require( + decimalsChainlink != 8 && decimalsChainlink != 18, + "chainlink pricefeed is not compatible, must be 8 or 18 decimals" + ); + dao.setAddressConfiguration(_PRICE_FEED_ADDRESS_CONFIG, priceFeedAddr); + } + /** * @notice Creates and sponsors a financing proposal. * @dev Applicant address must not be reserved. @@ -155,38 +159,39 @@ contract FinancingChainlinkContract is IVoting.VotingState.PASS, "proposal needs to pass" ); + + address priceFeedAddr = + dao.getAddressConfiguration(_PRICE_FEED_ADDRESS_CONFIG); + dao.processProposal(proposalId); BankExtension bank = BankExtension(dao.getExtensionAddress(BANK)); - //if - elif - else block to check chainlink pricefeeds - - //if decimalsChainlink = 8 decimals, than use conversion + AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddr); + uint8 decimalsChainlink = priceFeed.decimals(); + uint256 amount = details.amount; + address token = details.token; + address applicant = details.applicant; if (decimalsChainlink == 8) { //enter US Dollar = details.amount to convert with _usdToETh - uint256 amount = _usdToEth(details.amount); - bank.subtractFromBalance(GUILD, details.token, amount); - bank.addToBalance(details.applicant, details.token, amount); - } - //else if decimalsChainlink = 18, then use the standard financing - else if (decimalsChainlink == 18) { - uint256 amount = details.amount; - bank.subtractFromBalance(GUILD, details.token, amount); - bank.addToBalance(details.applicant, details.token, amount); - } - //if decimalsChainlink does equal 8 or 18, then revert - else { - revert( - "chainlink pricefeed is not compatible, must be 8 or 18 decimals" - ); + uint256 convertedAmount = _usdToEth(amount, priceFeed); + bank.subtractFromBalance(GUILD, token, convertedAmount); + bank.addToBalance(applicant, token, convertedAmount); + } else { + bank.subtractFromBalance(GUILD, token, amount); + bank.addToBalance(applicant, token, amount); } } - /** - * Returns the latest price from AggregatorV3Interface.sol - Chainlink returns the latest price to 8 decimials - e.g. 176471000000 = $1,764.71 + /* + @param _amount = the amount in US Dollars to convert to ETH + multipliers are needed in numerator (usd to wei) and denominator + (convert result from chainlink to wei) */ - function getLatestPrice() public view returns (int256) { + function _usdToEth(uint256 amount, AggregatorV3Interface priceFeed) + internal + view + returns (uint256) + { ( uint80 roundId, int256 answer, @@ -194,20 +199,12 @@ contract FinancingChainlinkContract is uint256 updatedAt, uint80 answeredInRound ) = priceFeed.latestRoundData(); - return answer; - } - /* - @param _amount = the amount in US Dollars to convert to ETH - multipliers are needed in numerator (usd to wei) and denominator - (convert result from chainlink to wei) - */ - function _usdToEth(uint256 _amount) internal view returns (uint256) { //convert int to uint - uint256 denominator = uint256(getLatestPrice()); + uint256 denominator = uint256(answer); //multipliers to ensure values calculated in wei uint256 ethInUsdAmount = - ((_amount * 1000000000000000000000) / denominator) * 100000; + ((amount * 1000000000000000000000) / denominator) * 100000; return ethInUsdAmount; } } From 31836fa007e8ba74b106607b84977b9b1a139c90 Mon Sep 17 00:00:00 2001 From: mcchan1 Date: Tue, 29 Jun 2021 15:42:37 -0400 Subject: [PATCH 3/4] setup for 8 decimal test - change function name `_usdToEth` to `_convertToWeiValue` - return 8 decimals in FakeChainlinkPriceFeed.sol - `it` block in tests --- contracts/adapters/FinancingChainlink.sol | 14 ++++++++------ contracts/test/FakeChainlinkPriceFeed.sol | 4 +++- test/adapters/financing-chainlink.test.js | 5 +++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/contracts/adapters/FinancingChainlink.sol b/contracts/adapters/FinancingChainlink.sol index 00862c876..3544d6b73 100644 --- a/contracts/adapters/FinancingChainlink.sol +++ b/contracts/adapters/FinancingChainlink.sol @@ -41,7 +41,7 @@ contract FinancingChainlinkContract is MemberGuard, AdapterGuard { - //move from internal to public + bytes32 internal constant _PRICE_FEED_ADDRESS_CONFIG = keccak256("finance-chainlink:price-feed:address"); @@ -49,10 +49,11 @@ contract FinancingChainlinkContract is /** * @notice choose a priceFeed contract, see https://docs.chain.link/docs/ethereum-addresses - * Aggregator: ETH/USD + * Aggregator: ETH/USD - 8 decimals * contract address of the price feed from Chainlink * Rinkeby Contract Address: 0x8A753747A1Fa494EC906cE90E9f37563A8AF630e * Mainnet Contract Address: 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419 + * Mainnet Contract USDC/ETH - 18 decimals: 0x986b5E1e1755e3C2440e960477f25201B0a8bbD4 */ // constructor(address feedAddress) { @@ -165,15 +166,16 @@ contract FinancingChainlinkContract is dao.processProposal(proposalId); BankExtension bank = BankExtension(dao.getExtensionAddress(BANK)); - + //Chainlink feed instance AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddr); + //check decimals to see if 8 or 18 uint8 decimalsChainlink = priceFeed.decimals(); uint256 amount = details.amount; address token = details.token; address applicant = details.applicant; if (decimalsChainlink == 8) { - //enter US Dollar = details.amount to convert with _usdToETh - uint256 convertedAmount = _usdToEth(amount, priceFeed); + //enter US Dollar = details.amount and convert with _convertToWeiValue + uint256 convertedAmount = _convertToWeiValue(amount, priceFeed); bank.subtractFromBalance(GUILD, token, convertedAmount); bank.addToBalance(applicant, token, convertedAmount); } else { @@ -187,7 +189,7 @@ contract FinancingChainlinkContract is multipliers are needed in numerator (usd to wei) and denominator (convert result from chainlink to wei) */ - function _usdToEth(uint256 amount, AggregatorV3Interface priceFeed) + function _convertToWeiValue(uint256 amount, AggregatorV3Interface priceFeed) internal view returns (uint256) diff --git a/contracts/test/FakeChainlinkPriceFeed.sol b/contracts/test/FakeChainlinkPriceFeed.sol index e68dd783b..7f7ad23c2 100644 --- a/contracts/test/FakeChainlinkPriceFeed.sol +++ b/contracts/test/FakeChainlinkPriceFeed.sol @@ -3,7 +3,9 @@ pragma solidity ^0.8.0; import "../adapters/interfaces/chainlink/AggregatorV3Interface.sol"; contract FakeChainlinkPriceFeed is AggregatorV3Interface { - uint8 public override decimals; + function decimals() external view virtual override returns (uint8) { + return(8); + } string public override description; diff --git a/test/adapters/financing-chainlink.test.js b/test/adapters/financing-chainlink.test.js index 905916921..c3a0ee2bd 100644 --- a/test/adapters/financing-chainlink.test.js +++ b/test/adapters/financing-chainlink.test.js @@ -467,4 +467,9 @@ describe("Adapter - Financing", () => { expect(err.reason).equal("adapter not found"); } }); + + it("should not be possible to import a priceFeed from Chainlink that is not 8 or 18 decimals", async () => { + console.log("write test for price feed decimals"); + }); + }); From 8d6c5d99a9010ae44b6b10360d5259e03091641e Mon Sep 17 00:00:00 2001 From: mcchan1 Date: Wed, 30 Jun 2021 11:54:50 -0400 Subject: [PATCH 4/4] npm lint fix --- contracts/adapters/FinancingChainlink.sol | 1 - contracts/test/FakeChainlinkPriceFeed.sol | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/adapters/FinancingChainlink.sol b/contracts/adapters/FinancingChainlink.sol index 3544d6b73..19db815e7 100644 --- a/contracts/adapters/FinancingChainlink.sol +++ b/contracts/adapters/FinancingChainlink.sol @@ -41,7 +41,6 @@ contract FinancingChainlinkContract is MemberGuard, AdapterGuard { - bytes32 internal constant _PRICE_FEED_ADDRESS_CONFIG = keccak256("finance-chainlink:price-feed:address"); diff --git a/contracts/test/FakeChainlinkPriceFeed.sol b/contracts/test/FakeChainlinkPriceFeed.sol index 7f7ad23c2..261c5d2d6 100644 --- a/contracts/test/FakeChainlinkPriceFeed.sol +++ b/contracts/test/FakeChainlinkPriceFeed.sol @@ -4,7 +4,7 @@ import "../adapters/interfaces/chainlink/AggregatorV3Interface.sol"; contract FakeChainlinkPriceFeed is AggregatorV3Interface { function decimals() external view virtual override returns (uint8) { - return(8); + return (8); } string public override description;