The Distributor::_distribute()
does not account for the stuck amount mistakenly transferred to the Proxy
contract. If the stuck token's address is identical to the prize token's address, the stuck amount will be distributed to contest winners.
Subsequently, a rescue requestor will lose their token permanently.
The Sparkn
protocol was designed as an escrow for distributing prizes (tokens) to contest winners. One of the protocol features is recovering stuck tokens (being sent by mistake) from a contest's Proxy
contract after the contest expires.
In other words, all prizes must be distributed to all winners before an owner (protocol admin) can execute the stuck tokens' recovery process. However, if the stuck token's address is identical to the prize token's address, the stuck amount will not be accounted for by the _distribute()
.
The _distribute()
will count the total balance locked in the Proxy
as prizes for winners. Therefore, the stuck amount will also be distributed to the contest winners. The remaining amount will then become the protocol's commission fee and be transferred to the protocol's fee collector.
Consequently, a rescue requestor could not retrieve their token as expected.
The total token balance is viewed as winners' prizes
: https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/Distributor.sol#L139
All balance (minus the commission fee) is distributed to winners
: https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/Distributor.sol#L146-L147
Transferring the commission fee
: https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/Distributor.sol#L154
The commission fee is transferred to the protocol's fee collector
: https://github.com/Cyfrin/2023-08-sparkn/blob/0f139b2dc53905700dd29a01451b330f829653e9/src/Distributor.sol#L164
Suppose the stuck token's address is identical to the prize token's address. In that case, the stuck amount will be distributed to contest winners unexpectedly since the stuck amount will be counted as prizes for the winners.
Subsequently, a rescue requestor will lose their token permanently.
Manual Review
To fix the vulnerability, I recommend improving the following functions: distribute()
, _distribute()
, and _commissionTransfer()
, as shown below.
The uint256 maxAmountToTransfer
param is introduced to the distribute()
, which must also be passed to the _distribute()
.
The _distribute()
must count the maxAmountToTransfer
param as the maximum prize amount to distribute to winners instead.
The _commissionTransfer()
must transfer only a commission fee calculated from the maxAmountToTransfer
param instead of transferring all remaining amount.
This way, an organizer or owner (protocol admin) can define the maxAmountToTransfer
param when executing the distribute()
. The stuck amount will not be mistakenly transferred from the Proxy
and can be retrieved to a rescue requestor as expected.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.