Assessment reports>Mina Token Bridge>High findings>Bypassing daily quota may lead to stuck funds
Category: Coding Mistakes

Bypassing daily quota may lead to stuck funds

High Severity
High Impact
Medium Likelihood

Description

The bridge has an adjustable daily quota of ETH that can be bridged. On the front end, this check is done on the client side to prevent users from entering a number that would be over the remaining daily quota. Once a bridge-deposit transaction has made it through the various crawlers, a check is done on the backend if the current bridging transaction would bring the total sum of all transactions by that user above the daily threshold.

private async isPassDailyQuota(address: string, networkReceived: ENetworkName): Promise<boolean> {
const fromDecimal = this.configService.get(
    networkReceived === ENetworkName.MINA ? EEnvKey.DECIMAL_TOKEN_EVM : EEnvKey.DECIMAL_TOKEN_MINA,
);
const [dailyQuota, todayData] = await Promise.all([
    await this.commonConfigRepository.getCommonConfig(),
    await this.eventLogRepository.sumAmountBridgeOfUserInDay(address),
]);
assert(!!dailyQuota, 'daily quota undefined');
this.logger.info(\`totals: ${todayData.totalamount} and quota ${dailyQuota.dailyQuota}\`);
if (
    todayData?.totalamount &&
    BigNumber(todayData.totalamount).isGreaterThan(addDecimal(dailyQuota.
    dailyQuota, fromDecimal))
) {
    return true;
}
return false;
}

See that isPassDailyQuota() is called in the handleSendTxJob() function, which is responsible for the sum-of-transactions check.

private async handleSendTxJobs(data: IJobUnlockPayload) {
// check if there is enough threshhold -> then create an unlock job.
if (await this.isPassDailyQuota(data.senderAddress, data.network)) {
    this.logger.warn('this tx exceed daily quota, skip until next day', data.eventLogId);
    await this.eventLogRepository.update(data.eventLogId, { nextSendTxJobTime: getNextDayInUnix().toString() });
    return;
}
await this.queueService.addJobToQueue<IUnlockToken>(
    this.getSenderQueueName(data.network),
    {
    eventLogId: data.eventLogId,
    },
    {
    attempts: 5,
    removeOnComplete: {
        age: this.jobRemoveDueDate,
    },
    backoff: this.sendTxJobBackOff,
    },
);
}

If the transactions pass the daily quota, they are moved to be processed the next day. A user may decide to bypass the client-side checks and send multiple transactions or a single large transaction that breaches the daily quota. Each would then be moved to be processed the following day, where the same check would be done, again passing the daily quota and being moved to the next day yet again. As this constraint can never be satisfied, since the transaction will always be bigger than the daily quota, the funds are stuck until admin intervention. We were unable to verify this finding due to the lack of a fully working test environment.

Impact

A user who decides to bypass the limits enforced by the UI may have their funds stuck in the bridging contract if they send an amount that is above the daily quota.

Recommendations

Ensure that user funds cannot become stuck if they manage to send transactions that are above the daily quota. Either refund the deposit, send the entire amount on the following day, or bridge tokens up to the daily quota and send the rest of the amount the following day.

Remediation

The team has acknowledged the finding and responded with the following comment:

For MINA, currently we don’t apply any daily quota checking mechanism since MINA has limited data structures to work with.

For ETH, we skip daily quota checks to reduce gas usage, because ETH price is expensive.

For the reason why we move over daily quoted transactions to the next day.

Admin should set the max value of each bridge transaction to be smaller than the daily quota, then there will be no transactions pending forever due to its value exceeding the daily quota.

In the next version, due to the limitations of variables and storage of Mina network, we will need to move this logic to BE. This issue will not happen even though the admin can configure it differently than we expected above.

Zellic © 2025Back to top ↑