Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added adapters mappings and helpers #10910

Closed
53 changes: 52 additions & 1 deletion packages/protocol/contracts/common/FeeCurrencyWhitelist.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,17 @@ contract FeeCurrencyWhitelist is
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should update interface as well.

// Array of all the tokens enabled
address[] public whitelist;
// it is not enforce that underlyingTokens in the same order as their underlying
martinvol marked this conversation as resolved.
Show resolved Hide resolved
// whitelisted address
address[] public underlyingTokens;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could make this AddressSet which would allow to add underlyingTokens directly in setAdaptor rather than have special method for it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not work if we want to have multiple adapters for one token ..

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not update this to 0.8 and leave as is

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with current approach is that it is rather manual.

You can setAdapter and you can forget to setUnderlyingToken which will result in inconsistent behaviour. If we will introduce AddressSet instead of array, then we can remove setUnderlyingToken and instead set the underlying token in setAdapter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can make setAdapter also set the underlying token, so it's one call and there's no chance for error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And regarding this will not work for more than a token, yes, that's correct. But in reality I don't see how we could have more than one adaptor for a token,

mapping(address => address) public adapters;

event FeeCurrencyWhitelisted(address token);

event FeeCurrencyWhitelistRemoved(address token);
event AdaptorSet(address underlyingToken, address adapter);
event UnderlyinTokenSet(address underlyingToken);
event UnderlyinTokenRemoved(address underlyingToken);

/**
* @notice Sets initialized == true on implementation contracts
Expand All @@ -46,7 +53,51 @@ contract FeeCurrencyWhitelist is
* @return Patch version of the contract.
*/
function getVersionNumber() external pure returns (uint256, uint256, uint256, uint256) {
return (1, 1, 1, 0);
return (1, 1, 2, 0);
}

// removed with address zero
function setAdaptor(address token, address adaptor) external onlyOwner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also be able to remove Adapter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can set it to zero

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not the same though ... it will not remove it from the storage

adapters[token] = adaptor;
emit AdaptorSet(token, adaptor);
}

