Author | flush
Editor | Liz
Introduction
As the decentralized finance (DeFi) ecosystem continues to evolve rapidly, AAVE V2 has established itself as one of the leading decentralized lending protocols by offering innovative solutions for borrowing and liquidity management. Its trustless mechanism and efficient capital utilization have attracted a wide range of users and institutions. However, as the platform’s adoption and the scale of funds involved grow, the importance of security audits and risk management measures has become increasingly prominent. This manual delves into the core design, key functionalities, and audit considerations of the AAVE V2 protocol.
Project Background Overview
AAVE V2 is an open lending platform built on the Ethereum blockchain. It enables users to deposit various ERC-20 tokens to earn interest and borrow market assets by paying interest. By introducing the concept of “interest rate markets,” AAVE V2 achieves decentralized fund pool management and automated interest rate adjustment mechanisms. Additionally, AAVE V2 provides advanced features such as flash loans, collateralized loans, and token swaps to meet diverse user needs, further cementing its position as a leader in the DeFi space.
Project Architecture Analysis
The architecture of AAVE V2 is centered on key functionalities such as user operations, fund flow management, collateral mechanisms, liquidation processes, and interest rate strategies. It aims to deliver efficient and secure decentralized lending services. Below is a comprehensive analysis:
User Operations Flow
- Users: Users can perform various actions, including deposits, loans, repayments, withdrawals, interest rate mode switching, flash loans, and credit delegation. When users interact with the protocol, corresponding aTokens are minted or burned automatically to represent their deposit entitlements and provide interest earnings based on interest rate strategies.
- Credit Delegation: Users can delegate their credit to others, enhancing protocol flexibility and use cases.
Core Components
- LendingPool: The core module responsible for handling all user operation requests, including deposits, loans, repayments, interest rate mode switching, flash loans, and liquidations, while updating interest rates and states.
- Collateral Manager: Manages collateral assets to ensure safe and controllable borrowing activities. When collateral is insufficient, it triggers liquidation to maintain overall system liquidity.
- Libraries: Provide logic for savings, validations, and general functionalities like liquidation and borrowing calculations, supporting the LendingPool.
GenericLogic: Computes and validates user states, including asset valuation, collateral calculations, and health factor assessments.
ReserveLogic: Manages reserve pools by tracking and updating the deposit and borrow amounts as well as current interest rates for each asset.
ValidationLogic: Verifies that user actions comply with protocol rules by performing rigorous checks on collateral and liabilities during operations such as deposits, borrowing, repayments, liquidations, and flash loans.
Debt and Tokenization
- Debt Tokens: Track user borrowing liabilities with a 1:1 correspondence to borrowed amounts. Debt tokens come in fixed-rate and variable-rate options (e.g., DebtDAI Stable, DebtDAI Variable) and are non-transferable.
- aTokens: Minted in a 1:1 ratio to deposited assets and accrue interest over time to reflect the earnings of deposits. A scaled balance (ScB) mechanism is used to store principal and interest together.
The formulas are as follows:
Pricing and Interest Rates
- Oracles Proxy: Relies on external oracles (e.g., Chainlink) to provide market price data for assets, ensuring accurate collateral valuation and system stability.
- Lending Rate Oracle: Offers dynamic interest rates based on system state and market conditions, optimizing capital utilization and liquidity.
Configuration and Management
- Configurator: Configures system parameters, such as risk metrics and borrowing limits for various assets, and manages reserve operations, including activation, borrowing, collateralization, freezing, and updating parameters. It ensures dynamic protocol adjustments in response to market changes.
Other Key Features
- Liquidation Manager: Oversees liquidation operations when collateral value falls below the liquidation threshold, safeguarding system funds. Liquidators are rewarded for their efforts.
- Reserves Balances: Stores reserve fund data to calculate and adjust interest rate strategies.
Interest Rate Strategy
- Interest Rate Strategy: Dynamically adjusts interest rates based on market conditions and user demand to achieve optimal capital allocation while considering liquidity risks. This ensures the system remains flexible and stable under varying market conditions.
Despite having two interest rate models (stable and variable), both models use a breakpoint-based calculation. Below the optimal utilization rate, slope1 applies, while slope2 applies above the optimal utilization rate. Both models are further divided into fixed and variable rate models under these conditions.
The formulas are as follows:
Process Overview
Deposits
Users deposit funds by calling the deposit
function of the LendingPool
contract, which accepts four parameters: asset address, deposit amount, recipient address, and referral code. The contract first verifies that it is not in a paused state. Then, through ValidationLogic.validateDeposit
, it ensures that the deposit amount is greater than 0 and confirms that the reserve is active and not frozen. Next, the system updates the reserve state by invoking reserve.updateState()
, which recalculates the cumulative liquidity index and variable borrowing index, as well as the interest accrued over the time period. A portion of the interest is minted and transferred to the protocol treasury.
The formula is as follows:
Subsequently, the reserve.updateInterestRates
function dynamically adjusts the liquidity rate, stable borrowing rate, and variable borrowing rate based on the latest supply-demand relationship. These rates are calculated and updated using the DefaultReserveInterestRateStrategy.calculateInterestRates
function. For asset transfer, the system transfers the user’s base assets to the aToken
contract and mints an equivalent amount of aTokens
to the onBehalfOf
address specified by the user. aToken
adopts a scaled balance mechanism to handle interest accumulation. If it is the user's first deposit, the system automatically designates the asset as collateral.
Compared to Compound, AAVE V2’s deposit process has the following features:
- Supports specifying a recipient address (
onBehalfOf
). - Validates deposits via the
ValidationLogic
contract. - Updates cumulative liquidity and variable borrowing indices and allocates protocol treasury interest.
- Simultaneously adjusts liquidity, stable borrowing, and variable borrowing rates.
- Uses
aToken
’s scaled balance mechanism to handle interest. - Automatically marks the first deposit as collateral.
Withdrawals
Users perform withdrawals by calling the withdraw
function. The process first retrieves the reserve data of the specified asset, including the corresponding aToken
address, and checks the user’s balance in the aToken
. Then, the system invokes ValidationLogic.validateWithdraw
to validate the withdrawal request, ensuring that the withdrawal amount is valid, the user’s balance is sufficient, and the reserve is active.
The system uses the GenericLogic.balanceDecreaseAllowed
function to examine the user’s health factor and assess whether the withdrawal would affect the collateral. This is similar to Compound’s getHypotheticalAccountLiquidityInternal
function. Within the balanceDecreaseAllowed
function, calculateUserAccountData
and calculateHealthFactorFromBalances
calculate the liquidation threshold after the withdrawal and check the user’s total collateral, total borrowings, and current health factor to ensure the user remains in a safe liquidity threshold.
The formula for calculating the health factor (HF) is as follows:
Next, the system updates the reserve state and interest rates, passing the withdrawal amount to the function. If the withdrawal amount equals the user’s current balance, the system updates the user configuration and marks the reserve as no longer being used as collateral. Finally, the system burns the user’s aToken
and transfers the withdrawn assets to the specified address.
Compared to Compound, AAVE V2’s withdrawal process has the following features:
- Uses
aToken
to represent users’ deposits in the protocol, and withdrawals essentially involve burningaTokens
. - Allows users to withdraw to a specified address (via the
to
parameter), providing greater flexibility. - Offers options for partial or full withdrawals.
- Employs a more sophisticated
balanceDecreaseAllowed
function to verify the impact of withdrawals on users’ overall collateral status. - Directly updates interest rates during the withdrawal process, unlike Compound, which updates rates through the
accrueInterest
function.
Borrowing
Users initiate borrowing by calling the borrow
function. The process first retrieves the current price of the asset from the price oracle and converts the borrowed amount into its ETH equivalent. Then, the system checks the legality of the borrowing operation using ValidationLogic.validateBorrow
and calculates the user’s total collateral, total debt, current loan-to-value ratio (LTV), liquidation threshold, health factor, and market stability through GenericLogic.calculateUserAccountData
. This is similar to Compound’s getHypotheticalAccountLiquidityInternal
function.
The reserve.updateState
function updates the reserve state, such as interest rates and borrowing indices, similar to Compound’s accrueInterest
function. The system then generates debt according to the user’s chosen interestRateMode
(stable or variable), using different token contracts for each model. While minting the debt token, the system verifies whether the onBehalfOf
address is the caller and subtracts the borrowing authorization from the token contract if it isn’t. If this is the user’s first borrowing action, the system marks them as an active borrower. After minting the debt token, the protocol updates borrowing rates through updateInterestRates
to reflect the new rates and changes in the reserve pool after the borrowing. If the user requests the borrowed underlying asset to be released, the protocol transfers the asset directly to the user.
Compared to Compound, AAVE V2’s borrowing process has the following features:
- Supports both stable and variable interest rate modes.
- Employs a separate validation logic contract for borrowing validation.
- Uses DebtTokens to represent users’ borrowings.
- Supports credit delegation, allowing users to borrow on behalf of other addresses.
Repayments
Users perform repayments through the repay
function, which first retrieves the user’s current debt (including stableDebt
and variableDebt
). Based on the user’s chosen interest rate mode (stable or variable), the system validates the repayment operation through ValidationLogic.validateRepay
, ensuring the user has sufficient debt balance for repayment. The repayment amount determines whether the system handles a partial or full repayment.
If the repayment amount is less than the current debt, the system uses the provided amount for partial repayment. Otherwise, it clears all debts. The system updates the reserve state via updateState
, recalculating and updating interest, borrowing volume, and borrowing indices. Subsequently, the system burns the corresponding stable debt tokens and updates borrowing rates through updateInterestRates
. If all debts are cleared, the system marks the user’s borrowing status as inactive. Finally, the user transfers the repayment amount from their account to the protocol’s aToken
contract address.
Compared to Compound, AAVE V2’s repayment process has the following features:
- Supports repayments for both stable and variable interest rate modes.
- Uses DebtTokens to represent and manage debt, burning the corresponding tokens during repayment.
- Supports both partial and full repayments, handling stable and variable debts separately.
- Allows users to repay debts on behalf of other addresses through credit delegation.
Liquidations
Liquidations are executed via the liquidationCall
function of the LendingPool
, which internally calls the liquidationCall
function of LendingPoolCollateralManager
through a proxy. First, the system retrieves collateral and debt data of the user and calculates the user’s health factor through GenericLogic.calculateUserAccountData
.
The ValidationLogic.validateLiquidationCall
function ensures the legitimacy of the liquidation call, checking the user’s health factor, debt status, and collateral configuration. If the health factor is below the threshold, the collateral is flagged, and neither type of debt (stable or variable) is zero, the call is validated. The system then calculates the maximum debt eligible for liquidation and determines the actual debt amount to be liquidated. If the liquidation debt exceeds the user’s available collateral, the liquidation amount is adjusted accordingly.
For liquidators who opt to receive the underlying collateral, the system ensures sufficient liquidity in the reserve. The reserve state is updated, and the system burns the corresponding amounts of stable and variable debt tokens. Borrowing rates are updated to reflect market changes post-liquidation. If the liquidator chooses to accept aToken
, they receive the equivalent aToken
. If they opt for the underlying asset, the system updates the collateral status, burns the corresponding aTokens
, and transfers the underlying asset to the liquidator. The liquidation debt amount is then transferred from the liquidator to the reserve’s aToken
address, completing the liquidation process.
Compared to Compound, AAVE V2’s liquidation process has the following features:
- Supports a combination of multiple collateral and debt assets for liquidation.
- Allows liquidators to choose between receiving
aToken
or the underlying asset. - Features a more modular liquidation process, separating validation logic and calculation logic into different functions.
- Supports both stable and variable debt types for liquidation.
Flash Loans
Users initiate flash loans via the flashLoan
function of the LendingPool
. As a unique borrowing mechanism, flash loans allow users to either repay immediately or settle the loan as a debt for later repayment, depending on the modes
parameter (0 for immediate repayment, 1 for stable debt, and 2 for variable debt).
The system first validates the parameters via ValidationLogic.validateFlashloan
, calculates the flash loan’s premium, and transfers the required aTokens
to the recipient address. It invokes the recipient’s executeOperation
function to carry out predefined operations for the flash loan. AAVE’s flash loan implementation includes exchange, liquidation, and repayment operations. Once executeOperation
completes, the system records the loan repayment amount and fees. If the user opts for non-debt repayment, the reserve state is updated, liquidity is accumulated, and liquidity indices are recalculated. Finally, the requested funds are transferred back to the protocol. If the user opts to process using the debt mode, the _executeBorrow
function is invoked to initiate the corresponding debt position.
Switching Debt Modes
In AAVE V2, users can switch between stable interest rate mode and variable interest rate mode using the swapBorrowRateMode
function. First, the getUserCurrentDebt
function is called to retrieve the user's current stable and variable debt for the target asset, determining the user's debt status. Then, the ValidationLogic.validateSwapRateMode
function is invoked to validate whether the switching operation is legitimate. This process checks whether the user has sufficient stable or variable debt to support the mode switch, ensuring that the target mode aligns with the asset's configuration and the user's debt situation. The reserve.updateState
function is then called to update the reserve's state, ensuring the reserve data is up-to-date. Following this, the two types of debt tokens are converted—burning stable debt tokens and minting variable debt tokens, or burning variable debt tokens and minting stable debt tokens. Once the conversion is complete, the reserve.updateInterestRates
function is used to update the interest rates for the target asset, ensuring they reflect the current market conditions and changes in the user's debt.
Security Vulnerability Checklist
Rounding Vulnerability Caused by Empty Markets
Both AAVE and Compound are susceptible to vulnerabilities caused by precision loss in empty markets. In an empty market (where no users are engaging in borrowing or lending), the value of the liquidityIndex
in the cumulateToLiquidityIndex
function depends on the quantity of the underlying asset tokens associated with the contract. Attackers can exploit this by depositing a large amount of the underlying asset tokens into the contract via flash loans, thereby manipulating the price of aToken.
Similar to the first hack of the Compound fork project, Hundred Finance, in the HopeLend incident, the attacker first manipulated the liquidityIndex
, setting the value of hETHWBTC to WBTC at a 1:1 ratio. By exchanging underlying assets and leveraging borrowing, the attacker further increased the value of the liquidityIndex
. The attacker then executed repeated flash loans in a loop, continuously invoking the _handleFlashLoanRepayment
function.
In the cumulateToLiquidityIndex
function, the precision loss caused by rayDiv
further amplifies the value of reserve.liquidityIndex
, ultimately increasing the amount of WBTC that can be redeemed.
(Attack transaction: https://etherscan.io/tx/0x1a7ee0a7efc70ed7429edef069a1dd001fbff378748d91f17ab1876dc6d10392)
Audit Focus: When conducting an audit, it is crucial to assess whether the exchange rate calculation can be easily manipulated and whether the rounding methods used are appropriate. Additionally, it is recommended to advise project teams to mint aToken immediately after creating a new market to prevent manipulation in an empty market scenario.
Reentrancy Vulnerability Caused by ERC677/ERC777 Tokens
Similar to the second hack on the Compound fork project, Hundred Finance, in the Agave attack, the attacker invoked the liquidationCall
function to liquidate themselves without having any outstanding debt. The liquidated token was an ERC-677 standard token used on the Gnosis Chain. This type of token triggers an external call to the receiving address's function during a transfer, allowing the liquidation contract to invoke the attack contract.
During this process, the attack contract deposited 2,728 WETH obtained via flash loans, minting 2,728 aWETH. Using this as collateral, the attacker borrowed all available assets from the Agave project. After the external call ended, the liquidationCall
function directly liquidated the previously deposited 2,728 aWETH and transferred it to the liquidator.
function transfer(address _to, uint256 _value) public returns (bool) {
require(superTransfer(_to, _value));
callAfterTransfer(msg.sender, _to, _value);
return true;
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(super.transferFrom(_from, _to, _value));
callAfterTransfer(_from, _to, _value);
return true;
}
function callAfterTransfer(address _from, address _to, uint256 _value) internal {
if (AddressUtils.isContract(_to) && !contractFallback(_from, _to, _value, new bytes(0))) {
require(!isBridge(_to));
emit ContractFallbackCallFailed(_from, _to, _value);
}
}
function contractFallback(address _from, address _to, uint256 _value, bytes _data) private returns (bool) {
return _to.call(abi.encodeWithSelector(ON_TOKEN_TRANSFER, _from, _value, _data));
}
(Source: https://x.com/danielvf/status/1503756428212936710; Attack Transaction: https://gnosis.blockscout.com/tx/0xa262141abcf7c127b88b4042aee8bf601f4f3372c9471dbd75cb54e76524f18e)
Audit Focus: During audits, pay special attention to whether the lending-related code adheres to the CEI (Checks-Effects-Interactions) pattern or includes reentrancy locks. Additionally, consider the potential impacts of tokens with callback functionality.
Price Manipulation Risk Due to Improper Oracle Mechanisms
In the Blizz Finance hack, the issue arose as the price of LUNA continued to plummet. The protocol relied on inaccurate Chainlink price feeds, which allowed high-priced LUNA collateral to be used for borrowing funds. The project lacked a robust fail-safe mechanism. Although alerts seemed to have been raised beforehand, there were no timely preventive measures implemented to mitigate losses.
When the price dropped below a certain threshold, attackers were able to purchase large amounts of LUNA at market prices (much lower than $0.10 per unit) and use it as collateral (valued at $0.10 per unit) to borrow funds from the platform.
function _executeBorrow(ExecuteBorrowParams memory vars) internal {
...
uint256 amountInETH =
IPriceOracleGetter(oracle).getAssetPrice(vars.asset).mul(vars.amount).div(
10**reserve.configuration.getDecimals()
);
ValidationLogic.validateBorrow(
vars.asset,
reserve,
vars.onBehalfOf,
vars.amount,
amountInETH,
vars.interestRateMode,
_maxStableRateBorrowSizePercent,
_reserves,
userConfig,
_reservesList,
_reservesCount,
oracle
);
...
}
function validateBorrow(
...
) external view {
...
(
vars.userCollateralBalanceETH,
vars.userBorrowBalanceETH,
vars.currentLtv,
vars.currentLiquidationThreshold,
vars.healthFactor
) = GenericLogic.calculateUserAccountData(
userAddress,
reservesData,
userConfig,
reserves,
reservesCount,
oracle
);
require(vars.userCollateralBalanceETH > 0, Errors.VL_COLLATERAL_BALANCE_IS_0);
require(
vars.healthFactor > GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD,
Errors.VL_HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
);
vars.amountOfCollateralNeededETH = vars.userBorrowBalanceETH.add(amountInETH).percentDiv(
vars.currentLtv
); //LTV is calculated in percentage
require(
vars.amountOfCollateralNeededETH <= vars.userCollateralBalanceETH,
Errors.VL_COLLATERAL_CANNOT_COVER_NEW_BORROW
);
...
}
(Reference: https://x.com/BlizzFinance/status/1524911400992243761)
Audit Focus: During audits, attention should be given to the price feed mechanisms used to calculate collateral value, ensuring they are not easily manipulated by external factors. It is advisable to recommend that the project team adopt multiple price sources for comprehensive evaluation to mitigate risks caused by reliance on a single price source. Additionally, it is essential to check whether the project has a reasonable pause mechanism to handle such unexpected situations.
Unintended Issues Introduced by External Calls in Peripheral Contracts
In the interaction between the AAVE protocol and the Para Swap protocol, the _buyOnParaSwap
function of the Aave Collateral Repay Adapter V3 contract contains multiple security vulnerabilities. This function uses the safeApprove
method to set the allowance of assetToSwapFrom
on the tokenTransferProxy
to maxAmountToSwap
, but it fails to consider scenarios where the swap is either not executed or only partially executed. As a result, the remaining allowance may persist in situations where it has not been fully utilized.
Additionally, the function relies on external contract calls (augustus.call(buyCalldata)
) to execute the swap but does not sufficiently validate and restrict the paraswapData
input parameter. This allows attackers to manipulate the decoded buyCalldata
and augustus
contract address through maliciously crafted paraswapData
, enabling them to bypass the intended swap logic or avoid the swap entirely.
Since the function does not reduce or verify the token allowance of assetToSwapFrom
after the swap, even if the swap fails or is bypassed, attackers can exploit the unchanged high allowance to withdraw tokens from the contract, leading to unauthorized fund transfers. These vulnerabilities stem from insufficient validation of input data and swap results, as well as ineffective management of token allowances, making the contract exploitable by attackers.
Audit Focus: During audits, special attention must be given to the interaction code with external third-party protocols. Key points include evaluating whether the inputs and outputs of external contracts are tightly restricted, whether the interaction logic poses potential risks to the protocol’s core model or fund security, and whether input data is sanitized and validated to prevent malicious data from causing security issues. By thoroughly reviewing the external interaction code logic and data validation mechanisms, the risk of such vulnerabilities can be effectively reduced.
Compatibility Issues Between Tokens and Interest Rate Strategies
During the deployment of AAVE on the Polygon chain, functionality anomalies occurred due to an incompatible InterestRateStrategy setting, where an incorrect interest rate strategy was mistakenly assigned to WETH.
The interface of the incorrectly set InterestRateStrategy contract is as follows:
function calculateInterestRates(
address reserve,
uint256 utilizationRate,
uint256 totalStableDebt,
uint256 totalVariableDebt,
uint256 averageStableBorrowRate,
uint256 reserveFactor
)external view returns (…);
And the implementation of the LendingPool in AAVE V2 is as follows:
function calculateInterestRates(
address reserve,
address aToken,
uint256 liquidityAdded,
uint256 liquidityTaken,
uint256 totalStableDebt,
uint256 totalVariableDebt,
uint256 averageStableBorrowRate,
uint256 reserveFactor
)external view returns (…);
(Reference: https://x.com/mookim_eth/status/1659589328727859205)
Due to interface incompatibility, the new InterestRateStrategy could not be properly invoked by the LendingPool, directly resulting in the disruption of the AAVE V2 WETH pool functionality, where users were unable to deposit or withdraw ETH.
Audit Focus: During audits, it is essential to ensure that the interfaces of key components in the code (or fork) are fully compatible. Additionally, although the above issue was not caused by multi-chain characteristics, it is still necessary to consider whether the unique features of different chains might lead to unexpected results during audits.
The Dust Token Issue
In AAVE, deposits and withdrawals are managed using the setUsingAsCollateral
function, which sets the usingAsCollateral
flag to enable flexible collateral strategies. When an external protocol or contract borrows funds from AAVE for the first time, the borrowing function sets usingAsCollateral
to true
. Conversely, when the external protocol or contract fully withdraws funds from AAVE, the protocol processor in AAVE sets the usingAsCollateral
state to false
.
However, due to arithmetic precision errors, a minimal amount of aTokens may remain in the protocol processor during withdrawal. Consequently, the next time the protocol processor deposits funds into AAVE, the usingAsCollateral
state will not change and will remain set to true
. Since the protocol processor contract does not have an interface to call the setUserUseReserveAsCollateral
function, this situation may result in the protocol processor being unable to perform borrowing operations.
function deposit(
address asset,
uint256 amount,
address onBehalfOf,
uint16 referralCode
) external override whenNotPaused {
...
if (isFirstDeposit) {
_usersConfig[onBehalfOf].setUsingAsCollateral(reserve.id, true);
emit ReserveUsedAsCollateralEnabled(asset, onBehalfOf);
}
...
}
function withdraw(
address asset,
uint256 amount,
address to
) external override whenNotPaused returns (uint256) {
...
if (amountToWithdraw == userBalance) {
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
emit ReserveUsedAsCollateralDisabled(asset, msg.sender);
}
...
}
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
external
override
whenNotPaused
{
...
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, useAsCollateral);
...
}
Audit Focus: During audits, it is necessary to have a thorough understanding of the protocols being invoked. Based on a full grasp of their characteristics, assess whether there are compatibility issues in their interactions with external protocols, such as token compatibility and invocation logic compatibility.
Conclusion
This manual delves into the core design, key features, and associated audit points of the AAVE V2 protocol. We hope this guide will better assist developers and security researchers in identifying potential risks and ensuring the secure operation of the protocol.
References
[1] https://github.com/aave/protocol-v2
[2] https://github.com/YAcademy-Residents/defi-fork-bugs
[3] https://solodit.cyfrin.io/
[4] https://blog.solidityscan.com/aave-repay-adapter-hack-analysis-aafd234e15b9
[5] https://gist.github.com/mookim-eth/72f185019e8c4df3a1edba637067f734
About SlowMist
SlowMist is a blockchain security firm established in January 2018. The firm was started by a team with over ten years of network security experience to become a global force. Our goal is to make the blockchain ecosystem as secure as possible for everyone. We are now a renowned international blockchain security firm that has worked on various well-known projects such as HashKey Exchange, OSL, MEEX, BGE, BTCBOX, Bitget, BHEX.SG, OKX, Binance, HTX, Amber Group, Crypto.com, etc.
SlowMist offers a variety of services that include but are not limited to security audits, threat information, defense deployment, security consultants, and other security-related services. We also offer AML (Anti-money laundering) software, MistEye (Security Monitoring) , SlowMist Hacked (Crypto hack archives), FireWall.x (Smart contract firewall) and other SaaS products. We have partnerships with domestic and international firms such as Akamai, BitDefender, RC², TianJi Partners, IPIP, etc. Our extensive work in cryptocurrency crime investigations has been cited by international organizations and government bodies, including the United Nations Security Council and the United Nations Office on Drugs and Crime.
By delivering a comprehensive security solution customized to individual projects, we can identify risks and prevent them from occurring. Our team was able to find and publish several high-risk blockchain security flaws. By doing so, we could spread awareness and raise the security standards in the blockchain ecosystem.