Skip to content

Commit

Permalink
chore(FeesManager): add FeesManager contract coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
gitmp01 committed Jul 28, 2024
1 parent 568ea14 commit f3a416a
Show file tree
Hide file tree
Showing 7 changed files with 539 additions and 57 deletions.
72 changes: 34 additions & 38 deletions solidity/src/FeesManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,6 @@ contract FeesManager is IFeesManager, Ownable {
using SafeERC20 for IERC20;
uint16 public currentEpoch;

bytes32 public WITHDRAW_ROLE;
bytes32 public UPGRADE_ROLE;
bytes32 public SET_FEE_ROLE;

mapping(uint16 => mapping(address => uint256))
public depositedAmountByEpoch;
mapping(uint16 => mapping(address => mapping(address => uint256)))
Expand All @@ -25,36 +21,31 @@ contract FeesManager is IFeesManager, Ownable {
mapping(uint16 => uint256) public totalStakedAmountByEpoch;
mapping(address => Fee) public feeInfoByAsset;

bool public initialized;

event FeeUpdated(address token, uint256 minFee, uint256 basisPoints);
event FeeUpdated(address token, uint256 minFee, uint16 basisPoints);
event NewEpochStarted(uint16 epoch);

error InvalidToken();
error InvalidFromAddress();
error InvalidEpoch();
error NothingToClaim();
error TooEarly();
error AlreadyClaimed();
error AlreadyInitialized();
error NotLocal(address xerc20);
error UnsupportedToken(address xerc20);
error DifferentLength(uint256 len1, uint256 len2);

modifier onlyOnce() {
if (initialized) revert AlreadyInitialized();
_;
initialized = true;
}

constructor() Ownable(msg.sender) {}

function intialize(
constructor(
uint16 firstEpoch,
address[] calldata nodes,
uint256[] calldata amounts
) public onlyOnce {
address[] memory nodes,
uint256[] memory stakedAmounts
) Ownable(msg.sender) {
if (nodes.length != stakedAmounts.length)
revert DifferentLength(nodes.length, stakedAmounts.length);

currentEpoch = firstEpoch;
for (uint i = 0; i < nodes.length; i++) {
stakedAmountByEpoch[currentEpoch][nodes[i]] = amounts[i];
totalStakedAmountByEpoch[currentEpoch] += amounts[i];
stakedAmountByEpoch[currentEpoch][nodes[i]] = stakedAmounts[i];
totalStakedAmountByEpoch[currentEpoch] += stakedAmounts[i];
}
}

Expand All @@ -67,44 +58,53 @@ contract FeesManager is IFeesManager, Ownable {

/// @inheritdoc IFeesManager
function claimFeeByEpoch(address token, uint16 epoch) external {
address payable sender = payable(_msgSender());
if (token == address(0) || !feeInfoByAsset[token].defined)
revert InvalidToken();
if (epoch >= currentEpoch) revert TooEarly();
if (claimedAmountByEpoch[epoch][token][sender] > 0)
if (claimedAmountByEpoch[epoch][token][msg.sender] > 0)
revert AlreadyClaimed();

uint256 amount = (depositedAmountByEpoch[epoch][token] *
stakedAmountByEpoch[epoch][sender]) /
stakedAmountByEpoch[epoch][msg.sender]) /
totalStakedAmountByEpoch[epoch];

if (amount == 0) revert NothingToClaim();
claimedAmountByEpoch[epoch][token][sender] += amount;
if (token == address(0)) sender.transfer(amount);
else IERC20(token).safeTransfer(sender, amount);

claimedAmountByEpoch[epoch][token][msg.sender] += amount;

IERC20(token).safeTransfer(msg.sender, amount);
}

function registerAndAdvanceEpoch(
address[] calldata nodes,
uint256[] calldata amounts
) external onlyOwner {
if (nodes.length != amounts.length)
revert DifferentLength(nodes.length, amounts.length);

currentEpoch += 1;
for (uint i = 0; i < nodes.length; i++) {
stakedAmountByEpoch[currentEpoch][nodes[i]] = amounts[i];
totalStakedAmountByEpoch[currentEpoch] += amounts[i];
}

emit NewEpochStarted(currentEpoch);
}

/// @inheritdoc IFeesManager
function calculateFee(
address xerc20,
uint256 amount
) public view returns (uint256) {
) external view returns (uint256) {
// We take the fees only when wrapping/unwrapping
// the token. Host2host pegouts won't take any
// fees, otherwise they would be taken twice when
// fees, otherwise this logic would taken them twice when
// pegging-out
if (!IXERC20(xerc20).isLocal()) revert NotLocal(xerc20);
if (!IXERC20(xerc20).isLocal()) return 0;

Fee memory info = feeInfoByAsset[xerc20];

if (!info.defined) revert UnsupportedToken(xerc20);
if (!info.defined) return 0;

uint256 fee = (amount * info.basisPoints) / 1000000;

Expand All @@ -114,12 +114,6 @@ contract FeesManager is IFeesManager, Ownable {
: fee;
}

/// @inheritdoc IFeesManager
function depositFeeForEpoch(uint16 epoch) public payable {
if (epoch < currentEpoch) revert InvalidEpoch();
depositedAmountByEpoch[epoch][address(0)] += msg.value;
}

/// @inheritdoc IFeesManager
function depositFee(address xerc20, uint256 amount) external {
depositFeeForEpoch(xerc20, amount, currentEpoch);
Expand Down Expand Up @@ -160,11 +154,13 @@ contract FeesManager is IFeesManager, Ownable {
IERC20(xerc20).safeTransferFrom(from, address(this), amount);
}

/// @inheritdoc IFeesManager
function setFee(
address xerc20,
uint256 minAmount,
uint16 basisPoints
) external onlyOwner {
if (xerc20 == address(0)) revert InvalidToken();
feeInfoByAsset[xerc20] = Fee(minAmount, basisPoints, true);
emit FeeUpdated(xerc20, minAmount, basisPoints);
}
Expand Down
19 changes: 13 additions & 6 deletions solidity/src/interfaces/IFeesManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ interface IFeesManager {
*/
function claimFeeByEpoch(address xerc20, uint16 epoch) external;

/*
* Allows to deposit protocol fees for the native currency that will be distributed for a specific epoch.
* @param {uint16} epoch - The epoch number for which fees are being deposited.
*/
function depositFeeForEpoch(uint16 epoch) external payable;

/*
* Allows to deposit protocol fees for a certain token that will be distributed for the current epoch.
* @param {address} xerc20 - The token address for which fees are being deposited.
Expand Down Expand Up @@ -97,4 +91,17 @@ interface IFeesManager {
uint256 amount,
uint16 epoch
) external;

/**
* Set the fees for the underlying xerc20 token
*
* @param xerc20 supported token where the fees are denominated to
* @param minAmount minimum amount of fees
* @param basisPoints basis points (4 decimals, i.e. 2 basis points => 0.2% => 2000)
*/
function setFee(
address xerc20,
uint256 minAmount,
uint16 basisPoints
) external;
}
2 changes: 1 addition & 1 deletion solidity/src/xerc20/XERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract XERC20 is ERC20, Ownable, IXERC20, ERC20Permit {
feesManager = newAddress;
} else if (msg.sender != feesManager) revert OnlyFeesManager();

if (newAddress.code.length == 0) revert NotAContract(feesManager);
if (newAddress.code.length == 0) revert NotAContract(newAddress);

feesManager = newAddress;

Expand Down
Loading

0 comments on commit f3a416a

Please sign in to comment.