Lack of reentrancy protection
Description
There are currently a few scenarios that do not sufficiently protect against reentrancy attacks. In order to execute such attacks, it is necessary to interact with a token that gives control back to a caller during execution. This currently happens for certain tokens that offer hook functions when tokens are transferred or that require fees to be paid for each transfer.
One such example is in the swap
function,
for (uint i = indexEnd; i > indexStar; i--) {
Issue storage issueInfo = issues[i];
if (issueInfo.isStaking) {
if (amountB > 0) {
if (amountB >= issueInfo.issueAmount) {
uint amountA = (issueInfo.issueAmount * 10000) / rate;
amountB -= issueInfo.issueAmount;
redeemToekn.safeTransfer(issueInfo.user, amountA);
issueInfo.isStaking = false;
} else {
uint amountA = (amountB * 10000) / rate;
redeemToekn.safeTransfer(issueInfo.user, amountA);
issueInfo.issueAmount -= amountB;
amountB = 0;
}
}
}
}
where the isStaking
flag is set to false
after the transfer has happened.
Impact
If the swap
function above interacts with a token that gives some interactability during safeTransfer
, it is possible to reenter the function, pay more redeem tokens, and then the contract will pick the same issue over again.
Recommendations
Make sure that all tokens are carefully vetted to ensure that there are no ways to trigger reentrancy during transfer. Additionally, consider adding reentrancy protections using OpenZeppelin's nonReentrant
modifier↗ and refactor function logic to follow the checks-effects-interaction pattern↗.
Remediation
This issue has been acknowledged by Chateau Capital, and a fix was implemented in commit 910de03d↗.