Pinjam

Smart Contract Audit Report

Audit Summary

Pinjam Audit Report Pinjam Labs is releasing a new innovative decentralized lending platform implementing the Diamond Standard. Users additionally earn rewards by depositing collateral.

For this audit, we reviewed the project team's Diamond contract, DiamondToken contract, facets folder, libraries folder, and vaultStrategyContract folder with the exception of the AaveStrategyFacet, SimpleRewardsStrategy, and SmartChefStrategyFacet folders at commit 3e434a75e1258f5840ea635e26adb7fd56acda64 on the team's private GitHub repository.

Audit Findings

All findings have been resolved, though some centralized aspects are present.
Date: June 7th, 2023.
Updated: June 16th, 2023 with changes from commit 4bc84e92017bb52dda785ab1ad6c79d416025d27 to commit 3e434a75e1258f5840ea635e26adb7fd56acda64.

Finding #1 - CompoundStrategyFacet - Low (Resolved)

Description: QI tokens are swapped for the Vault's "want" token at any price when harvesting, allowing the potential for frontrunning opportunities on any swap.
Risk/Impact: If harvesting rewards grow to a significant value, a user could potentially manipulate the QI/want token price for profit. As a result, the platform would receive less want token rewards than intended.
Recommendation: The team should consider adding a Price Oracle, such as a TWAP, to prevent frontrunning opportunities. Alternatively, the team can adjust the harvesting incentive percentage as needed in order to encourage more frequent swaps of smaller amounts.
Resolution: The team is now utilizing the Equilibre Pair contract to implement TWAP functionality. As the Equilibre Pair contract was not included within the scope of this audit, we are unable to provide an assessment with regards to its security or functionality.

Finding #2 - LibPool - Low (Resolved)

Description: Users are allowed to borrow as long as their resulting health factor would be above the liquidation threshold.
Risk/Impact: An inexperienced user could borrow at very marginally above the liquidation threshold. A subsequent price drop in their collateral would result in liquidation.
Recommendation: The team should consider adding a buffer to the health factor users are allowed to borrow at to prevent immediate liquidation.
Resolution: The team has implemented a threshold so users can only borrow if their health factor would be above 1.5.

Finding #3 - LibPool - Informational (Resolved)

Description: The _liquidate() function accepts a _receiveAToken parameter that is not used.
Recommendation: The team should consider removing this parameter or implementing functionality that would incorporate the parameter.
Resolution: The team has implemented the above recommendation.

Contracts Overview

  • The team must exercise caution when adding assets to avoid using fee-on-transfer tokens.
Diamond Contract:
  • This contract is used to perform core platform functionality and store platform data by supporting various Facet functionality.
  • "Diamond Cuts" can be executed by the Diamond's owner in order to add, remove, or replace support for various functionality.
  • When calldata is passed to the contract, the included function selector is used to determine the associated Facet and function call to delegate to.
  • Users should ensure that their specified function selector is supported by the contract and executes the intended functionality.
  • The contract's owner is set to a specified address upon deployment.
DiamondToken Contract:
  • This contract is used to create either DebtTokens or pTokens, and can include various Facet functionality similar to the Diamond contract.
  • ERC20 and AccessControlFacet functionality is initially added for both pTokens and DebtTokens.
  • TokenManagerFacet functionality is also initially added for created pTokens.
  • As described in the Diamond contract, diamond cuts can also be executed by the owner of this contract.
  • The contract owner and Core Diamond addresses are specified upon deployment.
DiamondCutFacet Contract:
  • This contract is used to manage the function selectors for various Facet addresses.
  • A Facet address must be a Contract.
  • Each Facet address may have any number of function selectors, but no Facet address may share a function selector with another Facet.
  • The Diamond's owner may add a new function selector for a Facet address at any time.
  • The Diamond's owner may replace a function selector for a Facet address at any time.
  • The Diamond's owner may remove a function selector from a Facet address at any time.
  • The Diamond's owner may set a new contract owner at any time.
PTokenFacet Contract:
  • This contract is used to represent a user's share of their deposited assets of a certain type.
  • The Diamond contract can mint or burn any amount of tokens from any address at any time.
  • When tokens are burned from a user, the underlying asset value of the burned pTokens is transferred to the specified receiver.
  • Whenever a user's balance is updated, their balance is updated in the token's OnwardIncentivesController address.
  • As the OnwardIncentivesController was not included within the scope of this audit, we are unable to provide an assessment with regards to its security or functionality.
  • The Diamond contract can transfer any of the contract's underlying assets to any address at any time.
  • The Diamond contract can update the associated Treasury address at any time.
