OKX security record

Arbitrum, BNB Chain, Ethereum, Polygon (POS)okx.comX

As of 16 Oct 2026, OKX has 5 audit reports by 1 security firm on Trustblock; the latest is by CertiK, dated 19 May 2024. Those reports list no open critical findings. It also holds 1 incident (latest 12 Dec 2023).

Critical findings
No open critical findings
Across the 5 reports, 1 critical finding was reported. It is marked fixed by the firm that reported it. See the findings
Incident
Incident 12 Dec 2023
Source: De.Fi incident database. 1 audit has been published since. See the incident
Open the latest report
Your label, live
Get the label for your site

Record last updated . It holds audits and findings, contracts and incidents up to Oct 2026. Trustblock does not review code.

Audits5

Newest first

AuditSecurity firmDateScopeCritical findingsReport
OKX SDKSecurity firm: CertiKDate: Scope: Not recorded. See the reportNo open critical findingsOpen · skynet.certik.com
OKX (Threshold-lib)Security firm: CertiKDate: Scope: Repository at commit ece01171 critical finding, fixedOpen · skynet.certik.com
OKX - Audit 3Security firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
OKC SwapSecurity firm: CertiKDate: Scope: Files named in the reportNo open critical findingsOpen · skynet.certik.com
OKX Marketplace of SolanaSecurity firm: CertiKDate: Scope: Not recorded. See the reportNo open critical findingsOpen · skynet.certik.com

Findings79

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

SeverityOpenAcknowledgedFixed
Critical001
High111
Medium016
Low43025
HighCentralization Related RisksOKX - Audit 3 · CertiK · 16 May 2023Open

