Numoen security record

As of 14 Oct 2026, Numoen has 1 audit report on Trustblock, by CertiK, dated 7 Dec 2022. That report lists 1 open critical finding.

Critical findings
1 open critical finding
Reported in “Numoen” by CertiK, Dec 2022. Not marked fixed in that report. See the findings
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. Statuses are the security firms’ own. Trustblock does not review code.

Audits1

Newest first

AuditSecurity firmDateScopeCritical findingsReport
NumoenSecurity firm: CertiKDate: Scope: Files named in the report1 open critical findingOpen · skynet.certik.com

Findings8

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

SeverityOpenAcknowledgedFixed
Critical100
High000
Medium000
Low304
CriticalCurve Model Could Lead to ArbitrageNumoen · CertiK · 7 Dec 2022Not fixed

In the pair contract, swapping tokens/mint(add liquidity)/burn(remove liquidity) operations need to check the invariant to be consistent. In the following is a code snippet that goes through the current implementation of the `verifyInvariant()` function. ```solidity if (!verifyInvariant(balance0, balance1, liquidity + totalSupply)) revert InvariantError(); ``` ```solidity function verifyInvariant( uint256 r0, uint256 r1, uint256 shares ) public view returns (bool valid) { uint256 scale0 = PRBMathUD60x18.div(PRBMathUD60x18.div(r0, shares), 10**baseScaleFactor); uint256 scale1 = PRBMathUD60x18.div(PRBMathUD60x18.div(r1, shares), 10**speculativeScaleFactor); uint256 a = scale0; uint256 b = PRBMathUD60x18.mul(scale1*, upperBound); uint256 c = PRBMathUD60x18.powu(scale1, 2) / 4; uint256 d = PRBMathUD60x18.powu(upperBound, 2); if (scale1 > 2 * upperBound) revert SpeculativeInvariantError(); return a + b == c + d; } ``` The above code implements the following the following equation: $$a + b = c + d$$ The above equation can be simplified as following steps: Step 1: $$scale0 + scale1*upperBound = \frac{scale1^2}{4} + upperBound^2$$ Step 2: $$scale0 = \frac{scale1^2}{4} - scale1*upperBound + upperBound^2$$ Step 3: $$scale0 = (\frac{scale1}{2} - upperBound)^2$$ The above formula will be part of the curve between [0, 2*upperBound]. However, this model could lead to potential economic attacks. This is because when adding liquidity or removing liquidity, users can adjust the expected output amount of base tokens or speculative tokens to gain more profit. In the following example, an attacker can mint 1 ETH of liquidity and burn 1 ETH of liquidity to gain profits because he can withdraw more tokens from the liquidity than he would normally be allowed. **Proof of concept** 1. Initially, 1 ETH of liquidity is added to the 16 ETH base tokens and 2 speculative tokens. The assumed price of speculative tokens 8. 2. The attacker can add another 1 ETH of liquidity to the collection of 16 ETH base tokens and 2 speculative tokens. 3. Attacker burns his 1 ETH of liquidity along with 9 ETH base tokens and 4 speculative tokens. 4. Since the price of speculative tokens is 8, the attacker actually gains a profit of 7 additional base tokens. ```solidity function testAttackOnCurveModel() public { // Initially, there is 1 liquidity with 16 base tokens and 2 speculative tokens // Suppose current market price is 1:8 _pairMint(16 ether, 2 ether, 1 ether, cuh); //The attacker added 1 liquidity another 16 base tokens and 2 speculative tokens _pairMint(16 ether, 2 ether, 1 ether, cuh); assertEq(pair.totalSupply(), 2 ether); uint baseTokenBalanceBefore = base.balanceOf(address(cuh)); uint speculativeTokenBalanceBefore = speculative.balanceOf(address(cuh)); //The attacker can actually withdraw much more tokens than he added as liquidity pair.burn(cuh, 9 ether , 4 ether , 1 ether); assertEq(pair.totalSupply(), 1 ether); uint baseTokenBalanceAfter = base.balanceOf(address(cuh)); uint speculativeTokenBalanceAfter = speculative.balanceOf(address(cuh)); assertEq(baseTokenBalanceAfter - baseTokenBalanceBefore, 9 ether); assertEq(speculativeTokenBalanceAfter - speculativeTokenBalanceBefore, 4 ether); } ``` **Result** The tests pass which means that the attack is successful based on the current model.\ [PASS] testAttackOnCurveModel() (gas: 272404) **[CertiK, 11/14/2022]:** \ This model does have fancy features and can restrict the boundary of the price. However, due to the decentralized feature of blockchain, those tokens trading/swapping with the pair contract may lead to inconsistency issues and thus cause arbitrage risk. Here are two main concerns regarding the curve model: 1. When the token is also available in AMM like UniSwap/PancakeSwap using xy=k as their curve model, the inconsistency with other AMMs could lead to arbitrage due to different models used (see the above example). 2. The curve itself is not consistent regarding the swapping process (see the following two testcases). ```solidity // case 1: swap 2 ether speculative tokens with 9 ether base tokens function testSwapOnCurveModel() public { // Initially, there is 1 liquidity with 16 base tokens and 2 speculative tokens _pairMint(16 ether, 2 ether, 1 ether, cuh); // To swap 2 ether speculative tokens out, 9 ether base token are required base.mint(cuh, 9 ether); vm.prank(cuh); base.transfer(address(pair),9 ether); uint speculativeBalanceBefore = speculative.balanceOf(address(cuh)); pair.swap(cuh, 0, 2 ether); uint speculativeBalanceAfter = speculative.balanceOf(address(cuh)); // In normal cases, assertEq(speculativeBalanceAfter - speculativeBalanceBefore, 2

Open the report
LowPayment for minting tokensNumoen · CertiK · 7 Dec 2022Not fixed

From the analysis of the contracts, the minting and burning of NRD tokens within the `Lendgine.sol` contract seem to be open to any user to access. No centralized roles have been put in place to control this part of the process. There seems to be a mechanism in place that controls payment in exchange for the minting of tokens. Within the `mint()` function, there is a call to the `msg.sender` which is supposed to be a contract address that contains the function `MintCallback()`. ```sol=136 IMintCallback(msg.sender).MintCallback(amountS, data); ``` Upon further investigation, the `mint()` function is actually calling an implementation of the interface `IMintCallback`. ```sol=9 function MintCallback(uint256 amount0, bytes calldata data) external; ```

Open the report
LowPotential Front-Running RiskNumoen · CertiK · 7 Dec 2022Not fixed

To deposit into the contract, users are supposed to call `mint()` to add liquidity. However, if the `mint()` and `deposit()` calls are invoked in separate transactions, it might lead to front-run attack. In this situation, an attacker can front run the original `deposit()` with his own address. Therefore, the original user might lose his liquidity forever.

Open the report
LowUnlocked Compiler VersionNumoen · CertiK · 7 Dec 2022Not fixed

The contract has 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 different compiler versions. This can lead to an 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

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="numoen"
></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