DebtTokenFacet Contract:
  • This contract is used to represent a user's debt to the platform of a certain asset type.
  • The Diamond contract can mint or burn any amount of tokens from any address at any time.
  • Users cannot transfer, approve, or grant allowances of DebtTokens.
  • A User can adjust a delegatee's borrow allowance at any time, allowing the delegatee to borrow on their behalf.
  • Whenever a user's balance is updated, their balance is updated in the token's OnwardIncentivesController address.
  • The Diamond contract can decrease any delegatee's borrow allowance of any user's tokens at any time.
PoolFacet Contract:
  • This contract enables users to perform various actions for supported tokens in the Lending Pool through the Diamond contract.
  • Users may deposit collateral into the Lending Pool when deposits are enabled.
  • pTokens corresponding to the deposited asset are minted to the user.
  • Users may also deposit on behalf of another address, if desired.
  • The total amount of deposited assets may not exceed the "supply limit" if one has been set for the asset.
  • The supply limit is set per asset and may vary between assets.
  • If the asset has vault deposits enabled the user may elect to have their collateral "worked" by the Lending Pool.
  • All of the pToken's underlying assets will be transferred to its associated Strategy.
  • A "min reserve amount" will be maintained in the pToken contract if the asset has a "max capital efficiency" set.
  • Users will be credited with the contract's collected vault fees when depositing.
  • pTokens are burned when withdrawing the underlying asset from the Lending Pool.
  • Users may only withdraw or toggle the use of deposited collateral if their current health factor would remain above the liquidation threshold.
  • Users may only transfer pTokens if their health factor would not fall below 1.2.
  • A user may borrow an amount of an asset from the Lending Pool, provided that the user has deposited enough funds to be used as collateral and the asset has borrowing enabled.
  • The total amount borrowed for the asset may not exceed its "debt limit", if one has been set.
  • The debt limit is set per asset and may vary between assets.
  • Borrowed funds have no deadline to be paid back, however interest will be accrued as time passes. This can lead to a decrease in the loan's health factor, based on the liquidation threshold and the value of the borrowed assets relative to the value of the users deposited collateral.
  • A reserve factor of all accrued interest is minted to the Treasury address.
  • If a user's health factor drops below 1 and above 0.95, another user can liquidate up to 50% of the funds borrowed and receive a portion of the collateral plus an additional percentage of the collateral as a bonus.
  • If the health factor drops below 0.95, another user can liquidate up to 100% of the funds borrowed and receive all of the users deposited collateral.
  • A user's health factor can be increased by either repaying borrowed funds or by depositing further collateral.
  • Users can specify a different address to receive debt, provided they have been given a borrow allowance.
  • Users will additionally earn rewards from deposited assets.
  • All deposited assets will be transferred to the Vault associated with the deposited asset.
  • Users will earn accumulated rewards as a ratio of the amount of assets deposited to the total amount of assets deposited.
  • A farm fee will be taken from all collected rewards. The corresponding amount of pTokens will be minted to the Treasury address owner by the team.
  • Of the farm fee, 5% is retained in the contract until the next user deposits.
  • Users will be credited with their pending rewards on deposits and withdrawals but rewards must be claimed separately.
PoolManagerFacet Contract:
  • This contract allows addresses approved in the AccessControlStorage contract to perform various admin functions on specified assets.
  • An approved admin address may withdraw all funds from the Vault associated with the specified asset at any time.
  • An approved admin address may move funds from the pToken to the Vault associated with the specified address.
  • An approved admin address may update the rate at which interest and debt accumulate and the target utilization ratio for any asset at any time.
  • An approved admin address may update the Vault associated with an asset.
  • An approved admin address may set the rate at which reserve is held in a pToken contract rather than being deposited to the Vault at any time.
  • An approved admin address may toggle deposits and borrows for a specified asset at any time.
  • An approved admin address may set the loan-to-value, liquidation threshold, and liquidation bonus for a specified asset at any time.
  • An approved admin address may set the supply and debt limits for a specified address at any time.
  • An approved admin address may set the borrow reserve factor and farming reserve factor at any time.
  • An approved admin address may toggle the deposit to vault status of a pToken at any time.
  • The Diamond's owner may transfer the ownership of a pToken and debt token for a specified asset at any time.
  • The Diamond's owner may set the Liquidator and Treasury addresses at any time.
  • The Diamond's owner may set the IncentivesController address for a specified pToken or Debt token at any time.
TokenManagerFacet Contract:
  • This Facet is used to facilitate certain Pool and PoolManager functionality.
  • This consists of setting an assets associated Vault, working funds, and withdrawing funds, and harvesting their earned Vault rewards.
  • As the Vault contracts were not included within the scope of this audit, we are unable to provide an assessment with regards to their security or functionality.