In the contract `Entrance` the role `_owner` has authority over the functions shown in the diagram below. Any compromise to the `_owner` account may allow the hacker to take advantage of this authority and configure key parameters and manipulate the project. ![](https://accelerator-tasks-prod.acc.corp.certik.com/74536283dfda4244a8dd9f655ff2839b/diagrams/centralization_Entrance-Entrance-_owner.svg) In the contract `Entrance` the role `operator` has authority over the functions shown in the diagram below. Any compromise to the `operator` account may allow the hacker to take advantage of this authority and manipulate the project. ![](https://accelerator-tasks-prod.acc.corp.certik.com/74536283dfda4244a8dd9f655ff2839b/diagrams/centralization_Entrance-Entrance-operator.svg) In addition, the contract `Entrance` is an upgradeable contract, meaning the owner can upgrade the contract without the community's commitment. If an attacker compromises the account, the attacker can change the implementation of the contract and drain tokens from the contract. ----- In the contract `DexRouter` the role `owner` has authority over the following functions: - setApproveProxy() to set approveProxy - setWNativeRelayer() to set the wNativeRelayer - setXBridge() to set the xBridge - initializePMMRouter() to initialize the PMM Router - setPMMFeeConfig() to set the fee rate and receiver of the PMM Router In addition, the role `xBridge` has authority over the following functions: - smartSwapByOrderIdByXBridge() to perform the token swaps with the smartswap - unxswapByOrderIdByXBridge() to perform the token swaps with unxswap - uniswapV3SwapToByXBridge() to perform the token swaps with uniswapV3 - PMMV2SwapByXBridge() to perform the token swaps with PMMV2Swap In addition, the contract `DexRouter` is an upgradeable contract, meaning the owner can upgrade the contract without the community's commitment. If an attacker compromises the account, the attacker can change the implementation of the contract and drain tokens from the contract. ----- In the contract `OkxNFTMarketAggregator` the role `owner` has authority over the following functions: - approveERC20(() to approve the ERC20 tokens to the operator - setMarketRegistry() to set the marketRegistry

Open the report
LowFrontEnd use Insecure Cryptographic PrimitivesOKX SDK · CertiK · 19 May 2024Open

We found the cryptographic primitives used a custom random number generation implementation. It’s using the `Math.random()` JS function, which is cryptographically insecure. `okxwallet/app/scripts/controllers/keyrings/utils/random.js`

Open the report
LowSDK uses crypto modules that are no longer activeOKX SDK · CertiK · 19 May 2024Open

The SDK uses multiple sources for the module importing within both the JS and Golang components of the SDK. The JS component (audit/src/index.ts) uses the `crypto-js` module, which is no longer under active development. The Golang components (audit/src/index.ts) use the `crypto-js` module, which is no longer under active development. It’s recommended to replace the import with the native `Crypto` library.

Open the report
LowDiscrepancy Between Implementation and SpecificatOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

In the Schnorr proof implementation, the computation of the challenge value `h` doesn't match with the specification. According to the specification, the challenge c is computed as `H(G || V || A)`, where `H` is a hash function, `G` is the base point of the elliptic curve, `V = rG` is the random commitment, and `A` is Alice's public key. However, in the implementation, the challenge `h` is computed as `crypto.SHA256Int(X.X, X.Y, R.X, R.Y)`, which is a hash of the `X` and `Y` coordinates of the public key `X` and the random commitment `R`. The base point `G` is not included in the computation. This discrepancy between the implementation and the specification can lead to potential vulnerabilities or incorrect results when using the Schnorr proof. The implementation needs to be corrected to follow the specification.

Open the report
LowHard-coded Source of RandomnessOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The generation of random numbers in a crypto safe way plays a paramount role in ensuring the security of the implemented cryptographic algorithms. Such task can be delegated to the underlying operating system which offer such service using a mixture of hardware and software mechanism to generate unpredictable and unique random values. In particular, the codebase in scope always relies on the standard `rand.Reader` utility which, calling the operating system, generates random strings of bytes. However, there are some cases in which users of crypto wallets require strong guarantees of randomness for some reasons: the runtime environment may not be fully trusted, several source of randomness may be required, an external dedicated hardware for random number generation may be available, and so on. Moreover, given the sensibility of the implemented algorithms, a common testing practice is to compare the implementation with the same algorithm executed in a different programming language. Since many algorithms rely on random numbers and results are not deterministic, in order to perform such tests, deterministic random number generators are put in place so the final results and intermediate outcomes can be compared. Since the codebase in scope implements cryptographic algorithm built **on top** of a secure random number generator, the randomness security is not a responsibility of the library and hard-coding it to the default random byte generator of Go may be limiting for the library usage.

Open the report
LowMismatch on Chaincode Usage in BIP-32 Key DerivatOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The [BIP-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) specification document adopts HMAC-SHA512 for generating derived child data from parent information. Such algorithm provides data authentication through an hash function and a key. It takes two parameters, an authentication key and the data to authenticate. The BIP-32 specification uses what it calls chaincode as authentication key, and the parent public key concatenated with the derivation index as data to authenticate. However the derivation function implemented at the pointed location uses a constant hard-coded string as authentication key and prepends the chaincode to the data to authenticate. Even though such changes do not introduce security problems thanks to the hash function properties, the BIP-32 definitions are not respected, so the library can not claim to be compliant and interoperable with correct implementations of such standard.

Open the report
LowMissing Round Enforcement in ECDSA ContextsOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The cryptography algorithms implemented in the codebase in scope strictly follow their reference paper by wrapping each step or round of a party participating in the protocols in dedicated methods of a single struct. The context of each session of an algorithm is kept by storing information in the assigned method struct, while messages coming from the interacting counterparties are passed as methods parameters. By following this pattern it is important that struct methods are executed according to the prescribed order so that the struct state is consistent with the algorithm progress in a certain session. Any wrong method call would invalidate the state, cause an information loss and force the algorithm session abort. In order to avoid such situation, the structs wrapping the context for Distributed Key Generation with Verified Secret Sharing, Key Reshare and Ed25519 signature include a `RoundNumber` field which explicitly track the algorithm progress. Then, all the methods implementing algorithm steps check if the current `RoundNumber` is the one supposed to be in the invoked round before executing the assigned logic and update the `RoundNumber`, as well. However, such verification mechanism of the current round is absent in the ECDSA signature implementation based on [Lidell17](https://eprint.iacr.org/2017/552.pdf). In this way, the correctness of methods call order is completely delegated to the library caller, which may not be aware of such constraints or may unwillingly implement a faulty logic.

Open the report
LowNon Timing-Constant Int Value ComparisonOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The `hash.Cmp(C) == 0` code used in commitment verification is not a constant comparison procedure which could potentially lead to timing side channel attack. Although the auditors did not notice it is exploitable at this moment but it is suggested to perform constant timing comparison.

Open the report
LowPanic Used Instead of Error MessagesOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

In function `NewRefresh` and `NewSetUp` of this library, the developers check parameter `total` to unsure the number of participated parties is no less than 2. However, the code simply panics if the constrain does not meet. As a library which can be used as part of code in other developers' project, the auditors suggest that use error message instead of panic to show that the passed parameters are inappropriate, which is consistent with the rest of the library coding style.

Open the report
LowPotential Vulnerable Runtime VersionOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The audited Go project is designated to run with version 1.17. However, Go language (before version 1.19.5) suffers an issue ([GO-2023-1621](https://pkg.go.dev/vuln/GO-2023-1621)) where the ScalarMult and ScalarBaseMult methods of the P256 Curve may return an incorrect result if called with some specific unreduced scalars. Although this does not directly impact the audited project for now, this may impact future version of the audited library. Similarly, Go language runtime (version 1.17) also suffers an issue ([GO-2023-1840](https://pkg.go.dev/vuln/GO-2023-1840)) on Unix platform which could lead to information leakage or privilege escalation. Although the audited code is used as library instead of full binary, the users of this library may keep the same Go version thus auditor still consider this as potential impact.

Open the report
Show 10 more
LowUnnecessary Computation of Random Polynomial in nOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The aim of the key reshare process is to make a change in the set of shareholders while keeping the same global key-pair. This is achieved through the participation of two shareholders from the old set which, relying on their shares, act as dealers of new shares for the new set of shareholders. Such dealers are called `devotees` in the codebase while non-devotees simply verify the incoming information in order to assess that the process was concluded successfully. In the current implementation, non-devotees generate a local secret, a random polynomial to share such secret, a Schnorr proof to demonstrate the knowledge of the secret and the verifiers of the polynomial coefficient. Such data are also forwarded in the messages of step 1 and 2. However, all these computations are not necessary since the only source of information is represented by the devotees, and only their messages will be actually verified and taken into account in step 3 when finalizing the reshare algorithm.

Open the report
LowUnnecessary Memory AllocationOKX (Threshold-lib) · CertiK · 11 Oct 2023Open

The `s` variable in the `bigIntToEncodedBytes` function is initialized as a reference to a 32 byte array through the `new` operator which also allocates memory for the specified type. However, the memory allocated at variable initialization is practically unused since the a new `byte` array is allocated by the `copyBytes` function and the reference is replaced.

Open the report
LowAllowed Markets in Function `tradeV2()`OKX - Audit 3 · CertiK · 16 May 2023Open

The function `tradeV2()` allows the users to trade NFTs with specified markets. The comment in line 312 indicates only the market `wyvern` (id is 4 based on the implementation) is allowed to be used in `tradeV2()`, but the implementation in line 313: ```solidity=313 if(tradeDetails[i].marketId != 0 && tradeDetails[i].marketId !=4 && tradeDetails[i].marketId < 7) { ``` also allows the market with id 0.

Open the report
LowChoice of Token for Parameter `fromToken` For HopOKX - Audit 3 · CertiK · 16 May 2023Open

In the function `_exeHop()`, the first hop executed swaps `batchAmount` of `hops[0].fromToken`. It should be the case that `hops[0].fromToken` is the same address as `_baseRequest.fromToken` in `_smartSwapInternal()`, but there are no checks to guarantee this.

Open the report
LowConditional Check on Message Value Should Be UncoOKX - Audit 3 · CertiK · 16 May 2023Open

The function `_transferTokens()` has a check to see if `msg.value` matches the amount of native tokens that have been transferred to other addresses. ```solidity=382 if (msg.value != 0) {require(msg.value == nativeAmount, MetaXInvestErrors.QUANTITY_MISMATCH);} ``` However, this check is only performed if `msg.value != 0`, when it should be always done. If `msg.value == 0` but `nativeAmount > 0`, it is possible for the contract to lose native currency that was stored within it before the current `msg.sender` interacted with it. Since this contract has a `receive()` function, it is expected that it will hold native currency, which can be lost due to the above.

Open the report
LowFunction State Mutability Can Be Restricted to PuOKX - Audit 3 · CertiK · 16 May 2023Open

The functions `bytesToAddress()` and `bytesToBytes4()` do not read any state variable so they can be restricted to pure. In addition, the function `verifyWyvern()` does not modify any state variable, so it can be changed to view.

Open the report
LowInaccurate Require StatementOKX - Audit 3 · CertiK · 16 May 2023Open

The function `shareToAmount()` is used to calculate the withdrawal amounts with the specified LP tokens. After the update in the total supply of the LP, the following check is executed: ```solidity=156 require(totalSupply > amount, MetaXInvestErrors.NOT_ENOUGH); ```` According to UniswapV2 design, `MINIMUM_LIQUIDITY = 10**3` LP tokens have been locked. Therefore, a more precise statement is as follows: ```solidity require(totalSupply > amount + MINIMUM_LIQUIDITY, MetaXInvestErrors.NOT_ENOUGH); ```

Open the report
LowInconsistent Handling of Other MarketsOKX - Audit 3 · CertiK · 16 May 2023Open

In the two functions labeled `trade()`, there is a check on the provided `tradeData` that the `orderToAddress` is the `msg.sender`. However, this check is done for some of the markets instead of all of them.

Open the report
LowMissing Validation of Token Order in Function `wiOKX - Audit 3 · CertiK · 16 May 2023Open

In the implementation of Uniswap V2, the function `IUniswapV2Pair(pool).getReserves()` retrieves the reserves of tokens in which the first reserve corresponds to the token0 and the second one is the token1. The token0 and token1 are recorded according to the rule that address(token0) < address(token1). In the function `withdraw()` and `_withdraw()`, the passed `tokensOut` does not validate whether the tokens are in the order that `address(tokensOut[0].token) < address(tokensOut[1].token)`. If one of them is a zero address, it needs to be converted to the weth address first. ```solidity=50 function withdraw( address pool, TokenOutInfo[] calldata tokensOut, bytes calldata data ) external override { // Check Length require(tokensOut.length == 2, 'Length Wrong'); // Decode feeRate+mode from data // mod=2 for withdraw two tokens;mod=0 for withdraw token0;mod=1 for withdraw token1 (uint256 feeRate, uint256 mode) = _getFeeRate(pool, data); // Read Status uint256 tokenAmount = _balanceOf(pool, address(this)); // Transfer to Pool _transfer(pool, pool, tokenAmount); // remove liquidity (uint256 amount0, uint256 amount1) = IUniswapV2Pair(pool).burn(address(this)); _withdraw(pool, feeRate, mode, tokensOut, amount0, amount1); // Event emit Withdraw(pool, tokenAmount); } ``` In this case, it will revert or transfer the wrong token amounts if the tokens are flipped.

Open the report
LowMissing Validation of Tokens in Function `amountTOKX - Audit 3 · CertiK · 16 May 2023Open

The function `amountToShare()` is used to calculate the liquidity given the amounts of two tokens. However, there is no check that the passed `tokenAmounts` has length 2 and the token order matches with the tokens in the underlying pool that has the requirement `address(token0) < address(token1)`. In the case that one of them is the eth, it needs to be converted to the weth first.

Open the report
Show 10 more
LowMissing Validation That Sum of `batchesAmount` IsOKX - Audit 3 · CertiK · 16 May 2023Open

In the function `smartSwapByInvest()`, the `fromToken` balance of the contract is redistributed proportionally with the ratio `batchesAmount[i] / baseRequest.fromTokenAmount` as shown in line 447-449. ```solidity=446 uint256[] memory newBatchesAmount = new uint256[](batchesAmount.length); for (uint256 i = 0; i < batchesAmount.length; ++i) { newBatchesAmount[i] = batchesAmount[i] * amount / baseRequest.fromTokenAmount; } ``` That means the sum of `batchesAmount[i]` should be the same as `baseRequest.fromTokenAmount`, but there is no such validation in the current implementation of `smartSwapByInvest()`.

Open the report
LowMissing Zero Address ValidationOKX - Audit 3 · CertiK · 16 May 2023Open

Addresses should be checked before assignment to ensure they are not zero addresses.

Open the report
LowOverwriting FailureOKX - Audit 3 · CertiK · 16 May 2023Open

In the function `tradeV2()`, low-level calls are performed in three places: 1. `beforeExecute()`, which transfers tokens from `msg.sender` to `address(this)` 2. During a call to `SEAPORT` 3. `afterExecute()`, which transfers tokens from `address(this)` to `msg.sender` The only way for the process to revert is during 2) if `isAtomic == true` and the call to `SEAPORT` fails and returns data. Hence, it is possible for 1) or 2) to fail but 3) succeeds. Note that 3) returns `true` even if no low-level calls are made, for example, using an unsupported `actionType` will have 3) return `true`.

Open the report
LowPossible Missing Functionality From `BaseAdapter`OKX - Audit 3 · CertiK · 16 May 2023Open

The `UniswapV2AdapterMain` contract inherits the `BaseAdapter` abstract contract which contains various functions that perform no operations. `UniswapV2AdapterMain` has overwritten some of the functions but not all of them, namely `unstake()`, `unStakeAndWithdraw()`, and `claimReward()`.

Open the report
LowPossibly Incorrect feeRate in Contract UniswapV2AOKX - Audit 3 · CertiK · 16 May 2023Open

The standard fee charged on the UniswapV2 pool is 3 out of 1000 deducted from the tokens to be swapped. In the contract `UniswapV2AdapterMain`, a custom fee rate is used to calculate `getAmountOut()`, which could result in a different value if the underlying pool uses the standard fee. **Yield-External-Audit/contracts/libraries/MetaXMath.sol** ```solidity=74 function getAmountOut( uint256 amountIn, uint256 reserveIn, uint256 reserveOut, uint256 feeInPrecision, uint256 precision ) internal pure returns (uint256 amountOut) { require( reserveIn > 0 && reserveOut > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY" ); if (amountIn > 0) { uint256 amountInWithFee = amountIn.mul(feeInPrecision); uint256 numerator = amountInWithFee.mul(reserveOut); uint256 denominator = reserveIn.mul(precision).add(amountInWithFee); amountOut = numerator / denominator; } } ```

Open the report
LowReserves Are Used In Calculation of Liquidity RemOKX - Audit 3 · CertiK · 16 May 2023Open

The function `shareToAmount()` is used to calculate the withdrawal amounts with the specified LP tokens. In the calculation of the withdrawal amounts of token0 and token1, the reserves of the pool are used instead of the balances of the pool. ```solidity=152 (uint256 r0, uint256 r1, ) = IUniswapV2Pair(pool).getReserves(); // TotalSupply uint256 totalSupply = _feeLp(r0,r1,pool); require(totalSupply > amount, MetaXInvestErrors.NOT_ENOUGH); uint256 amount0 = r0 * amount / totalSupply; uint256 amount1 = r1 * amount / totalSupply; r0 = r0 - amount0; r1 = r1 - amount1; ... ``` However, in the implementation of the function `burn()` in the UniswapV2, the balances in the pool are used to ensure pro-rata distribution. ```solidity function burn(address to) external lock returns (uint amount0, uint amount1) { (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings address _token0 = token0; // gas savings address _token1 = token1; // gas savings uint balance0 = IERC20(_token0).balanceOf(address(this)); uint balance1 = IERC20(_token1).balanceOf(address(this)); uint liquidity = balanceOf[address(this)]; bool feeOn = _mintFee(_reserve0, _reserve1); uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution ... ```

Open the report
LowTypo in Variable NameOKX - Audit 3 · CertiK · 16 May 2023Open

The variable `reserves` passed in the function `_exeForks()` seems to be `reverse` or `reversed` based on the following observation: - `reserves` is assigned with the value `and(rawData, _REVERSE_MASK)`, as the constant name `_REVERSE_MASK` indicates; - when `reserves` is true, it invokes the `IAdapter(path.mixAdapters[i]).sellQuote()` to sell the token1 in the pool pair; Otherwise, it calls `IAdapter(path.mixAdapters[i]).sellBase()` to sell the token0 in the pool pair. The second point implies that it sells the token1 if reversed and it sells the token0 if not reversed.

Open the report
LowUnchecked ERC-20 `transfer()`/`transferFrom()` CaOKX - Audit 3 · CertiK · 16 May 2023Open

The return value of the transfer()/transferFrom() call is not checked. ```solidity=352 weth.transfer(to, amount); ```

Open the report
LowUnlocked Compiler VersionOKX - Audit 3 · CertiK · 16 May 2023Open

The contracts cited have an unlocked compiler version. An unlocked compiler version in the source code of the contract permits the user to compile it at or above a particular version. This, in turn, leads to differences in the generated bytecode between compilations due to differing compiler version numbers. This can lead to ambiguity when debugging, as compiler specific bugs may occur in the codebase that would be hard to identify over a span of multiple compiler versions rather than a specific one.

Open the report
LowUnnecessary Receive FunctionOKX - Audit 3 · CertiK · 16 May 2023Open

All contracts in the auditing scope contain a `receive()` function that accepts direct transfers of the native token. However, there is currently no purpose for the contracts to hold native tokens and it is possible for users to take these tokens. Each contract contains mechanisms for a user to take any native tokens held by the contract: - `DexRouter` performs arbitrary swaps and performs no checks on `msg.value`, allowing a user to swap native tokens inside the contract; - `OkxNFTMarketAggregator` returns unused ETH, but performs no checks on `msg.value`, allowing a user to have native tokens inside the contract be viewed as unused ETH; - `Entrance` transfers tokens to arbitrary addresses in `_transferToken()` and the `msg.value` check in `_transferTokens()` can be bypassed if `msg.value == 0`, allowing the transfer of native tokens held inside the contract; - `UniswapV2AdapterMain` expects users to transfer tokens to the contract and deposit them, allowing users to deposit native tokens already held inside the contract. If the transfer of native tokens are meant to be used with a `payable` function, then it would be better to ensure `msg.value` is correct instead of having a `receive()` function.

Open the report
Show 10 more
LowUnprotected InitializerOKX - Audit 3 · CertiK · 16 May 2023Open

One or more logic contracts do not protect their initializers. An attacker can call the initializer and assume ownership of the logic contract, whereby she can perform privileged operations that trick unsuspecting users into believing that she is the owner of the upgradeable contract. ```solidity=18 contract Entrance is InvestBase, OwnableUpgradeable, ReentrancyGuardUpgradeable { ``` - `Entrance` is an upgradeable contract that does not protect its initializer. ```solidity=73 function initialize(IWETH _weth) public initializer { ``` - `initialize` is an unprotected initializer function.

Open the report
LowUnused EventOKX - Audit 3 · CertiK · 16 May 2023Open

The event `SetIfCheck(bool ifCheck)` is declared in the contract `Entrance`, but it has never been used in the codebase.

Open the report
LowUse of `delegatecall`OKX - Audit 3 · CertiK · 16 May 2023Open

Anyone is able to use the functions `trade()` and `tradeV2()`, which may make a `delegatecall` to `proxy` with data `tradeData`. ```solidity=191 (bool success, ) = isLib ? proxy.delegatecall(tradeData) : proxy.call{value: ethValue}(tradeData); ``` As `tradeData` is user defined, if `proxy` has functions that can change state variables, state variables of `OkxNFTMarketAggregator`, such as `_owner`, may be changed.

Open the report
LowFunction `approve` of Contract `OKCSwapERC20` DoeOKC Swap · CertiK · 13 Dec 2022Open

It is expected that calls of the form `approve(spender, amount)` fail if the address in `spender` is the zero address.

Open the report
LowMissing Zero Address Validation for TransfersOKC Swap · CertiK · 13 Dec 2022Open

It is expected that calls of the form - `transfer(recipient, amount)` - `transferFrom(from, dest, amount)` fail if the recipient address is the zero address.

Open the report
LowPossible for Unearned Rewards to be Locked InsideOKC Swap · CertiK · 13 Dec 2022Open

When there are no stakers, the total staked amount is 0. Then when updating rewards, only the reward update time is updated. ```solidity=165 modifier updateReward(address account) { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); ``` ```solidity=62 function rewardPerToken() public view override returns (uint256) { if (_totalSupply == 0) { return rewardPerTokenStored; } ``` If the duration of time for when there are no stakers is denoted as `T`, then `rewardRate * T` rewards will be forever locked in the contract. This is because when updating the reward amount, the contract does not handle rewards that have not been earned due to no stakers. ```solidity=135 function notifyRewardAmount(uint256 reward, uint256 newRewardsDuration) external override onlyRewardsDistribution updateReward(address(0)) { rewardsDuration = newRewardsDuration; if (block.timestamp >= periodFinish) { rewardRate = reward.div(rewardsDuration); } else { uint256 remaining = periodFinish.sub(block.timestamp); uint256 leftover = remaining.mul(rewardRate); rewardRate = reward.add(leftover).div(rewardsDuration); } ``` For example, suppose the first reward duration was for 10 days and 100 tokens are given as rewards each day. If there are no stakers for the first 5 days and the first staker stakes on the 6th day, then the 500 tokens meant to be given as rewards will be locked inside the contract forever.

Open the report
LowReward Check Can Be Bypassed If Reward Token andOKC Swap · CertiK · 13 Dec 2022Open

When adding rewards to the staking contract, a check is performed to ensure the contract's balance is sufficient for the stated reward. ```solidity=149 uint balance = rewardsToken.balanceOf(address(this)); require(rewardRate <= balance.div(rewardsDuration), "Provided reward too high"); ``` However, in the situation where the reward token is the same as the staking token, this check can be bypassed as `balance` will include tokens that users have staked. ### Proof of Concept The below test done in forge does the following: 1. A staking contract is created where the staking token and reward token are the same 2. 100 tokens are staked to the contract 3. `notifyRewardAmount()` is called and succeeds, stating 100 reward tokens are available even though no reward tokens were transferred to the contract ```solidity pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "forge-std/Test.sol"; import {mockERC20} from "../src/mocks/mockERC20.sol"; import {StakingRewards} from "../src/farm/StakingRewards.sol"; contract StakingRewardsTest is Test { StakingRewards public stakingRewards; mockERC20 public mockToken; function setUp() public { mockToken = new mockERC20(); stakingRewards = new StakingRewards(address(mockToken), address(mockToken)); } function testBypassRewardCheck() public { mockToken.mint(address(this), 100); mockToken.approve(address(stakingRewards), 100); stakingRewards.stake(100); stakingRewards.notifyRewardAmount(100, 1); } } ```

Open the report
LowSusceptible to Signature MalleabilityOKC Swap · CertiK · 13 Dec 2022Open

The signature malleability is possible within the Elliptic Curve cryptographic system. An Elliptic Curve is symmetric on the X-axis, meaning two points can exist with the same `X` value. In the `r`, `s` and `v` representation this permits us to carefully adjust `s` to produce a second valid signature for the same `r`, thus breaking the assumption that a signature cannot be replayed in what is known as a replay-attack. ### Proof of Concept Using the signature obtained from https://solidity-by-example.org/signature/, the below unit test in `forge` shows that two different `s` and `v` values can produce the same address for one `r` value. ```solidity pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "forge-std/Test.sol"; contract ECRecoverTest is Test { function setUp() public { } function getMessageHash( address _to, uint _amount, string memory _message, uint _nonce ) public pure returns (bytes32) { return keccak256(abi.encodePacked(_to, _amount, _message, _nonce)); } function getEthSignedMessageHash(bytes32 _messageHash) public pure returns (bytes32) { return keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", _messageHash) ); } function testVerifySignature() public { bytes32 r = 0x993DAB3DD91F5C6DC28E17439BE475478F5635C92A56E17E82349D3FB2F16619; bytes32 s_a = 0x6F466C0B4E0C146F285204F0DCB13E5AE67BC33F4B888EC32DFE0A063E8F3F78; bytes32 s_b = 0x90B993F4B1F3EB90D7ADFB0F234EC1A3D43319A763C0117891D4548691A701C9; uint8 v_a = 0x1B; // 27 uint8 v_b = 0x1C; // 28 bytes32 messageHash = getMessageHash(0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C, 123, "coffee and donuts", 1); bytes32 ethSignedMessageHash = getEthSignedMessageHash(messageHash); address owner_a = ecrecover(ethSignedMessageHash, v_a, r, s_a); address owner_b = ecrecover(ethSignedMessageHash, v_b, r, s_b); assertEq(owner_a, owner_b); } ```

Open the report
LowUnused InterfaceOKC Swap · CertiK · 13 Dec 2022Open

The following interface is unused: ```solidity=4 import '../interfaces/IOKCSwapPair.sol'; ```

Open the report
LowVariables That Could Be Declared as ImmutableOKC Swap · CertiK · 13 Dec 2022Open

The linked variables assigned in the constructor can be declared as `immutable`. Immutable state variables can be assigned during contract creation but will remain constant throughout the lifetime of a deployed contract. A big advantage of immutable variables is that reading them is significantly cheaper than reading from regular state variables since they will not be stored in storage.

Open the report
Show 4 more
LowMissing metadata validations on `make_order`OKX Marketplace of Solana · CertiK · 27 Jul 2022Open

The `make_order` instruction allows the maker to create an order in base to the **mint** of an expected NFT. However, the mint is missing validation to verify that it is the type of NFT that the program should accept. This is inconsistent with the validation made during the `take_order` instruction where the Metadata has some validations. This could lead to Orders that are impossible to take because during the `make_order` instruction they don't throw and error but during the `take_order` they do. Makers would not be informed about these problems in live time so they will not realize that their orders have problems.

Open the report
LowMissing rent-exempt validation when update `protoOKX Marketplace of Solana · CertiK · 27 Jul 2022Open

The account `protocol_fee_recipient` has a rent-exempt validation when the protocol configuration is set up using the instruction `init_protocol_config`. However, the instruction `update_protocol_fee_recipient` that allows to update this account doesn't have the same validation, if a no rent-exempt account is passed it will not throw any error.

Open the report
LowNFT metadata doesn't have sufficient validationsOKX Marketplace of Solana · CertiK · 27 Jul 2022Open

The `metadata_account` is a PDA storing the metadata of an NFT. It is only used during the `take_order` instruction to get the `seller_fee_basis_points` (total royalty fee) and the `creators` (creators' address and share in percentage of the total royalty fee). This account is a PDA obtained using the `mpl_metadata_program` (Metaplex's metadata program) and the NFT's mint (`spl_token` mint). However, this account could be missing some validations, for example: - The address of the official `mpl_metadata_program` is used to generate the PDA's address and to validate it with the passed address. However, the PDA could be owned by an unknown program replicating the `mpl_metadata_program` program. - [The `key` field](https://docs.rs/mpl-token-metadata/1.2.5/mpl_token_metadata/state/enum.Key.html) could be unexpected between the following options: - Uninitialized, - EditionV1, - MasterEditionV1, - ReservationListV1, - MetadataV1, - ReservationListV2, - MasterEditionV2, - EditionMarker, - UseAuthorityRecord, - CollectionAuthorityRecord - [The `token_standard` field](https://docs.rs/mpl-token-metadata/1.2.5/mpl_token_metadata/state/enum.TokenStandard.html) could be unexpected between the following options: - NonFungible - FungibleAsset - Fungible - NonFungibleEdition

Open the report
LowUseless `token_program` field on order accountOKX Marketplace of Solana · CertiK · 27 Jul 2022Open

The order account uses the field `token_program` to store the official token program account's address. This operation is made on the instruction `make_order` where the address is compared to the official address given by the library `spl_token`. However, this field is never used by this instruction or other instruction. This means that the order account is storing `32` unused bytes that could be removed to reduce the rent costs. This can be translated to an extra of 0.0011136 SOL by order or 1 SOL every 898 orders.

Open the report

Incidents1

Attached through a shared contract address. Our incident data ends in Oct 2026.

Incident

Incident,

Source: De.Fi incident database.

Audits before it
4.
Audits since
1, the latest dated 19 May 2024.

Contracts

No deployed address is recorded for these audits.

The reports name a repository or files. Open a report to see its scope.

The repositories and files the reports name

Show this on your site

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.

Content

Facts

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.

Fixed as
Language

Style

Layout
Theme
Accent
Corners

Format

Size
Date
Embed as
HTML
<head>
<script src="https://widget.trustblock.run/project-widget.min.js" defer></script>
</head>
<body>
<div
data-tb-project-widget
data-tb-project-slug="okc-swap"
></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