K9 Farming
Smart Contract Audit Report
Executive Summary
This report presents the outcomes of our collaborative engagement with the K9 team, focusing on the comprehensive evaluation k9-shibarium-contracts repository.
Our team conducted an initial security assessment from June 16th to August 13th, 2024, and updated the report on August 26th to reflect changes from commit 59b704d on the team's GitHub repository to Shibarium testnet.
This report was updated again on September 3rd, 2024 to reflect changes to the FarmingInstanceV2 contract from address 0x7fE1..1a65
on Puppynet to commit 31c5b4a
on the team's private GitHub repository. This report was updated again on September 18th, 2024 to reflect mainnet deployment.
K9 Farming is a new platform which includes a new token and various staking methods to earn rewards.
Audit Scope
Name |
Source Code - Proxy (Shibarium) |
Source Code - Implementation (Shibarium) |
Visualized |
esKNINE |
|||
FarmingFactoryV2 |
|||
FarmingProxyV2 |
|||
FarmingInstanceV2 |
|||
RealYieldStaking |
|||
Vesting |
Name/Source Code |
Visualized |
esKNINE
|
|
FarmingFactoryV2
|
|
FarmingInstanceV2
|
|
FarmingProxyV2
|
|
RealYieldStaking
|
|
Vesting
|
Audit Findings
High findings were identified and the team must review them. In addition, centralized aspects are present.
Finding #1 |
FarmingInstanceV2 |
High Resolved |
Finding #1 - FarmingInstanceV2
|
||
Description: Minimum amounts to receive from swaps during zaps and unzaps are not specified, leaving them susceptible to front running. If a token's price is manipulated before a zap or unzap occurs, users may swap their tokens for an unexpected loss. Risk/Impact: A malicious user can execute a sandwich attack to profit from the contract's swap, resulting in a loss of funds for the user as described in Finding #1. Recommendation: An expected price parameter should be added to the zap() and unzap() functions. This parameter should be used to compare with the calculated price at the time of the function's execution, as well as for calculating an expected minimum amount to receive from the swap. Resolution: Minimum amounts are now specified when zapping or unzapping to protect against price manipulation. |
||
Finding #2 |
FarmingInstanceV2 |
Medium Resolved |
Finding #2 - FarmingInstanceV2
|
||
Description: No minimum amounts to receive are specified when adding or removing liquidity during a zap or unzap, leaving these operations susceptible to front running. Risk/Impact: A price manipulation before a liquidity add or removal could result in the user receiving less LP or individual pool tokens than intended. Recommendation: An expected price parameter should be added to these contracts as previously mentioned to calculate expected LP or token amounts to receive from a liquidity add or removal. Resolution: Minimum amounts are now specified when zapping or unzapping to protect against price manipulation. |
||
Finding #3 |
FarmingInstanceV2 |
Informational Open |
Finding #3 - FarmingInstanceV2
|
||
Description: Liquidity Pools consisting of fee-on-transfer tokens are not supported as staking tokens.
Recommendation: The project team should exercise caution and avoid creating FarmingInstances using fee-on-transfer tokens for staking unless the proper exemptions are made. |
||
Finding #4 |
FarmingInstanceV2 |
Informational Open |
Finding #4 - FarmingInstanceV2
|
||
Description: The FarmingInstanceV2 contract does not support fee-on-transfer tokens for Bonus Pool rewards. Recommendation: The project team should exercise caution and avoid creating Bonus Pools using fee-on-transfer tokens as reward tokens unless the proper exemptions are made. |
System Overview
esKNINE Token & Staking
ESKNINEThe Escrowed KNINE token is a reward token minted to users who are staked in a FarmingInstance and can be used for additional staking in the Vesting contract. Token transfers are restricted, only allowing the token contract to transfer tokens or callers with the Transferrer Role to execute transfers. The Admin and Minter Roles have the authority to mint any amount of tokens to any address at any time, while the Burner Role allows token holders to burn their own tokens. Additionally, the Admin Role can withdraw any tokens held within the contract to their own address and can update the FarmingFactory address at any time. The FarmingFactory address has the ability to grant the Minter Role to any address at any time. This contract complies with the ERC-20 standard.
KNINE STAKINGThe RealYieldStaking contract allows users to stake KNINE with optional locking periods to earn native token rewards over time. Deposits can be made either as locked or unlocked, with locked deposits offering higher rewards based on a multiplier that increases with the lock duration. The pool's reward distribution begins once a start time is set by the Default Admin after deployment, and continues until the funds are depleted or the Default Admin ends the pool. Native tokens are automatically added to the contract's reward supply upon receival.
Users can specify a lock duration up to 12 months when depositing. The weight of their deposit is determined based on the amount staked and the lock duration. The lock multiplier scales linearly up to the max lock multiplier for a lock duration of 12 months. The contract mints an equivalent sKNINE amount as a representation of the user's stake, which is non-transferable and used solely for voting purposes. The user can also increase their lock duration at any time. Rewards accumulate over time and are calculated based on the pool's reward rate and the user’s stake weight. Users can manually claim their accumulated rewards at any time or automatically upon withdrawal. Withdrawals can only occur after the lock period ends if the tokens were locked, or at any time for unlocked tokens. If a user withdraws unlocked tokens and the Vesting address has been set, the vesting schedule is recalculated accordingly. sKNINE equivalent to the user's withdrawal amount is burned from them upon withdrawal.
The Default Admin has the ability to execute an emergency stop while distribution is active. Any funds reserved for future reward distribution are withdrawn and future reward accrual is stopped. The Default Admin also has the ability to withdraw any ERC20 tokens, erroneous KNINE, or excess native tokens not reserved for reward distribution at any time. Additionally, the Default Admin can update the associated Vesting address at any time. Both the Default Admin and Admin Roles have the ability to update the reward rate and maximum lock multiplier at any time.
ESKNINE STAKINGThe Vesting contract allows users to deposit esKNINE to be converted into vested KNINE. In addition, the contract implements ERC20VotesUpgradeable functionality, granting voting power to participants. In order to deposit, the user must have a staked KNINE balance in the associated RealYieldStaking contract. To ensure backing, a user's deposits cannot result in the user exceeding the minimum "backing ratio" between their deposited esKNINE and staked KNINE amounts. If the locked-only flag is enabled, only staked KNINE that is actively locked is used for backing calculations.
A vest's duration begins at the time of deposit, where KNINE rewards are linearly unlocked for claims throughout the deposit's vesting period. Reward amounts are determined by the esKNINE deposit amount and the contract's "vesting ratio". Users also minted vKNINE equal to their deposit amount, granting them voting power. vKNINE cannot be transferred. Rewards accrue throughout the vesting period and can be manually claimed by the user, or automatically claimed upon an unlocked withdrawal from the RealYieldStaking contract. Claims are handled based on the current vesting state and whether the contract is paused. In a paused state, debts and earned amounts are accumulated for future claims. When not paused, users receive their tokens and rewards directly, and the contract burns the corresponding esKNINE tokens.
If a user withdraws from the RealYieldStaking contract, their deposit data is updated to reflect the reduced staked amount, recalculating both the reserved and vested amounts in accordance with the backing and vesting ratios. If the staked amount decreases below the reserved amount, the function may reduce the user's deposit or remove it entirely.
Both the Default Admin and Admin Roles can toggle the locked-only flag and adjust the vesting period, vesting ratio, and backing ratio at any time. In addition to the previously mentioned permissions, the Default Admin has the ability to pause or emergency stop the contract at any time, as well as withdraw any tokens from the contract, excluding esKNINE. Deposits and token withdrawals are disabled while the contract is paused. If an emergency stop is executed, all vesting is stopped and deposit functionality is permanently disabled. As a result, users will lose any future vesting tokens and rewards from ongoing vests, but can withdraw a portion of their deposited esKNINE equal to the percentage of time left in the vest.
Farming
FARMING INSTANCE V2The FarmingInstance contract allows users to deposit LP tokens in exchange for rewards. The contract supports both standard LP deposits and "zap" deposits, where the one of the pool's input tokens are automatically converted into LP Tokens. The main pool provides rewards in the form of minted esKNINE. Bonus pools with custom reward tokens can also be added to a FarmingInstance through the associated Farming Factory address. Users accrue rewards from these pools based on their share of total deposited liquidity.
For zap deposits, the contract handles liquidity provision, including swapping tokens as necessary, and stores the resulting LP Position NFT in this contract. A zap fee is first taken from the provided amount and transferred to the FarmingFactory. A portion of the remaining input tokens swapped to minimize impact to the existing reserve ratio of the liquidity pool. Both tokens and native currency can be zapped.
Users can manually claim rewards from either all pools at once or individual pools. A Position's rewards are automatically harvested upon a withdrawal or unzap. Users can withdraw their LP tokens at any time, provided they have not been deposited as a zap. A user must withdraw their zap deposits by unzapping. Upon unzapping, any unclaimed fees earned by the zap deposit's created Position are first transferred to the withdrawing user. The contract then distributes the underlying assets back to the user after deducting an exit fee and performing any necessary swaps.
The FarmingProxy contract utilizes the EIP-1967 standard for proxy functionality. The FarmingInstance implementation address used by all registered FarmingProxys can be upgraded by the Default Admin at any time.
FARMING FACTORY V2The FarmingFactory contract is used to create and manage FarmingInstances initialized through created FarmingProxy contracts. Any Admin can create and register a new FarmingInstance through this contract at any time. The FarmingInstance is created with a specified token pair, upper and lower ticks for desired pricing, start and end times, and reward rate for the main pool. Upon creation, the Proxy address is granted the Minter Role within the esKNINE contract, used for minting esKNINE rewards. Any Admin can add a Bonus Pool to a registered FarmingInstance through this contract with a custom reward token, rate, and accrual period.
The Factory's Default Admin has the ability to stop a FarmingInstance pool's rewards, or update the main pool's esKNINE rewards per second at any time. Once stopped, any of the pool's reward tokens reserved for future distribution are transferred to the Default Admin. The Factory Default Admin has the authority to update the Router, NFTPositionManager, wBONE, and esKNINE addresses at any time. The Factory Default Admin can also modify the entry and exit fees to up to 100% each and can adjust the upper and lower ticks for any registered FarmingInstance to any amounts at any time. Additionally, the Factory Default Admin has the ability to withdraw any tokens or native currency from the FarmingFactory at any time.
Vulnerability Analysis
Vulnerability Category | Notes | Result |
---|---|---|
Arbitrary Jump/Storage Write | N/A | PASS |
Centralization of Control |
|
WARNING |
Compiler Issues | N/A | PASS |
Delegate Call to Untrusted Contract | N/A | PASS |
Dependence on Predictable Variables | N/A | PASS |
Ether/Token Theft | N/A | PASS |
Flash Loans | N/A | PASS |
Front Running | N/A | PASS |
Improper Events | N/A | PASS |
Improper Authorization Scheme | N/A | PASS |
Integer Over/Underflow | N/A | PASS |
Logical Issues | PASS | |
Oracle Issues | N/A | PASS |
Outdated Compiler Version | N/A | PASS |
Race Conditions | N/A | PASS |
Reentrancy | N/A | PASS |
Signature Issues | N/A | PASS |
Sybil Attack | N/A | PASS |
Unbounded Loops | N/A | PASS |
Unused Code | N/A | PASS |
Overall Contract Safety | PASS |
About SourceHat
SourceHat has quickly grown to have one of the most experienced and well-equipped smart contract auditing teams in the industry. Our team has conducted 1800+ solidity smart contract audits covering all major project types and protocols, securing a total of over $50 billion U.S. dollars in on-chain value!
Our firm is well-reputed in the community and is trusted as a top smart contract auditing company for the review of solidity code, no matter how complex. Our team of experienced solidity smart contract auditors performs audits for tokens, NFTs, crowdsales, marketplaces, gambling games, financial protocols, and more!
Contact us today to get a free quote for a smart contract audit of your project!
What is a SourceHat Audit?
Typically, a smart contract audit is a comprehensive review process designed to discover logical errors, security vulnerabilities, and optimization opportunities within code. A SourceHat Audit takes this a step further by verifying economic logic to ensure the stability of smart contracts and highlighting privileged functionality to create a report that is easy to understand for developers and community members alike.
How Do I Interpret the Findings?
Each of our Findings will be labeled with a Severity level. We always recommend the team resolve High, Medium, and Low severity findings prior to deploying the code to the mainnet. Here is a breakdown on what each Severity level means for the project:
- High severity indicates that the issue puts a large number of users' funds at risk and has a high probability of exploitation, or the smart contract contains serious logical issues which can prevent the code from operating as intended.
- Medium severity issues are those which place at least some users' funds at risk and has a medium to high probability of exploitation.
- Low severity issues have a relatively minor risk association; these issues have a low probability of occurring or may have a minimal impact.
- Informational issues pose no immediate risk, but inform the project team of opportunities for gas optimizations and following smart contract security best practices.