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

feat(collector): add multi-token support #119

Merged
merged 16 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ artifacts
/contracts-exposed

.vscode
revenue-sharing-addresses.json
deploy-collector.input.json
119 changes: 71 additions & 48 deletions contracts/Collector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,45 +9,47 @@ import "@openzeppelin/contracts/math/SafeMath.sol";
contract Collector is ICollector {
address private _remainderAddress;
RevenuePartner[] private _partners;
IERC20 public token;
IERC20[] private _tokens;
address public owner;

modifier validShares(RevenuePartner[] memory partners) {
uint256 totalShares;

for (uint256 i = 0; i < partners.length; i++) {
require(partners[i].share > 0, "0 is not a valid share");
totalShares = totalShares + partners[i].share;
}

require(totalShares == 100, "Shares must add up to 100%");
_;
}

modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this");
_;
}

modifier noBalanceToShare() {
antomor marked this conversation as resolved.
Show resolved Hide resolved
require(
token.balanceOf(address(this)) < _partners.length,
"There is balance to share"
);
for (uint256 i = 0; i < _tokens.length; i++) {
require(
_tokens[i].balanceOf(address(this)) < _partners.length,
"There is balance to share"
);
}
_;
}

modifier updateValidShares(RevenuePartner[] memory partners) {
_;
uint256 totalShares;
franciscotobar marked this conversation as resolved.
Show resolved Hide resolved
for (uint256 i = 0; i < partners.length; i++) {
require(partners[i].share > 0, "0 is not a valid share");
totalShares = totalShares + partners[i].share;
_partners.push(partners[i]);
}
require(totalShares == 100, "Shares must add up to 100%");
}

constructor(
address _owner,
IERC20 _token,
IERC20[] memory tokens,
RevenuePartner[] memory partners,
address remainderAddress
) public validShares(partners) {
) public updateValidShares(partners) {
owner = _owner;
token = _token;
_remainderAddress = remainderAddress;
for (uint256 i = 0; i < partners.length; i++)
_partners.push(partners[i]);

for (uint i = 0; i < tokens.length; i++) {
_tokens.push(tokens[i]);
}
}

function getPartners() external view returns (RevenuePartner[] memory) {
Expand All @@ -56,46 +58,61 @@ contract Collector is ICollector {

function updateShares(
RevenuePartner[] memory partners
) external validShares(partners) onlyOwner noBalanceToShare {
) external onlyOwner noBalanceToShare updateValidShares(partners) {
delete _partners;

for (uint256 i = 0; i < partners.length; i++)
_partners.push(partners[i]);
}

//@notice Withdraw the actual remainder and then update the remainder's address
//for a new one. This function is the only way to withdraw the remainder.
function updateRemainderAddress(
address remainderAddress
) external onlyOwner noBalanceToShare {
uint256 balance = token.balanceOf(address(this));
address tokenAddr = address(token);
for (uint256 i = 0; i < _tokens.length; i++) {
IERC20 token = _tokens[i];
uint256 balance = token.balanceOf(address(this));

if (balance != 0) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory ret) = address(token).call(
abi.encodeWithSelector(
hex"a9059cbb",
_remainderAddress,
balance
)
);

require(
success && (ret.length == 0 || abi.decode(ret, (bool))),
"Unable to transfer remainder"
);
}
}
_remainderAddress = remainderAddress;
}

if (balance != 0) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory ret) = tokenAddr.call{gas: 200000}(
abi.encodeWithSelector(
hex"a9059cbb",
_remainderAddress,
balance
)
);
function addToken(IERC20 token) external onlyOwner {
_tokens.push(token);
}

require(
success && (ret.length == 0 || abi.decode(ret, (bool))),
"Unable to transfer remainder"
);
}
function getTokens() external view returns (IERC20[] memory) {
return _tokens;
}

// solhint-disable-next-line
_remainderAddress = remainderAddress;
function removeToken(uint256 tokenIndex) external onlyOwner {
antomor marked this conversation as resolved.
Show resolved Hide resolved
require(
_tokens[tokenIndex].balanceOf(address(this)) == 0,
"There is balance to share"
);

_tokens[tokenIndex] = _tokens[_tokens.length - 1];
_tokens.pop();
}

function getBalance() external view returns (uint256) {
return token.balanceOf(address(this));
function getRemainderAddress() external view returns (address) {
return _remainderAddress;
}

function withdraw() external override onlyOwner {
function withdrawToken(IERC20 token) public onlyOwner {
uint256 balance = token.balanceOf(address(this));
antomor marked this conversation as resolved.
Show resolved Hide resolved
require(balance >= _partners.length, "Not enough balance to split");

Expand All @@ -118,8 +135,14 @@ contract Collector is ICollector {
}
}

function withdraw() external override onlyOwner {
for (uint256 i = 0; i < _tokens.length; i++) {
withdrawToken(_tokens[i]);
}
}

function transferOwnership(address _owner) external override onlyOwner {
require(_owner != address(0), "New owner is the zero address");
require(_owner != address(0), "Owner cannot be zero address");
owner = _owner;
}
}
6 changes: 3 additions & 3 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ const config: HardhatUserConfig = {
},
tdd: {
tasks: [
// 'clean',
// { command: 'compile', params: { quiet: true } },
'clean',
{ command: 'compile', params: { quiet: true } },
{
command: 'test',
params: {
Expand All @@ -89,7 +89,7 @@ const config: HardhatUserConfig = {
},
},
],
files: ['./test/**/*.ts'],
files: ['./contracts/*.sol', './test/**/*.ts', './tasks/**/*.ts'],
verbose: true,
clearOnStart: true,
},
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rsksmart/rif-relay-contracts",
"version": "2.0.0-beta.0",
"version": "2.0.0-beta.1",
"private": false,
"description": "This project contains all the contracts needed for the rif relay system.",
"license": "MIT",
Expand Down
14 changes: 7 additions & 7 deletions tasks/deployCollector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ export interface Partner {
share: number;
}

interface CollectorConfig {
export type CollectorConfig = {
collectorOwner: string;
partners: Partner[];
tokenAddress: string;
tokenAddresses: string[];
remainderAddress: string;
}
};

export type DeployCollectorArg = {
configFileName?: string;
Expand Down Expand Up @@ -54,13 +54,13 @@ export const deployCollector = async (
fs.readFileSync(configFileName, { encoding: 'utf-8' })
) as CollectorConfig;

const { collectorOwner, partners, tokenAddress, remainderAddress } =
const { collectorOwner, partners, tokenAddresses, remainderAddress } =
inputConfig;

const collectorFactory = await ethers.getContractFactory('Collector');
const collector = await collectorFactory.deploy(
collectorOwner,
tokenAddress,
tokenAddresses,
partners,
remainderAddress
);
Expand All @@ -82,7 +82,7 @@ export const deployCollector = async (
const objToPrint = {
'Collector Contract': collector.address,
'Collector Owner': await collector.owner(),
'Collector Token': await collector.token(),
'Collector Tokens': await collector.getTokens(),
'Collector Remainder': remainderAddress,
...partnerPrintings,
};
Expand All @@ -106,7 +106,7 @@ export const deployCollector = async (
jsonConfig[networkId] = {
collectorContract: collector.address,
collectorOwner: await collector.owner(),
tokenAddress: await collector.token(),
tokenAddresses: await collector.getTokens(),
remainderAddress: remainderAddress,
partners,
};
Expand Down