// TODO make external
function getAdaptor(address underlyingToken) public view returns(address){
martinvol marked this conversation as resolved.
Show resolved Hide resolved
address result = adapters[underlyingToken];
return (result != address(0))? result: underlyingToken;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case the if i call getAdaptor with an random address the random address will be returned.

from what we said last night we wanted to restrict to valid ones right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's actually hard to check now if something is or not a whitelisted gas fee address as it's an array and not a mapping. You'll get that but it doesn't mean anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough.

In that case Id argue it should return address(0) when nothing is found. The method is getAdapter() so it returning null when either address is wrong or when address does not need work is intuitive.


function setUnderlyinToken(address tokenAddress) external onlyOwner() {
underlyingTokens.push(tokenAddress);
emit UnderlyinTokenSet(tokenAddress);
}

function getUnderlyingTokens() external view returns(address[] memory){
return underlyingTokens;
}

// TODO when this contracts gets moved to Solidity 0.8 it should
// return an array of tuples
// secuence of underlyingtoken and it's corresponding adapter
function getWhitelistUnderlyngPairs() external view returns(address[] memory){
martinvol marked this conversation as resolved.
Show resolved Hide resolved
uint256 outLenght = underlyingTokens.length*2;
address[] memory result = new address[](outLenght);

for (uint256 i=0; i < underlyingTokens.length; i++){
address underlyingToken = underlyingTokens[i];
result[i*2] = getAdaptor(underlyingToken);
result[(i*2)+1] = underlyingToken;
}
return result;
}

function removeUnderlyinTokens(address tokenAddress, uint256 index) external onlyOwner {
require(underlyingTokens[index] == tokenAddress, "Index does not match");
uint256 length = underlyingTokens.length;
underlyingTokens[index] = underlyingTokens[length - 1];
underlyingTokens.pop();
emit UnderlyinTokenRemoved(tokenAddress);
}

/**
Expand Down
198 changes: 198 additions & 0 deletions packages/protocol/test-sol/common/FeeCurrencyWhitelist.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.5.13;

import "celo-foundry/Test.sol";
import "../../contracts/common/FeeCurrencyWhitelist.sol";

contract FeeCurrencyWhitelistTest is Test {
FeeCurrencyWhitelist feeCurrencyWhitelist;
address nonOwner;
address owner;

function setUp() public {
owner = address(this);
nonOwner = actor("nonOwner");

feeCurrencyWhitelist = new FeeCurrencyWhitelist(true);
feeCurrencyWhitelist.initialize();
}
}

contract FeeCurrencyWhitelistInitialize is FeeCurrencyWhitelistTest {
function test_InitializeOwner() public {
assertTrue(feeCurrencyWhitelist.isOwner());
assertEq(feeCurrencyWhitelist.owner(), address(this));
}

function test_ShouldNotBeCallableAgain() public {
vm.expectRevert("contract already initialized");
feeCurrencyWhitelist.initialize();
}
}

contract FeeCurrencyWhitelist_adaptedTokens is FeeCurrencyWhitelistTest{
address whitelistedAddress1 = address(1);
address whitelistedAddress2 = address(2);
address whitelistedAddress3 = address(3);

address token1 = address(4);
address token2 = address(5);

function setUp() public {

super.setUp();

feeCurrencyWhitelist.addToken(whitelistedAddress1); // real token
feeCurrencyWhitelist.addToken(whitelistedAddress2); // adaptor of token 1
feeCurrencyWhitelist.addToken(whitelistedAddress3); // adaptor of token 2

feeCurrencyWhitelist.setUnderlyinToken(whitelistedAddress1);
feeCurrencyWhitelist.setUnderlyinToken(token1);
feeCurrencyWhitelist.setUnderlyinToken(token2);

feeCurrencyWhitelist.setAdaptor(token1, whitelistedAddress2);

}
}

contract FeeCurrencyWhitelist_setAdaptor is FeeCurrencyWhitelist_adaptedTokens {
event AdaptorSet(address underlyingToken, address adapter);
function test_geTokenForVanillaGasCurrency() public{
// should be the same, it doesn't require addaptor
assertEq(feeCurrencyWhitelist.getAdaptor(whitelistedAddress1), whitelistedAddress1);
}

function test_geTokenForAdaptedGasCurrency() public{
// should be the same, it doesn't require addaptor
assertEq(feeCurrencyWhitelist.getAdaptor(token1), whitelistedAddress2);
}

function test_Reverts_WhenNonOwnerAddsAToken() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(nonOwner);
feeCurrencyWhitelist.setAdaptor(token2, whitelistedAddress3);
}

function test_ShouldEmitAdaptorSet() public {
vm.expectEmit(true, true, true, true);
emit AdaptorSet(token2, whitelistedAddress3);
feeCurrencyWhitelist.setAdaptor(token2, whitelistedAddress3);
}
}

contract FeeCurrencyWhitelist_setUnderlyinTokens is FeeCurrencyWhitelist_adaptedTokens {
event UnderlyinTokenSet(address underlyingToken);

function test_ShouldReturnUnderlyingToken() external {
address[3] memory addresses = [whitelistedAddress1, token1, token2];
address[] memory results = feeCurrencyWhitelist.getUnderlyingTokens();

// TODO forge has a helper for comparing arrays?
assertEq(results.length, addresses.length);
for (uint256 i=0; i<results.length; i++){
assertEq(results[i], addresses[i]);
}
}

function test_ShouldEmitUnderlyinTokenSet() public {
vm.expectEmit(true, true, true, true);
emit UnderlyinTokenSet(token1);
feeCurrencyWhitelist.setUnderlyinToken(token1);
}

function test_Reverts_WhenNonOwnerSetsUnderlyinTokenn() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(nonOwner);
feeCurrencyWhitelist.setUnderlyinToken(token1);
}
}

contract FeeCurrencyWhitelist_removeUnderlyinTokens is FeeCurrencyWhitelist_adaptedTokens {
event UnderlyinTokenRemoved(address underlyingToken);

function test_ShouldRemoveToken() public {
feeCurrencyWhitelist.removeUnderlyinTokens(token1, 1);
address[] memory result = feeCurrencyWhitelist.getUnderlyingTokens();
assertEq(result.length, 2);
assertEq(result[0], whitelistedAddress1);
assertEq(result[1], token2);
}

function test_ShouldEmitUnderlyinTokenRemoved() public {
vm.expectEmit(true, true, true, true);
emit UnderlyinTokenRemoved(token1);
feeCurrencyWhitelist.removeUnderlyinTokens(token1, 1);
}

function test_ShouldRevert_WhenIndexIsWrong() public {
vm.expectRevert("Index does not match");
feeCurrencyWhitelist.removeUnderlyinTokens(token1, 2);
}

function test_ShouldRevert_WhenNonOwnerRemovesToken() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(nonOwner);
feeCurrencyWhitelist.removeUnderlyinTokens(token1, 2);
}
}

contract FeeCurrencyWhitelist_getWhitelistUnderlyngPairs is FeeCurrencyWhitelist_adaptedTokens {

function test_getsRightTouples() external {
feeCurrencyWhitelist.setAdaptor(token2, whitelistedAddress3);
address[6] memory addresses = [whitelistedAddress1, whitelistedAddress1, whitelistedAddress2, token1, whitelistedAddress3, token2];
address[] memory results = feeCurrencyWhitelist.getWhitelistUnderlyngPairs();

assertEq(results.length, addresses.length);
for (uint256 i=0; i<results.length; i++){
assertEq(results[i], addresses[i]);

}
}
}


contract FeeCurrencyWhitelistAddToken is FeeCurrencyWhitelistTest {
function test_ShouldAllowTheOwnerToAddAToken() public {
feeCurrencyWhitelist.addToken(address(1));
address[] memory whitelist = feeCurrencyWhitelist.getWhitelist();
assertEq(whitelist.length, 1);
assertEq(whitelist[0], address(1));
}

function test_ShouldRevert_WhenNonOwnerAddsAToken() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(nonOwner);
feeCurrencyWhitelist.addToken(address(1));
}
}

contract FeeCurrencyWhitelistRemoveToken is FeeCurrencyWhitelistTest {
function setUp() public {
super.setUp();
feeCurrencyWhitelist.addToken(address(1));
feeCurrencyWhitelist.addToken(address(2));
feeCurrencyWhitelist.addToken(address(3));
}

function test_ShouldRemoveToken() public {
feeCurrencyWhitelist.removeToken(address(2), 1);
address[] memory whitelist = feeCurrencyWhitelist.getWhitelist();
assertEq(whitelist.length, 2);
assertEq(whitelist[0], address(1));
assertEq(whitelist[1], address(3));
}

// TODO this test should check emits

function test_ShouldRevert_WhenIndexIsWrong() public {
vm.expectRevert("Index does not match");
feeCurrencyWhitelist.removeToken(address(2), 2);
}

function test_ShouldRevert_WhenNonOwnerRemovesToken() public {
vm.expectRevert("Ownable: caller is not the owner");
vm.prank(nonOwner);
feeCurrencyWhitelist.removeToken(address(2), 1);
}
}
75 changes: 0 additions & 75 deletions packages/protocol/test/common/feecurrencywhitelist.ts

This file was deleted.

Loading