PoolFactoryFacet
  • This contract allows the Diamond's owner to add new assets to the Lending Pool.
  • A new pToken and debt token will be deployed for the corresponding asset.
  • The asset will then be added to the Lending Pool.
OracleFacet Contract:
  • This contract is used to manage Sources for various Assets.
  • Each Asset may be associated with only one Source.
  • Each Source is used to report the current price of its corresponding Asset.
  • The Source contracts are outside the scope of this audit so we are unable to give an assessment in regard to security. However, the team plans to use Witnet Oracles for their Kava contracts and Chainlink Oracles for all other chains.
  • An approved admin address in the AccessControlStorage contract may set the Source for any Asset at any time.
PinjamVaultFacet Contract:
  • This contract is used to work funds by depositing them into various Strategy Facets.
  • Any asset tokens held by the contract are deposited into the contract's default Strategy upon any Vault deposit if the Strategy has not been marked as paused.
  • When withdrawing, the Vault will first check if sufficient idle funds are present to simply transfer to the user.
  • If there is not a sufficient balance to allow this, funds are withdrawn from the contract's associated Strategies and subsequently transferred to the withdrawer.
  • As the contract's logic can potentially allow excess funds to be withdrawn from Strategies in order to fulfill a withdrawal, the Vault's remaining asset balance is subsequently deposited into the contract's default Strategy if the Vault's Strategies are not marked as "frozen".
  • Any user can harvest any Strategy's rewards at any time, where they are intended to receive a portion of the earnings as a reward for harvesting.
  • The owner can "rebalance" any amount of funds from an associated Strategy to another one at any time.
  • The owner can add a new Strategy to the list of Strategies at any time. The project team may also need to initialize the Strategy through this contract to enable its functionality.
  • The owner can withdraw any miscellaneous tokens from the contract at any time, excluding the Vault asset.
  • The owner can update the Vault's set pToken and Vault asset at any time.
  • The owner can update a Strategy's Harvest on Deposit flag at any time.
  • The owner can mark a Strategy as paused or freeze all Strategies at any time.
  • The owner can update the default Strategy at any time.
CompoundStrategyFacet Contract:
  • This contract is intended to be used as a Vault Strategy.
  • When depositing into a Pinjam Vault using this Strategy, cTokens equal to the Pinjam Vault's want balance are minted.
  • When withdrawing from a Pinjam Vault using this Strategy, cTokens are intended to be redeemed for the specified want token amount using the cToken contract's current exchange rate.
  • When a user harvests this Strategy's rewards, QI tokens are first claimed from the associated Comptroller contract.
  • A Call Fee is taken from the resulting QI tokens and transferred to the harvester as a reward for harvesting.
  • The remaining QI tokens are swapped for the CompoundStrategy's "want token" using the contract's QI to Want token path.
  • If the contract's "Curve Exchange" flag is enabled, the associated KAVA 3 Pool is then used to execute an additional exchange.
  • The Call Fee percentage, want token, and QI to Want token path are set upon initialization and cannot be updated.
  • Harvesting is automatically triggered on any deposit if the "Harvest on Deposit" flag is enabled.
  • As the Comptroller, QI token, cToken, and KAVA 3 Pool contracts were not included within the scope of this audit, we are unable to provide an assessment with regards to their security or functionality.
  • The owner can update the Harvest on Deposit flag at any time.
AccessControlFacet Contract:
  • This contract is used to control Admin functionality within the PoolManager and Oracle Facets.
  • The Diamond contract owner can transfer ownership at any time.
  • The Diamond contract owner can add or remove an address as an Admin at any time.
AddressRegistryFacet Contract:
  • This contract is used to both fetch Diamond's associated Facet implementations, and is for external use only.
  • Only the Diamond owner can update an associated implementation address.
  • The implementations that can be updated consist of the following contracts: AddressRegistryFacet, AccessControlFacet, DiamondCutFacet, DiamondLoupeFacet, OracleFacet, PoolManagerFacet, PTokenFacet, DebtTokenFacet, PoolFacet, TokenManagerFacet, PoolFactoryFacet, and PoolDataFacet.

Audit Results

Vulnerability Category Notes Result
Arbitrary Jump/Storage Write N/A PASS
Centralization of Control
  • A PoolManagerFacet admin may withdraw all funds from a pToken's vault.
  • A PoolManagerFacet admin may update a pToken's vault.
  • A PoolManagerFacet admin may update the rate at which interest and debt accumulate for any asset.
  • A Diamond's owner may update the Facet address associated with any function call.
  • A Diamond's owner may add or remove any functionality from any Facet.
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 N/A 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

Contract Source Summary and Visualizations

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.