As of 3 Nov 2026, UsePay has 1 audit report on Trustblock, by CertiK, dated 16 Nov 2022. That report lists no open critical findings.
Record last updated . It holds audits, findings and contracts up to Nov 2026, and incidents up to Oct 2026. Trustblock does not review code.
Newest first
Statuses are the security firm’s own, from each report.
| Severity | Open | Acknowledged | Fixed |
|---|---|---|---|
| Critical | 0 | 0 | 2 |
| High | 1 | 0 | 2 |
| Medium | 0 | 0 | 3 |
| Low | 0 | 0 | 26 |
The `createTicket` function calls the constructor in `TicketPack` along with a `msg.value`. `TicketPack` also has a `fallback` function that implements a proxy to a commander address. The `fallback` function (and therefore the proxy) is never called even when `msg.value` is sent to the contract because `TicketPack` has a receive function which takes in the `msg.value` instead. After this happens, tokens are locked in the `TicketPack` contract as there isn't any withdraw functionality either. The solution to this particular problem would be to remove the `receive` function.
Open the reportIn the function `calculate` there is this logic: ```solidity uint quantityCount = packInfo.total - quantity - totalUsedCount; uint qunaityValue = _percentValue(packInfo.price,packInfo.noshowValue) * quantityCount; _transfer(packInfo.tokenType,owner,qunaityValue); ``` `qunaityValue` is transferred to the owners address (withdrawing their earnings). `qunaityValue` is dependant on `quantityCount`. The problem with `quantityCount` is that totalUsedCount can be artificially increased such that `quantityCount` overflows (due to the solidity version being `>= 0.7.0`) or `quantityCount` can simply be set to 0 in version `0.8.0` and above. Here is the attack flow: For this example let's assume there is 100 available packs 1) The attacker buys 100 packs, as a result `quantity` decreases by 100. 2) The attacker requests a refund for these packs before `packInfo.times1`, this way `quantity` increases by 100 (cancelling out the previous decrease) and `totalUsedCount` increases by 100. After the attack, assuming regular users buy the tickets and eventually the time comes where the owner would like to withdraw his earnings, all the funds will be locked away from them as: ```solidity uint quantityCount = packInfo.total - quantity - totalUsedCount; ``` Since `totalUsedCount` was artificially inflated, `quantityCount` will have underflowed (in versions `^0.7.0`) making `quantityCount` so large that the transfer will always fail due to insufficient funds in the contract. Or in versions `^0.8.0`: since `totalUsedCount` is so big, the calculation will fail and the owner will never be able to withdraw is funds.
Open the reportThe Checks-Effects-Interactions pattern is not present in the code base hence re-entrancy attacks are present. Attack scenario for BNB/ETH/KLA: 1) Attack buys a pack and their `hasCount` increases, allowing them to pass the `canUse` check in `requestRefund` 2) When the attacker calls `requestRefund`, eventually BNB/ETH will be sent to the attackers address at the value of `packInfo.price` 3) In the attackers address there is a fall back function which calls `requestRefund` again. It passes the `canUse` check because the attackers `hasCount` has not yet been reduced. Therefore the attacker receives another refund. 4) The cycle continues until the contract is drained of tokens.
Open the reportThe owner of a subscription pack is always vulnerable to a grief attack depending on the value of `noshowLimit`. For example, an `owner` sets `packInfo.total` as 40 and `noshowLimit` as 15. Lets say 25 packs are bought by regular customers and an attacker buys 15 subscription packs. The attacker can refund his packs immediately after `packInfo.times2` is reached, setting `isLive` to 1. When time comes for the `owner` to withdraw his earnings from the other 25 packs, they are unable to do so due to the `checkLive` check on the `calculate` function, losing out on all his earned tokens.
Open the reportThe contracts `BSC_SubscriptionCreator` and `BSC_TicketCreator` have essentially identical logic for the swap upon creating a pack. However in `BSC_SubscriptionCreator`, `tokenIdx` defaults to: ```solidity uint16[2] private tokenIdx = [101,550]; ``` and in `BSC_TicketCreator`, `tokenIdx` defaults to: ```solidity uint16[2] private tokenIdx = [550,101]; ``` Which are in the opposite order to each other, indicating one of the swaps being in the wrong order. The same problem extends to `couponCreator` contract but the contract does not include a `_swap` function.
Open the reportIn the `noShowRefund`, a percentage is taken off the refund value twice, once here: ```solidity uint refundValue = _percentValue(packInfo.price,95); ``` Which makes the refund value 95% of the price. This value is further altered here: ```solidity for( uint i = 0; i < _addrList.length; i++ ) { address _to = _addrList[i]; buyList[_to].hasCount--; (values[i], swaps[i]) = _refund(_to,refundValue,10); } ``` Where the refund value is further calculated against for fees and the total to be returned to the customer is 0.90 * 0.95 * `packInfo.price`.
Open the report`BSC_CouponCreator` features a lot of functionality that facilitates a swap, such as `tokenIdx`, `swapCount` and functions such as `setTokenIdx` and `setSwapCount`. However a swap never takes place in the contract.
Open the reportIt is unclear why in `BSC_Commander.sol`, the `checkFee` function has a require statement that: ```solidity require( msg.value > getPrice() * (n) , "C01"); ``` While in the sister contracts in `Commander.sol` and `KLA_Commander.sol`, the `n` variable is hard coded to 5. ```solidity require( msg.value > getPrice() * n * 5 , "C01"); ``` This inconsistency is also found in the `getCountFee` function.
Open the reportThere is some loss in accuracy when calculating percentages at the upper bound of a percentage change. For example 60 out of 100 is calculated as 59%. 500 out of 1000 is calculated as 49%, and so on.
Open the reportIn `launchSetAddresses()` and `launchCalculate()` a check is passed only if `multiSign.count == numConfirmationsRequired`. However, it should be allowed to pass if `multiSign.count >= numConfirmationsRequired` to make it consistent with `confirmSetAddresses()` and `confirmCalculate()`.
Open the reportThe function `changeTotal` has this logic: ```solidity require(packInfo.total - quantity <= _count,"count too high"); if ( _count > packInfo.total ) { checkFee(_count-packInfo.total); _swap(msg.sender,msg.value); quantity = quantity + ( _count - packInfo.total ); } else { quantity = quantity - ( packInfo.total - _count ); } ``` The arithmetic: ```solidity quantity = quantity + ( _count - packInfo.total ); ``` Is exactly the same as: ```solidity quantity = quantity - ( packInfo.total - _count ); ``` The reason to use to different methods of calculation for the same result is unclear.
Open the reportThe function `getPrice` relies on `BUSD` staying stable and pegged to the dollar which may not always be the case.
Open the reportIn the `subscription` contract, the `PackInfo packInfo` variable is defaulted to private visibility as it has no specifier included. However in the `Coupon` and `Ticket` contracts, this particular variable is set as internal.
Open the reportIn the function `changeTotal` there is this `require` statement: ```solidity require(packInfo.total - quantity <= _count,"count too high"); ``` The error message "count too high" is misleading because the count is supposed to be greater than `packInfo.total - quantity`. Most of the error messages in the contract are written as a key, e.g "O01","CT01". This may be understood by the developers using a reference, but for the users these error messages can not be interpreted.
Open the report`index` is suppose to be the input array of the indexes of the multi-sig addresses that are to be changed and `newAddress` is supposed to be the input array of the new addresses. It should be checked that these two arrays have the same length.
Open the reportThe `constructor` has no checks to confirm that the inputs to the `packInfo` or the owner address is valid. This may lead to unexpected results for both the buyer and the seller. ---- In the function `createTicket` in the `ticketCreator.sol` file, there is a restriction to the `packInfo.total` which is 1000. However in the `changeTotal` function in the `TicketCommander` there is no such check, meaning the owner can add as many packs as they want. Similar cases are present for `SubscriptionCommander` and `CouponCommander` (where the maximum `packInfo.total` is 3000) --- In the `checkManger` function, only indexes below 100 are checked for the managerial addresses, therefore there should be a check in `setAddresses` to ensure that _index.length is less than 100, and a check in `setAddress` to ensure that `_index` is less than 100. Furthermore there should be checks that the inputted address is not the 0 address. --- Addresses should be checked before assignment or external call to make sure they are not zero addresses. ```solidity owner = _owner; ``` - `_owner` is not zero-checked before being used. --- ```solidity owner = _owner; ``` - `_owner` is not zero-checked before being used. --- ```solidity owner = _owner; ``` - `_owner` is not zero-checked before being used. --- Throughout the contract logic, `quantity` is used to keep track of how many packs are available to buy, for example in the `_buy` function, the quantity is reduced. However in the `buy` function, there are no checks to see whether the inputted count is less than or equal to the available quantity, making `quantity` and by extension `packInfo.Total` redundant. This check in `canBuy`: ```solidity require ( quantity - count >= 0 , "B04"); ``` is subject to underflow due to solidity version `>=0.7.0`, so it does not satisfy the check. ---- The PAC eco & UsePAY ecosystem rely on PAC tokens being swapped for native tokens in events such as creating a pack. In this current implementation, when creating a ticket, coupon or subscription, there are no checks for fees, meaning that the pack creator can just insert 0 as the `msg.value`, and the ecosystem is not supported at all. This issue is also present in the BSC and KLA creation contracts. ----
Open the reportFunctions that affect the status of sensitive variables should be able to emit events. For example: an event should be emitted when `tokenIdx` is set. The function that affects the status of sensitive variables should be able to emit events as notifications to users. --- The event `calculateEvent` has a input field for `swap`, however in the `calculate` function where the event is emitted, swap is always initialized to 0 and never changes as in the `_refund` logic, `swapValue` is calculated but this value is never returned into the `swap` variable in the `calculate` function.
Open the reportThe **require** can be used to check for conditions and throw an exception if the condition is not met. It is better to provide a string message containing details about the error that will be passed back to the caller.
Open the reportThere is a view function for all the `private` variables except for `nowSwap`.
Open the reportWe do not recommend using modifiers for other purposes other than condition checks; modifiers should either revert a transaction or execute the function’s body. Breaking these rules is considered bad practice. It leads to code of poor quality, that’s hard to read and maintain and is prone to errors.
Open the reportFlash loans are a way to borrow large amounts of money for a certain fee. The requirement is that the loans need to be returned within the same transaction in a block. If not, the transaction will be reverted. An attacker can use the borrowed money as the initial funds for an exploit to enlarge the profit and/or manipulate the token price in the decentralized exchanges. We find that the `getPrice` function in the `Commander.sol` contracts rely on price calculations that are based on-chain, meaning that they would be susceptible to flash-loan attacks by manipulating the price of given pairs to the attacker's benefit.
Open the reportThe parameter `isLive` is a `uint256` and is only used as a flag that can only be `0` or `1`, the same as a boolean. Gas can be saved by just using a Boolean. In addition `isCaculated` is also only used as a flag that can only be `0` or `1`.
Open the reportThis `if` statement in the function `getValue` is redundant: ```Solidity if ( a == 10 && percent == 60) { return 6; ``` This is because this check is already made prior: ```Solidity if(percent == 60 ) { if ( a== 10 ) { return 6; ``` The second if statement in: ```solidity else if (block.timestamp > packInfo.times2 && block.timestamp < packInfo.times2 + 172800) { if (block.timestamp > packInfo.times2) { ``` Is redundant because the check to see whether `block.timestamp` is greater than `packInfo.times2` is already made right above it. This `else` block is never reached: ```solidity else { require(block.timestamp <= noShowTime + 15552000,"N04"); // require(block.timestamp <= noShowTime + 300,"N04"); refundValue = packInfo.price; } ``` That is because before this else block there is a check for `block.timestamp < packInfo.times2` and a check for `block.timestamp > packInfo.times2` and logically there is no alternative scenario than these two, making the final else statement redundant.
Open the report`pragma experimental ABIEncoderV2;` is still valid, but it is deprecated and has no effects since Solidity version 0.8.0. The compiler version in the contract is unlocked, so this statement is redundant at any version above and including version 0.8.0, which we recommend the project team to use. [(Reference)](https://learnblockchain.cn/docs/solidity/080-breaking-changes.html?highlight=pragma%20experimental%20abiencoderv2)
Open the reportThe function that is declared `payable` can receive ether. The functions presented seem not to be designed for receiving ether, so it is redundant to have the `payable` modifier. The functions `setAddress` and `setAddresses` are both payable functions. However this contract does not support any use cases of having tokens sent to it. Furthermore the contract does not have any functions to withdraw, leaving any tokens sent to it lost in the contract.
Open the reportThe aforementioned lines assign 0 to the `uint256` type variables. The default value for `uint256` type is 0, hence the aforementioned assignments are redundant and cost unnecessary gas.
Open the reportIn the `Pack` contract, `total` is a `uint32` while `refundCount`, `noshowCount`, `noshowLimit` and `quantity` are all `uint256` even though these values can not exceed the value of `total`
Open the reportStruct `PackInfo` in the `Pack.sol` `subscription` contract is not following the best practice of packing struct in Solidity. Referring to [Solidity Documentation](https://docs.soliditylang.org/en/v0.6.12/internals/layout_in_storage.html?highlight=pack#layout-of-state-variables-in-storage).
Open the reportThe name of the modifier `onCaculateTime` has a spelling mistake, it should be `onCalculateTime`. The word `qunaity` should be `quantity` The name of variable `checkManger` has a spelling mistake, it should be `checkManager`. ---- The file name `BSC_TikcetCreator.sol` should be `BSC_Ticketcreator.sol` ---- The comments on the aforementioned lines state: "0: pack indexed, 2: createTime, 3: PackInfo" where it should be "0: pack indexed, 1: createTime, 2: PackInfo".
Open the report`BSC_CouponCommander` and `KLA_CouponCommander` both import `Pack`. ```solidity import "../../Pack/CouponPack.sol"; import "../../Pack/Pack.sol"; import "./BSC_Commander.sol"; ``` However since `CouponPack` is already imported which also inherits `Pack`, the import of `Pack` in the `CouponCommander` contracts are not needed.
Open the reportThe aforementioned events are not used within the contract. ```solidity event buyEvent(address indexed pack, uint256 buyNum, address buyer,uint256 count); event useEvent(address indexed pack, address user,uint256 count); event requestRefundEvent(address indexed pack, address buyer ,uint256 count); event calculateRefundEvent(address indexed pack, address[] buyers ) ; event calculateEvent(address indexed); ``` ---- The event `constructorEvent` is declared but never used. ---- `giftEvent` and `giveEvent` seem to have the exact same functionality, making one of them redundant.
Open the reportThe variable `refundCount` is never used within the code base.
Open the reportAccording to the comments, `createNum` is the creation time of the pack. This can introduce errors if the pack creator has to input this value into the `createTicket` function which will likely lead to inaccuracy.
Open the reportThe reports name a repository or files. Open a report to see its scope.
Is this your project? Show this record on your own website. The label states what the record holds, links to this page and updates by itself. It is free and needs no sign-in.
Automatic: the three strongest facts of this project’s own record (its last audit and two more), highlighted. They update with the record. Picking a fact turns Automatic off.
<head><script src="https://widget.trustblock.run/project-widget.min.js" defer></script></head><body><divdata-tb-project-widgetdata-tb-project-slug="usepay"></div></body>
Paste the code where the label should appear. It uses no API credit and updates by itself.
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