Parex

CertiKFiles named in the report

CertiK reviewed Parex on 15 Oct 2024: 14 findings, 1 critical; 10 marked fixed, 4 acknowledged.

1 critical finding, fixed
Open the reportOpen Parex’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings14

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical001
High014
Medium002
Low033
CriticalIncorrect withdrawal of tokensFixed

The ` sendBridgeOwnerReward()` function is used to allow the contract owner to retrieve the `PAREX` token from the contract, but this function will fail. This is because the 197 line of code function is to withdraw the contract's BNB, not `PAREX`. And since the `unlockTokens()` function only mints tokens for the user, without minting `bridgefee` into the contract, the contract doesn't own any `PAREX` tokens, and therefore you couldn't use the transfer function to transfer the contract token, you may use the `_mint` function. ```solidity=192 function sendBridgeOwnerReward() public onlyOwner { require(approvalCount >= requiredApprovals, "Not enough approvals"); require(address(this).balance >= bridgeTotalFee, "Insufficient balance"); if (bridgeTotalFee > 0) { address payable bridgeOwner = payable(owner()); (bool success, ) = bridgeOwner.call{value: bridgeTotalFee}(""); require(success, "Transfer failed"); bridgeTotalFee = 0; } resetApprovals(); } ```

HighImproper Authorization Check in `lockTokens` FuncFixed

The `lockTokens()` function allows any user to lock their own tokens, internally calling `_burnFrom` to burn the user's tokens. At line 30, the contract checks the allowance of the account, but since line 134 passes `msg.sender`, it results in checking the allowance of the user for themselves. If the user has not authorized themselves, they cannot lock their tokens. ```solidity=128 function lockTokens(uint256 amount, uint256 nonce, uint256 targetChainID) external whenNotPaused { require(!processedNonces[nonce], "Transfer already processed"); require(balanceOf(msg.sender) >= amount, "Insufficient balance to lock tokens"); require(maxAmount >= amount, "Lock amount exceeds maximum allowed"); // Burn tokens from the user's balance using burnFrom _burnFrom(msg.sender,amount); processedNonces[nonce] = true; emit Burned(msg.sender, amount, block.timestamp, nonce, targetChainID); } ``` ```solidity=29 function _burnFrom(address account, uint256 amount) internal virtual { uint256 currentAllowance = allowance(account, msg.sender); require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance"); _burn(account, amount); } ```

HighImproper Function Design in `sendBridgeOwnerRewarFixed

The `sendBridgeOwnerReward()` function, which is intended for the contract owner to withdraw native tokens from the contract, will fail. This is because the contract lacks a `payable` function or a `receive` function, preventing it from accepting native tokens. ```solidity=158 function sendBridgeOwnerReward() public onlyOwner { require(address(this).balance >= bridgeTotalFee, "Insufficient balance"); if (bridgeTotalFee > 0) { address payable bridgeOwner = payable(owner()); (bool success, ) = bridgeOwner.call{value: bridgeTotalFee}(""); require(success, "Transfer failed"); bridgeTotalFee = 0; } ```

HighInitial Token DistributionFixed

`9000000 * 10 ** 18` `PRX` tokens are sent to the contract deployer. This is a centralization risk because the deployer can distribute tokens without obtaining the consensus of the community. Any compromise to these addresses may allow a hacker to steal and sell tokens on the market, resulting in severe damage to the project.

HighMinted `BridgeFee` Tokens Locked in Contract dueFixed

The `unlockTokens()` function allows the owner to unlock tokens for users. However, if the unlock amount is zero, at line 144, the `BridgeFee` token is minted to the current contract without accumulating it in `bridgeTotalFee`. This results in the token being locked in the contract with no way to withdraw it. ```solidity=131 function unlockTokens(address to, uint256 amount, uint256 nonce) external onlyOwner nonReentrant { require(!processedNonces[nonce], "Transfer already processed"); processedNonces[nonce] = true; uint256 amountToMint = amount; if (amount > 0) { require(amount > bridgeFee, "Amount must be greater than bridge fee"); amountToMint -= bridgeFee; // Deduct the bridge fee bridgeTotalFee += bridgeFee; // Accumulate the total fees collected } _mint(to, amountToMint); // Mint tokens to the recipient _mint(address(this), bridgeFee); // Mint tokens for the BridgeFee emit Minted(to, amountToMint+bridgeFee, block.timestamp, nonce); } ```

MediumIncomplete Token Minting in `unlockTokens` FunctiFixed

In the PRX contract, which manages cross-chain tokens, the `lockTokens` function is responsible for burning tokens on the origin chain, and the `unlockTokens` function mints tokens on the target chain while charging a fee. This fee is tracked using the `bridgeTotalFee` variable, but not minted to anyone. The owner is supposed to retrieve the fee by calling `sendBridgeOwnerReward`, but this function fails under certain conditions, preventing the owner from claiming the fees. ```solidity=165 function sendBridgeOwnerReward() public onlyOwner { if (bridgeTotalFee > 0) { address payable bridgeOwner = payable(owner()); require(transfer(bridgeOwner, bridgeTotalFee), "Transfer failed"); bridgeTotalFee = 0; // Reset the total fee counter after transferring } } ```

MediumNo Cap on FeesFixed

The `bridgeFee` variables in the contract have no set limits, allowing fees to potentially reach 100%. If this occurs, users would receive no tokens from the contract, resulting in a complete loss of their investment. ```solidity=155 function setBridgeFee(uint256 _fee) public onlyOwner { require(_fee > 0, "Bridge fee must be greater than 0"); bridgeFee = _fee; } ```

LowMissing Check of Duplicate Signer AddressFixed

The smart contract fails to validate the uniqueness of signer addresses during the set process. This lack of validation could allow for duplicate addresses within the signer array, potentially undermining the integrity of the multi-sig approval mechanism.

LowMissing Public Mint Function in Cross-Chain TokenFixed

The `PRX-BSCToken` contract, designed as a cross-chain token, does not include a public mint function after the initialization mint function was removed during the commit `1128a9b91128741280d9d4ffd07be7d91df6273f`. As a result, tokens can only be minted when transferred from another chain.

LowMissing Zero Address ValidationFixed

Addresses are not validated before assignment or external calls, potentially allowing the use of zero addresses and leading to unexpected behavior or vulnerabilities. For example, transferring tokens to a zero address can result in a permanent loss of those tokens. ```solidity=89 _owner = initialOwner; ``` - `initialOwner` is not zero-checked before being used.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (1)

  • PRX-BSCToken.sol

Part of Parex’s security record

Parex
Audits1 audit by 1 firmLast audit Oct 2024, by CertiK.
Critical findingsNo open critical findingsIn that report, 1 critical finding was reported. It is marked fixed by the firm that reported it.
Open Parex’s security record

Security firm

CertiK

1,044 projects reviewed · 1,375 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us