Uniswap V2 mathematics and source code interpretation

Source: Gryphsis Academy

This research report provides a detailed interpretation of the working principle, project composition, source code analysis and other parts of the UniswapV2 protocol.The working principle mainly involves automatic market makers (AMMs). This research report will describe the creation and management process of liquidity pools and how to earn fees by providing liquidity; in terms of project composition, we mainly outline the architecture of UniswapV2., including major contracts (such as factory contracts, exchange contracts) and their functions; in the source code analysis section, we analyze UniswapV2’s smart contract source code and explain the design concepts of key functions and data structures.

1. Introduction to the agreement

UniswapV2 is a decentralized transaction protocol based on the Ethereum blockchain, allowing users to trade cryptocurrencies without trusting intermediaries.Unlike traditional centralized exchanges, UniswapV2 adopts the automatic market maker (AMM) model to manage transactions and liquidity pools through smart contracts, thereby achieving complete decentralization.

The core of UniswapV2 is its constant product formula (x * y = k),inxandyRepresents the number of two different assets in the liquidity pool respectively.kis a constant.This formula ensures that after each transaction, the proportion of assets in the pool is rebalanced, thus providing liquidity to users.This design makes the trading process transparent and fair, and users can add or remove liquidity at any time and earn profits through transaction fees.

The UniswapV2 protocol consists of multiple smart contracts, the most important of which are factory contracts and exchange contracts.Factory contracts are responsible for creating and managing liquidity pools, while each exchange contract corresponds to a specific trading pair (e.g. ETH/DAI).

In addition, UniswapV2 also introduced router contracts and library functions to improve transaction efficiency and security.Compared with its predecessor, UniswapV1, UniswapV2 brings several important improvements.First, the lightning exchange function allows users to borrow assets in a single transaction and return them before the transaction is over.The second is the price oracle, which provides more reliable price data by accumulating the price time-weighted average (TWAP).

In addition, UniswapV2 supports direct transactions of any ERC-20 token without the need to use Ethereum as an intermediary.UniswapV2’s success lies not only in its technological innovation, but also in its open and community-driven development model.The Uniswap protocol is free to use and expand on anyone, which brings unlimited possibilities to the decentralized finance (DeFi) ecosystem.Many other DeFi projects, such as lending platforms and stablecoin protocols, are built on Uniswap to form a thriving ecosystem.

In general, UniswapV2 has completely changed the way cryptocurrency transactions through its innovative protocol design and decentralized operating model and has become an important cornerstone in the DeFi field.With the continuous development of technology and the continuous innovation of the community, UniswapV2’s influence will be further expanded, bringing more financial freedom and opportunities to global users.

2. Protocol features

2.1 ERC-20 Pairs

Uniswapv1 will use Ethereum (ETH) as the transition currency, that is, if the user wants to exchange TokenA and TokenB, he needs to use TokenA to exchange ETH first, and then use ETH to exchange TokenB. Although this measure reduces the dispersion of liquidity,It puts huge cost pressure on liquidity providers.Every liquidity provider must have an interface to exchange with ETH. At the same time, the value of assets held by the liquidity provider fluctuates with the price of ETH, which may lead to serious losses.

When two assets ABC and XYZ are associated (e.g., they are both dollar stablecoins), liquidity providers on Uniswap will typically suffer smaller permanent losses in the ABC/XYZ pair compared to ABC/ETH orXYZ/ETH is right.In addition, using ETH as a forced transition currency increases traders’ costs.The trader will pay twice as much as the direct purchase of ABC/XYZ, and will also suffer two slippages.

In UniswapV2, liquidity providers are allowed to create pair contracts for any two ERC-20 tokens.While the surge in the number of transaction pairs between any ERC-20 tokens may make it more complicated to find a specific asset transaction path, this problem can be solved by higher-level routing (via off-chain or on-chain routers oraggregator).

2.2 Price oracle

The price of UniswapV1 is calculated by at time t, the marginal price provided by Uniswap (excluding handling fees) can be obtained by dividing the reserve amount of asset a by the reserve amount of asset b.The specific calculation formula is as follows:

However, UniswapV1 is not safe to use as an on-chain price oracle because it is very easy to manipulate.The manipulator will sell a large number of asset A at the beginning of a block to affect the price, and then perform other contract operations (non-Uniswap pair contracts) in the middle of the block, and finally buy back at the end of the block.The same amount of asset A brings the price back to normal levels.Uniswap V2 introduces a price accumulation mechanism, allowing third parties to use the average price of a certain range, which greatly increases the difficulty of price manipulation and makes manipulation behavior have no substantial benefits.

Specifically, Uniswap v2 accumulates this price by recording the accumulated sum of prices at the beginning of each block, where someone interacts with the contract.Each price is weighted based on the time elapsed since the previous block is updated, based on the block timestamp.This means that at any given time (after update), the value of the accumulator should be the sum of the spot price for every second in the contract history.

To estimate the time-weighted average price from time t₁ to t₂, an external caller can check the accumulator value at t₁, then check again at t₂, subtract the first value, and divide by the elapsed seconds.(Note that the contract itself does not store the historical value of this accumulator – the caller must call the contract at the beginning of the cycle to read and store this value.)

The oracle user can choose when to start and end the cycle.Choosing a longer cycle will make it more costly for an attacker to manipulate the time-weighted average price (TWAP), although this will result in a lower latest price.Because the average value is used, the average value of A/B and B/A in a certain interval is no longer a reciprocal relationship, so Uniswap V2 offers both prices.

2.3 Price calculation accuracy

Since Solidity does not support non-integer numerical data types, UniswapV2 adopts the UQ112.112 data format to improve the accuracy of price calculations, and uses uint112 to store the number of assets in the transaction pair, and uses 32 bits to record the creation time of the current block.This time recording method will cause Unix timestamps to overflow 100 years later, February 7, 2106.This is because Unix timestamps represent time in seconds since January 1, 1970 (called the Unix Era), and uint32 can represent a range from 0 to 2^{32} – 1 second,That is about 136 years.

To ensure that the system will still work properly on February 7, 2106 and beyond, UniswapV2 is designed to require oracles to check the price at least once every other overflow cycle (approximately 136 years).This is because in this design, the method of accumulating prices is still overflow-safe even if the timestamps overflow, and the price change can be calculated correctly even if the transaction spans the overflow interval.In this way, the system is ensured to remain accurate and reliable for a long time.

2.4 Lightning redemption

Lightning Exchange is an instant cryptocurrency exchange conducted on a blockchain platform, where users can quickly complete transactions between different cryptocurrencies without waiting for multiple blocks to confirm.Such transactions are usually automatically executed by smart contracts, ensuring that both parties to the transaction fulfill their exchange commitments at the same time, thereby reducing transaction risks and improving efficiency.In short, lightning exchange means trading first and then repaying. Through lightning exchange, zero-cost arbitrage can be achieved.

Let’s explain lightning redemption through a practical example. Assuming that there is an A/B trading pair in UniswapV2, we can borrow A first to get B from Uniswap, and then use B to get from other decentralized exchanges.A0, then return Uniswap’s equal amount of A.Through the above process, we completed the price difference arbitrage between A0-A at 0 cost.We do not need any principal, we only need to pay a gas fee.If you find that there is a price difference between other dexes on the chain, you can also perform similar operations on them at zero cost arbitrage.

2.5 Protocol Fee

UniswapV2 introduces a 0.05% protocol fee that can be turned on and off.If enabled, this fee will be sent to the feeTo address specified in the factory contract.Initially, feeTo was not set, so no fee was charged.A pre-specified address feeToSetter can call the setFeeTo function in the UniswapV2 factory contract and set feeTo to a different value.feeToSetter can also be called setFeeToSetter to change the feeToSetter address itself.

If the feeTo address is set, the protocol will start charging a 5 basis point fee, which is the 30 basis point fee earned by liquidity providers \frac{1}{6}.That is, traders will continue to pay 0.30% of all transaction fees, of which 83.3% (i.e. 0.25%) will be paid to liquidity providers and 16.6% (i.e. 0.05%) will be paid to the feeTo address.Charging this 0.05% fee at each transaction adds additional gas costs.To avoid this, the accumulated fees are only charged when liquidity is deposited or withdrawn.The contract calculates the accumulated fees and mints new liquidity tokens to the fee beneficiaries immediately before the token is minted or destroyed.

The accumulated fees can be calculated by measuring the growth of √k (i.e. √(x·y)), since the last time the fee was charged.This formula gives the cumulative expense of the percentage of liquidity in the flow pool between t₁ and t₂  as t₂ :

If the fee is activated before t₁, the feeTo address should be captured to ⅙ between t₁ and t₂.Therefore, we want to mint a new liquidity token to the feeTo address, representing the pool of Φ·f₁,₂, where Φ=⅙ .

That is to say, we want to choose sₘ to satisfy the following relationship, where s₁ is the total amount of outstanding shares at time t₁:

After some calculations, including using 1-

Instead of f₁,₂ and solve sₘ , we can rewrite it as:

Set Φ to ⅙ and we will get the following formula

Below, let’s explain it with a specific example.Suppose the initial depositor deposits 100 DAI and 1 ETH into a pair and get 10 shares.After a while (no other depositors were involved in the pairing), they tried to withdraw funds, at which point there were 96 DAI and 1.5 ETH in the pairing.Substituting these values ​​into the above formula, we get the following result:

2.6 Transaction fee calculation

UniswapV1 The calculation of transaction fee is the formula

This formula means that the constant product formula is only reduced first and then executed; in the version of UniswapV2, because of the existence of the Flash Swaps mechanism, the calculation formula for transaction fees is adjusted to

2.7 sync() and skim()

sync() is used to update the number of assets temporarily stored in the contract to the current actual value of the contract, mainly for handling cases where proportional imbalances and no liquidity providers are available.skim() is used to handle the situation where the number of assets in the contract exceeds the maximum value of uint112, allowing the user to withdraw the part of assets exceeding the maximum value of uint112.

2.8 Handling non-standard and infrequently used tokens

The standard ERC20 token contract needs to return a boolean value after transferring the token to indicate whether the transfer is successful, but not all tokens will do so.Some tokens do not return values.In UniswapV1, token transfers without return values ​​are considered as a failure by default, and the entire transaction is reset.And in Uniswap V2, token transfers without return values ​​are considered successful.

Additionally, UniswapV1 assumes that token transfers do not trigger reentry of the transaction pair, but some ERC20 tokens that support ERC777 hooks break this assumption.To support these tokens, UniswapV2 adds anti-reentry locking function to all public state variable modification functions, and also prevents reentry of user-defined callbacks in Flash Swaps.

2.9 Liquidity Initialization Settings

If the user provides liquidity for an existing trading pair A/B, then based on the current ratio of A and B, it can calculate how many ratios A and B to provide.However, when a transaction pair is initialized, there is no reference ratio. How should we deal with it at this time?In the version of UniswapV1, when a new liquidity provider deposits tokens into an existing Uniswap token pair, the number of liquidity tokens minted will be calculated based on the number of existing tokens.The specific calculation formula is as follows:

For the first person to provide liquidity, the formula isXstartingYes 0.In the face of this situation, the method adopted by UniswapV1 is that the value of the initial liquidity is directly equal to the amount of ETH initially provided. The problem with the provision of this initial liquidity is that the value of the trading pair is entirely determined by the proportion of the initial liquidity.But the problem is that there is no mechanism to guarantee that this ratio is in line with the real value.In UniswapV2, the initialization of liquidity can be done by the following formula

This formula meansSmintedThis is the amount of liquidity tokens you will receive,XdepositedThis is the number of the first tokens you deposit. For example, if you deposit ETH, thenXdepositedIt is the amount of ETH you deposit.YdepositedThis is the number of the second token you deposit.

For example, if you deposit DAI,YdepositedIt is the amount of DAI you deposit.This formula can ensure that the share in the liquidity pool will never be lower than the geometric average in the pool, but the value of this formula will also change with the number of tokens in the pool, in order to weaken the number of tokens in the fund poolDue to the impact of the change, UniswapV2 destroyed the initial 1e-¹⁵ liquidity, which is 1000 times the minimum liquidity of 1e-¹⁸.Although this is trivial for any transaction pair, it significantly increases the cost of attackers who profit from this mechanism.

2.10 WETH

Since the interface for trading Ethereum native currency ETH is different from that for trading ERC20 tokens, many protocols do not directly support ETH, but use an alternative WETH (packaged ETH token).UniswapV1 is an exception because its trading pairs directly include ETH, allowing users to trade directly using ETH.However, Uniswap V2 is designed to support trading pairs between any ERC20 tokens, and direct support for ETH can complicate the system and increase risk.

Therefore, ETH is not directly supported in UniswapV2, and users must convert ETH to WETH before using the transaction pair.In fact, UniswapV2 internally converts user-supplied ETH to WETH, which simplifies users’ operations without manually converting ETH to WETH.While this conversion is trivial for any transaction pair, it effectively improves the security and ease of operation of the system.

2.11 Deterministic trading pair address

Whether it is UniswapV1 or UniswapV2, all pairs are created through a single factory contract.In UniswapV1, the create opcode is used, and the address of the transaction-to-contract will be affected by the order in which it is created.In UniswapV2, a new opcode create2 is used, and the address generated by this method is determined.This means that the address of the transaction pair can be calculated in advance off-chain without querying the status on the chain.

2.12 Maximum number of tokens

To efficiently implement oracle functions, Uniswap V2 uses uint112 to save the number of tokens, which means its maximum supported number of tokens is 2¹¹² – 1 .For tokens with an accuracy of 18, this value is enough, about 5192296858534828 (5.19e¹⁵), that is, 5.19 trillion.If the record value in the contract exceeds this limit, the transaction will fail and be reset.As mentioned earlier, anyone can recover using the skim() function, solving this problem by removing excess assets from the liquidity pool.

3. Analysis of UniswapV2 principle

Uniswap is an automated liquidity protocol powered by constant product formulas and implemented in non-upgradeable smart contract systems on the Ethereum blockchain.It eliminates the need for trusted intermediaries, prioritizing decentralization, censorship resistance and security.Each Uniswap smart contract or currency pair manages a liquidity pool consisting of two ERC-20 token reserves.Anyone can exchange pool tokens by depositing the equivalent value of each underlying token, thus becoming a liquidity provider (LP) of the fund pool.These tokens track prorated LP shares in the total reserve and can redeem the underlying assets at any time.

First, we will introduce the automatic market maker mechanism of Uniswap. The following shows the function image of the UniswapV2 automatic market maker (AMM) model, which is based on the following formula:

in,xRepresents the number of Token A,yRepresents the number of Token B,kis a constant that indicates that the product of the two tokens in the pool remains unchanged.When a trader trades on Uniswap, he changes the number of tokens in the pool by adding or removing tokens to the pool.According to the constant product formula, the number of another token will change accordingly to maintain the productkconstant.This change determines the price of the transaction.

For example, if a trader wants to exchange Token B with Token A, they need to add a certain number of Token A to the pool, which will cause the number of Token B in the pool to decrease, thereby changing the price.The trader’s operations will move along this curve, changing the number and price of tokens.Any point on the curve satisfies the constant product relationship.

The working principle of UniswapV2 can be roughly divided into three parts: Liquidity Provider, Uniswap Pool, and Trader.The role of liquidity providers is that liquidity providers deposit two tokens (for example, Token A and Token B) into a Uniswap pool.

In the example shown in the figure, the liquidity provider deposits 10 Token A and 1 Token B; the Uniswap pool contains various tokens, such as 100 Token A and 10 Token B shown in the figure.

The liquidity share in the pool is represented by the liquidity token, which shows that there are 12 liquidity tokens in the figure; traders can submit tokens to the pool and exchange another token they need.For example, a trader can deposit 10 Token A and pay a 0.3% handling fee to get 1 Token B from the pool.

Let’s first look at how liquidity providers (LPs) provide liquidity.As shown in the figure below, liquidity providers deposit tokens into Uniswap pools to increase liquidity.

For example, in the figure, the liquidity provider deposits 3 Token A and 1 Token B.When liquidity providers deposit tokens, they receive pool tokens representing their liquidity share.In the figure, the liquidity provider obtained 12.4 pool tokens.The token reserves in the pool will increase, for example, the token reserves in the pool in the figure will become 1210 Token A and 399 Token B.More liquidity helps reduce price slippage and make trading more stable.

The constant product formula used by Uniswapx · y = kto determine the price curve.The increased liquidity expands the low slippage area and improves the price stability of transactions.Liquidity providers increase liquidity in the pool by depositing tokens and obtain corresponding liquidity tokens in return.This not only helps traders obtain more stable prices, but also brings benefits to liquidity providers of transaction fees.

Next, from the perspective of traders, how traders exchange tokens and what impact will trading behavior have on Uniswap Pool.

As shown in the figure below, traders want to exchange tokens on Uniswap.For example, in the figure, the trader intends to exchange 3 Tokens.Traders enter 3 Tokens A and pay a 0.3% handling fee.Eventually, the trader will get about 0.997 Token Bs as output.The transaction changes the reserve balance in the pool, resulting in a new price.Before the transaction, there were 1200 Token A and 400 Token B in the pool.

According to the constant product formulax·y = kThere will be about 1203.009 Token A and about 399.003 Token B in the after-transaction pool.Uniswap usex·y = kThe constant product formula of the price curve is defined.As the trader exchanges, the number of tokens in the pool changes, and the price curve also adjusts to determine the new price.

4. Source code analysis

4.1 Core operation flow chart analysis

In this section, we will introduce the three most commonly used operations in UniswapV2, namely adding liquidity, removing liquidity, and exchanging tokens.We will analyze the contracts they call and the functions they call through the flowchart to gain a deeper understanding of the source code of UniswapV2.

4.1.1 Add liquidity

When a user adds liquidity, the user first calls the UniswapV2Router.sol contract to provide the number of Token A and Token B. The addLiquidity function of the UniswapV2Router.sol contract receives the user’s request and processes it.

The addLiquidity function further calls the UniswapV2Pair.sol contract. In the UniswapV2Pair.sol contract, the mint function is called to perform the actual liquidity addition operation. The mint function calculates the liquidity tokens to be minted based on the number of Token A and Token B provided by the user (The number of LP tokens) and allocate these LP tokens to the user. After the liquidity addition operation is completed, the mint function calls the _update function to update the reserve.

4.1.2 Exchange tokens

When a user wants to exchange tokens, he first calls the UniswapV2Router.sol contract to provide the number of input tokens and the minimum number of output tokens.

Then the swapExactTokensForTokens function of the UniswapV2Router contract receives the user’s request and processes it. The swapExactTokensForTokens function further calls the UniswapV2Pair.sol contract. In the UniswapV2Pair.sol contract, the swap function is called to perform the actual token exchange. The swap function is based on the number of input tokens and reserves., calculate the number of tokens to be output, and perform the exchange. After the exchange is completed, the swap function calls the _update function to update the reserve and accumulated handling fee.The specific process is as follows:

4.1.3 Removal of liquidity

The user first calls the UniswapV2Router.sol contract to provide the number of LP tokens to be withdrawn. The removeLiquidity function of the UniswapV2Router.sol contract receives the user’s request and processes it. The removeLiquidity function further calls the UniswapV2Pair.sol contract. In the UniswapV2Pair.sol contract, it callsThe burn function performs the actual liquidity withdrawal operation. The burn function calculates the number of Tokens A and Token B to be returned based on the number of LP tokens provided, and returns these tokens to the user.

4.2 Core contract

UniswapV2 Core contract is a core part of Uniswap, a decentralized trading platform, and is responsible for implementing its automated market maker (AMM) functions.Unlike traditional order books, Uniswap passes liquidity pools and constant product formulasx·y = kto realize the transaction.The liquidity provider deposits two tokens into the pool and obtains liquidity tokens as credentials.When a user conducts a transaction, the contract calculates the transaction price based on the number of tokens in the pool and the constant product formula.UniswapV2 introduced several improvements, including ERC20 pairs direct transactions, price oracle improvements, flash loans and protocol fees adjustments.The core components of the core contract include the following three files:

  • UniswapV2Pair.sol: manages liquidity pools for each trading pair, handles token exchanges, liquidity additions and removals

  • UniswapV2Factory.sol: Responsible for creating and managing transaction pairs

  • UniswapV2ERC20.sol: Standard implementation of liquidity tokens, representing the share of liquidity providers

4.2.1 UniswapV2 Factory.sol

The role of the UniswapV2Factory contract is responsible for creating and managing trading pairs (liquidity pools).This contract allows users to create new trading pairs and record all created trading pairs.In addition, it manages transaction fee receiving addresses and setter addresses.UniswapV2Factory.sol has five functions, let’s take a look at it separately

  • constructor function: constructor function, used to initialize UniswapV2Factory contract.The input is the transaction fee setter address _feeToSetter , and the output is None.

  • allPairsLength function: Returns the number of all created transaction pairs.The input is none, and the output is the unit of the number of all transaction pairs.

  • createPair function: Creates a new transaction pair.The input is the two token addresses of tokenA and tokenB, and the output is the transaction pair address pair created.

  • setFeeTo function: Set the transaction fee acceptance address.The input is the new transaction fee acceptance address _feeTo, and the output is None.

  • setFeeToSetter function: Sets the new transaction fee setter address.The input is the new transaction fee setter address _feeToSetter, and the output is None.

The specific code analysis is as follows:

createPair function

The function of the createPair function is to create a transaction pair with TokenA and TokenB. After entering TokenA and TokenB in the front end, it will first check whether TokenA and TokenB are the same currency. Then it will make a simple sort of TokenA and TokenB, followed byCheck the address of Token0 and require that the address of Token0 cannot be 0;

Then check whether this token pair exists by requiring(getPair[token0][token1] == address(0), ‘UniswapV2: PAIR_EXISTS’); and only if it exists, it can continue; then obtain UniswapV2Pair through creationCodeThe bytecode of the contract;

Then use the hash values ​​of token0 and token1 as the salt value to ensure that the address of each token pair is unique, because if the address of the token pair is not unique, then the trader’s liquidity may be added to the wrong pool;Then, create a contract using the inline assembly create2 instruction to ensure the uniqueness and predictability of the contract address;

Then, initialize the newly created token pair contract, then update the mapping table, record the address of the token pair contract, and then add the newly created token pair contract address to the list of all token pairs, and finally trigger PairCreatedEvents to notify outside of new token pairs to be created.

4.2.2 UniswapV2 ERC20.sol

The main function of UniswapV2ERC20.sol is to implement ERC-20 tokens, which implements the ERC20 standard token function, specifically used in UniswapV2 liquidity pools.The contract includes basic operations such as casting, burning, approval and transfer.In addition, it supports permission function, allowing the use of signatures to approve token transfers.Let’s look at the functions it contains one by one:

  • constructor function: Initialize the contract, set DOMAIN_SEPARATOR for the permission function.Input None, output None.

  • _mint function: mint new tokens, input is the receiving address “to”, and mint quantity “value”, output is no

  • _burn function: destroy tokens, input is the destroy address from and the destroyed quantity value, output is none.

  • _approve function: Approve token transfer, owner address owner, approved address spender and approved quantity value, output none.

  • _transfer function: transfer token, input is the transfer address from, receive address to and transfer quantity value, output is none.

  • approve function: the public approval function, the function is to call the _approve function, the input is the public approval function of the approve function, and the output is to return a boolean value true to indicate the operation is successful.

  • transfer function: The function is to call the _transfer function, the input is to accept the address to and transfer number value, and the output is to return the Boolean value true means the operation is successful

  • transferFrom function: an exposed authorization transfer function.The input is the transfer address from, the receiving address to and the transfer quantity value, the output is Return Boolean value true means the operation is successful

  • permit function: Use signature to approve token transfer, verify the signature and call the _approve function. The input is the owner address owner, the approval address spender, the approval quantity value, the deadline, the signature parameters v, r, s, and the output is none.

The official source code analysis is as follows:

4.2.3 UniswapV2 Pair.sol

UniswapV2Pair is a trading pair contract, which realizes the core function of Uniswap v2, namely, manages and operates the liquidity pool of each trading pair.This contract is responsible for handling the exchange of tokens, the addition and removal of liquidity, and the accumulation calculation of prices.It ensures that after each transaction, the reserve and price information of the transaction pair are updated and triggers corresponding event notifications.There are 11 functions in UniswapV2Pair.sol, which are shown in the following table:

The official UniswapV2Pair.sol code and comments are as follows:

UniswapV2Pair inherits IUniswapV2Pair, UniswapV2ERC20. First, take a look at the source code of IUniswapV2Pair and see how IUniswapV2Pair defines interfaces:

Global variables and modifiers are then defined

The above MINIMUM_LIQUIDITY is a constant that sets the minimum number of liquidity tokens that must be retained in the liquidity pool to ensure that liquidity providers retain at least a certain amount of tokens at any time, thus avoiding liquidity exhaustion.It is to the power of 10, which is burned out when providing initial liquidity; SELECTOR stores the ABI (application binary interface) selector of the token transfer function, which is used to accurately identify and call other things in a smart contractThe transfer function of the contract ensures that the correct function signature is used when executing token transfers;

factory is used to store the address of the Uniswap V2 factory contract for the transaction pair contract, Token0, Token1 is used to store the token address, reserve0, reserve1 and blockTimestampLast, the three state variables record the number of two assets in the latest constant product and the transaction timeblock (creation) time;

The price0CumulativeLast and price1CumulativeLast variables are used to record the accumulated values ​​of the two prices in the trading pair, and kLast is used to track the recent state of the product of the two token reserves in the UniswapV2 trading pair, as a key parameter to maintain the price stability of the liquidity pooland calculate transaction fees, mainly used for the calculation of team handling fees.

The following section of the decorator provides a lock mechanism to prevent reentry attacks. The specific code is parsed as follows:

The _; in the above code represents the modified function body. The general logic of this code is: a lock modifier is defined, which ensures that the contract will not be re-issued during the execution of the modified function by changing the state of the unlocked variable.Enter, thus preventing reentering attacks and race conditions.

The function below is to provide a way to publicly query and return information on the current two token reserves and the last updated timestamp of the UniswapV2 transaction pair contract.

The function of the _safeTransfer function is to perform token transfer operations within the smart contract, and check whether the transfer is successful. If it fails, an exception will be thrown, ensuring the security of the contract and the reliability of token transfer. The following is the detailed code of this section.Notes:

The following constructor is simply used to initialize the factory:

The function of the initialize function is to set the addresses of the two tokens involved in the transaction pair contract, and can only be called by the factory contract where the transaction pair is deployed, ensuring that the initialization process of the transaction pair is safe and controlled.

The main function of the _update function is to ensure that the reserve amount and price accumulator of the transaction-to-contract can reflect the latest state. The specific implementation method is to compare the timestamp of the current block with the timestamp of the last updated one.The four input parameters of the _update function are: balance0 and balance1, representing the current balance of the two tokens in the transaction pair; _reserve0 and _reserve1, representing the reserves of the first two tokens by the function call.Next, we will use the bullet point method to explain how the _update function is implemented:

  1. Check whether the balance value may cause overflow: Use the require statement to ensure that the incoming balance0 and balance1 do not exceed the maximum value of uint112, because reserve0 and reserve1 use the uint112 type when storing, and the security of data type conversion is required.

  2. Record the current block time: Get the timestamp of the current block, and perform modulo 2^32 operation with blockTimestampLast to obtain blockTimestamp.This operation is because the block timestamp of Ethereum is 32 bits, and we only care about the time difference within a block, not the absolute time.

  3. Calculate the time difference: Calculate the difference between the current block time and the last update time time.If timeElapsed is 0, it means that this is a continuous call within the same block, so the accumulated price value will not be updated.

  4. Price cumulative update: If the time difference is greater than 0 and the reserve is not 0, use the fixed-point math library UQ112x112 to calculate the price ratio and update price0CumulativeLast and price1CumulativeLast.”never overflows” here means that because the time interval timeElapsed is of type uint32, multiplying with the accumulated price value (uint224) will not cause overflow.”+ overflow is desired” refers to the accumulated value of the price to allow overflow, because the price calculation uses the delta instead of the absolute value. Even if there is an overflow, the delta used to calculate the average price is still accurate.

  5. Update reserve: Assign the new balance to reserve0 and reserve1 to update the reserve of the liquidity pool.

  6. Update timestamp: Assign the current block timestamp to blockTimestampLast to prepare for the next update.

  7. Trigger synchronization event: issue a Sync event through the emit keyword to inform the external listener that the reserve has been updated.

This design allows UniswapV2 to maintain price continuity and accuracy when processing large amounts of transactions, and can accurately calculate the average transaction price through the change volume even when the block timestamp or the accumulated value of the price may overflow.This is achieved by cleverly utilizing fixed point math and time difference.

In UniswapV2, users will be charged a 0.3% handling fee per transaction.One-sixth of this fee will be allocated to the development team, and the remaining five-sixth will be given to the liquidity provider as a reward.However, if the handling fee is calculated once per transaction, this will inevitably increase the user’s Gas fee.

Therefore, in UniswapV2, the handling fee will be accumulated and the handling fee will be allocated only when liquidity changes.The _mintFee function first checks whether the transaction fee is enabled and determines the fee receiving address.If the transaction fee is not enabled and there has been a minting fee (_kLast is not 0), reset the kLast value.This fee casting mechanism is part of UniswapV2 to provide additional incentives for liquidity providers; if transaction fees are turned on, the value of the fee is calculated based on the following formula.

Sₘ represents the number of liquid tokens that should be minted, k₁ represents the product of reserves after the previous liquidity event, k₂ represents the current reserve product k, S₁ represents the total liquid token supply after the previous liquidity eventquantity.

4.3 Periphery contract

The main function of UniswapV2 peripheral contracts is to serve as a bridge between external accounts and core contracts, including four parts: interface definition, tool library, Router and sample implementation.

4.3.1 Libraries

The Libraries folder contains four files

  • SafeMath.sol

  • UniswapV2Library.sol

  • UniswapV2LiquidityMathLibrary.sol

  • UniswapV2OracleLibrary.sol

Next, we will analyze these four sol files in detail

SafeMath.sol

SafeMath.sol is used to perform overflow-safe mathematical operations, which are very important to avoid integer overflow and underflow errors, especially in blockchain and smart contract development.It mainly contains three functions.

  • add function: used to safely perform addition operations of two unsigned integers

  • sub function: used to safely perform subtraction operations of two unsigned integers

  • mul function: used to safely perform multiplication operations of two unsigned integers

The specific code comments are as follows

UniswapV2Library.sol

UniswapV2Library.sol provides some practical functions for interacting and operating with Uniswap v2 exchange pairs.

These functions are mainly used to calculate transaction paths, obtain reserves, calculate prices and perform chain calculations.This library uses a library called SafeMath to ensure the security of mathematical operations and avoid integer overflow and underflow.UniswapV2Library.sol This file contains eight functions:

  • sortTokens function: Returns two token addresses sorted by address.The input is two token addresses tokenA and tokenB.The output is the sorted token address token0 and token1.

  • pairFor function: calculates the address of the given factory address and the pair of two token addresses without making external calls.The input is the factory address factory, the two token addresses tokenA and tokenB; the output is the address of the pair pair

  • getReserves function: Get and sort a pair of reserves.The input is the factory address factory, the two token addresses tokenA and tokenB; the output is the reserve of the two tokens reserveA and reserve B

  • quote function: Returns the equivalent quantity of another asset based on the given asset quantity and the reserve of the pair.Enter: Assets quantity A , ReserveA and ReserveB .The output is the amountB of another asset.

  • getAmountOut function: Returns the maximum output number of another asset based on the number of input assets and the reserves of the pair.The input is the number of input assets amountIn, reserveIn and reserveOut; the output is the number of output assets amountOut.

  • getAmountIn function: Returns the number of another asset to be entered based on the number of output assets and the reserves of the pair.The input is the output asset quantity amountOut, reserveIn and reserveOut.

  • getAmountsOut function: perform chain getAmountOut calculation on any number of pairs, the input is the factory address factory , the number of assets amountIn , the path path; the output is the array of assets for each path node.

  • getAmountsIn function: perform chain getAmountIn calculation on any number of pairs, the input is the factory address factory, the output number of assets amountOut, the path path; the output is the array of assets for each path node number of the number of assets.

The detailed comments on UniswapV2Libray source code are as follows:

UniswapV2OracleLibrary.sol

The UniswapV2OracleLibrary.sol file provides some auxiliary methods for operations related to oracle calculating average prices.The library includes methods to get the current block timestamp and calculate the cumulative price, helping to save gas costs and avoid frequent synchronous calls.It contains two functions, as follows:

  • currentBlockTimestamp function: Returns the current block timestamp.Input None, the output is the current block timestamp, type uint32 .

  • CurrentCumulativePrices function: calculates and returns the accumulated price.The input is the transaction pair address pair, and the output is the cumulative price price0Cumulative and price1Cumulative and the current block timestamp blockTimestamp.

The official source code of UniswapV2OracleLibrary.sol is as follows:

UniswapV2LiquidityMathLibrary.sol

The official source code of UniswapV2LiquidityMathLibrary.sol is as follows

  • computeProfitMaximizingTrade function: calculates the transaction direction and size that maximizes profits.

  • getReservesAfterArbitrage function: Under the observed “real price”, the reserves of the liquidity pool are obtained after arbitrage transactions.

  • computeLiquidityValue function: calculates liquidity value, all parameters of a given liquidity pool.

  • getLiquidityValue function: Gets all current parameters and calculates the value of the liquidity amount.

  • getLiquidityValueAfterArbitrageToPrice function: Given two tokens and their “real price”, as well as the liquidity amount, return the value of the liquidity.

UniswapV2Router02.sol

Let’s first look at the source code of UniswapV2Router02. The code in this part can be roughly divided into six major parts, constructors and modifiers, functions that accept ETH, add liquidity, remove liquidity, token exchange, and library functions.

Constructors and modifiers

  • constructor(address _factory, address _WETH): Initialize the factory contract address and the WETH contract address.

  • modifier ensure(uint deadline): Ensure that the transaction is completed before the deadline.

Functions that receive ETH

  • receive() external payable: receives ETH, only calls from WETH contracts are allowed.

Add liquidity

  • _addLiquidity: An internal function, used to add liquidity, calculate the optimal number of tokens based on the existing reserves.

  • addLiquidity: Add liquidity of two ERC-20 tokens.

  • addLiquidityETH: Add liquidity of ERC-20 tokens and ETH.

Remove liquidity

  • removeLiquidity: Removes liquidity of two ERC-20 tokens.

  • removeLiquidityETH: Removes liquidity of ERC-20 tokens and ETH.

  • removeLiquidityWithPermit: Removes liquidity of two ERC-20 tokens with license.

  • removeLiquidityETHWithPermit: Removed ERC-20 tokens and ETH liquidity with license.

  • removeLiquidityETHSupportingFeeOnTransferTokens: Removes liquidity of ERC-20 tokens and ETH, and supports fee tokens.

  • removeLiquidityETHWithPermitSupportingFeeOnTransferTokens: Removes liquidity of ERC-20 tokens and ETH with license, supports fee tokens.

Token exchange

  • _swap: Internal function, executes token exchange logic.

  • swapExactTokensForTokens: Use the exact number of tokens to exchange for another token.

  • swapTokensForExactTokens: Use tokens to exchange an exact number of another tokens.

  • swapExactETHForTokens: Use the exact number of ETH to exchange tokens.

  • swapTokensForExactETH: Use tokens to exchange the exact amount of ETH.

  • swapExactTokensForETH: Exchange ETH with the exact number of tokens.

  • swapETHForExactTokens: Use ETH to exchange the exact number of tokens.

  • swapExactTokensForTokensSupportingFeeOnTransferTokens: Use the exact number of tokens to exchange for another token, supporting fee tokens.

  • swapExactETHForTokensSupportingFeeOnTransferTokens: Use the exact number of ETH exchange tokens, supporting fee tokens.

  • swapExactTokensForETHSupportingFeeOnTransferTokens: Use the exact number of tokens to exchange ETH, supporting fee tokens.

Library functions

  • quote: Calculate the number of token B corresponding to a given quantity of token A based on the reserve amount.

  • getAmountOut: Calculates the number of outputs that can be obtained given the number of inputs and reserves.

  • getAmountIn: Calculates the number of inputs required for a given output quantity and reserve quantity.

  • getAmountsOut: Calculate the output number based on the path and the input number.

  • getAmountsIn: Calculate the number of inputs based on the path and the number of outputs.

Let’s look at the functions one by one below. The contract of UniswapV2Router02.sol is inherited from IUniswapV2Router02 through the following code.

The UniswapV2Router02 contract implements the IUniswapV2Router02 interface, providing the following key functions:

  1. Add liquidity: Allows users to add two tokens to the liquidity pool in exchange for liquidity provider tokens.

  2. Remove liquidity: Allow liquidity providers to exchange their liquidity provider tokens back to both tokens.

  3. Transactions: Allows users to trade between different tokens, supports direct transactions and paths through multiple trading pairs.

  4. Quotation calculation: Provides a series of functions to calculate transaction details when a given input or output volume.

The ensure modifier is used to check whether the current block time exceeds the latest transaction time, ensuring that the transaction specified by the user does not fail due to timeout, which helps improve the security and reliability of the transaction.

The constructor initializes the project contract address and the weth contract address, both of which are unchanged throughout the life cycle of the contract.

The receive function is usually used to allow a contract to receive ether directly, rather than via a function call.In this particular example, the receive function uses the assert statement to ensure that only the WETH contract can send ether to it.

The purpose of the _addLiquidity function is to calculate the optimal number of two tokens that the user needs to deposit when adding liquidity.There are six parameters for this function to accept. tokenA and tokenB are the addresses of the two tokens to add liquidity. amountADesired and amountBDesired are the initial number of the two tokens that the user wants to add. amountAMin and amountBMin are the minimum acceptable for the user toAdd a quantity to prevent the slippage from being too low. The function returns two parameters: amountA and amountB, that is, the number of two tokens actually added.

This calculation takes into account the amount of token reserves already in the liquidity pool to ensure that the liquidity added by the user is balanced.If the liquidity pool is new and there is no existing reserve, users can directly add the amount they want.If there is already reserve in the pool, the function will calculate the optimal number of additions based on the current proportion, ensuring that the proportion of tokens in the pool remains unchanged after liquidity is added.

addLiquidity’s public function to add liquidity to the liquidity pool of the specified two tokens (tokenA and tokenB)

The addLiquidityETH function allows users to add non-Ether tokens and Ether to the UniswapV2 liquidity pool in exchange for the corresponding liquidity token. This function has six accepted parameters. Token is the non-Ether token to add liquidity.Address, amountTokenDesired is the number of non-Ether tokens that the user wants to add, amountTokenMin is the minimum number of non-Ether tokens that the user can accept, used to prevent the slippage from being too low, amountETHMin is the minimum number of Ether tokens, and is also used toTo prevent slippage from being too low, to is the address of receiving newly minted liquidity tokens, deadline is the deadline for the transaction, used to prevent transaction timeouts.

The function returns three parameters: amountToken and amountETH are the amount of non-Ether and Ether coins that are actually added to the liquidity pool, and liquidity is the number of newly minted liquidity tokens.

The removeLiquidity function allows users to remove the liquidity they previously added from the liquidity pool and obtain two tokens proportionally.The burn function is an underlying function in the IUniswapV2Pair liquidity pool contract, used to perform actual liquidity destruction and token allocation operations.

The removeLiquidityETH function allows users to remove the liquidity they previously added with Ether from the UniswapV2 liquidity pool and obtain corresponding non-Ether and Ether respectively.The function first calls removeLiquidity , then extracts the corresponding token through safeTransfer , then replaces the WETH extracted from the combustion liquidity with ETH , and then transfers the redeemed ETH to the recipient.

The removeLiquidityWithPermit function enables users to authorize via ECDSA signature, allowing contracts to remove liquidity on behalf of users without the user’s previous authorization via approve function.

The removeLiquidityETHWithPermit function combines the removeLiquidityETH and permit authorization mechanisms, allowing users to remove liquidity by signing a one-time authorization contract without the need to use the standard approve mode.This provides better user experience and security while also reducing the gas costs of transactions.

The removeLiquidityETHSupportingFeeOnTransferTokens function allows users to remove the liquidity they previously added with specific tokens and ether from the UniswapV2 liquidity pool, taking into account that some tokens may charge fees when transferring.

The function first calls removeLiquidity to perform the liquidity removal, then handles the token transfer, ensuring that the user gets the number of tokens they deserve, and finally converts the WETH back to Ether and transfers to the user.The entire process needs to be completed before the transaction deadline specified by the user.

The removeLiquidityETHWithPermitSupportingFeeOnTransferTokens function combines the removeLiquidityETHSupportingFeeOnTransferTokens and permit authorization mechanisms, allowing users to remove liquidity by signing a one-time authorization contract while processing tokens that may charge fees during transfers.

This approach provides a way to authorize contracts to operate user assets without using the approve mode, reducing gas costs and improving user experience.

The _swap function is an internal function that performs a series of token exchange operations.It exchanges from one token to another according to the specified path and quantity until the final token is reached.This function is the core part of the liquidity pool interaction, used to implement token conversion and liquidity pool updates.

The swapExactTokensForTokens function is a key feature of UniswapV2 Router that allows users to exchange at least one minimum output token with an exact input amount.This function first calculates the output of the entire exchange path, then ensures that the final output meets the user’s minimum requirements, then safely transfers tokens from the user to the liquidity pool and performs the exchange operation.

The swapTokensForExactTokens function allows users to specify the number of tokens they wish to obtain and provides tokens that do not exceed the maximum value for exchange.This function first calculates the maximum input required to obtain amountOut, and then ensures that this input does not exceed the amountInMax specified by the user.

The swapExactETHForTokens function allows the user to exchange at least a certain number of another tokens with an exact amount of ETH.This function first verifies whether the exchange path is valid, then calculates the required number of WETH, saves the ETH into the WETH contract, then performs the exchange operation, and sends the exchanged tokens to the address specified by the user.The entire process needs to be completed before the transaction deadline specified by the user.

The function of the swapExactETHForTokens function is to sell a set number of ETH in exchange for other Tokens.First, perform effective path checks to ensure that the first element of the path array is the WETH address, because the Uniswap transaction pair is an ERC20/ERC20 transaction pair;

Next is to calculate the output amount, use the UniswapV2Library.getAmountsOut function to calculate the number of each token that the user can obtain based on the number of ETH sent by the user msg.value and token path path path, and store the result in the amounts array;Next is to do the minimum output verification, and the function checks whether the last element in the amounts array (i.e. the number of target tokens) meets the minimum output quantity set by the user amountOutMin.

If it is not satisfied, an error will be thrown; next is to call the deposit function of the WETH token contract, deposit the ETH of msg.value into the WETH contract, and assert that the WETH contract transfers the number[0] number to the specified transaction pair contract addressWETH, if it fails, the transaction will roll back, and finally the internal function _swap is called to execute the actual token exchange process.

swapTokensForExactETH is a process of selling other tokens in exchange for a certain amount of ETH. The function receives five parameters, including the expected amount of ETH amountOut , the maximum number of tokens the user is willing to provide amountInMax , the token exchange path path path , the address to which the ETH is received , anddeadline for transactions;

First, the path validity check is checked to ensure that the last element of the exchange path is the WETH address, and if not, an exception is thrown; next is the input amount calculation, and use the UniswapV2Library.getAmountsIn function to calculate the generation that the user needs to provide in order to obtain the amountOut number of ETHCoins number; ensure that the calculated number of tokens to be provided does not exceed the maximum number set by the user, and if not, an exception will be thrown; use the safeTransferFrom function of TransferHelper to safely transfer tokens from the msg.sender address;

Next, call the internal function_swap to execute the actual token exchange process; call the withdraw function of the WETH contract to convert the exchanged WETH back to ETH; finally use the safeTransferETH function of TransferHelper to safely transfer the obtained ETH to the address specified by the user..

The function of the swapExactTokensForETH function is used to exchange at least amountOutMin number of ETH with a fixed number of tokens. The function receives five parameters, including the number of tokens that the user is willing to provide, amountIn, the minimum number of ETH expected to obtain amountOutMin, the token exchange path path,The address to which the ETH is received, and the deadline of the transaction is deadline; the implementation logic of the function is very similar to the logic of swapTokensForExactETH.

The function of swapETHForExactTokens allows users to exchange a fixed number of tokens using ETH, and the implementation logic is similar to the above swapExactTokensForETH, swapTokensForExactETH.

The _swapSupportingFeeOnTransferTokens function supports the logic of token exchange for transaction fees. By traversing the token path, the number of tokens to be exchanged in each liquidity pool is calculated and the exchange operation is performed.

swapExactTokensForTokensSupportingFeeOnTransferTokens Fixed number of tokens is precisely exchanged for at least the expected number of another token, while handling the case of transaction fee tokens.

swapExactETHForTokensSupportingFeeOnTransferTokens implements the use of a specific number of ETH to exchange for other Tokens

The swapExactTokensForETHSupportingFeeOnTransferTokens function enables the user to accurately exchange at least the expected amount of ETH with a fixed number of tokens, while handling the transaction fee tokens, and ensuring accurate transfer of ETH.

The quote function implements the function of calculating and returning the number of another tokens that the user can exchange based on the number of tokens specified by the user and the reserve of two tokens in the liquidity pool.

The calculation principles of the getAmountOut function and the getAmountsOut function are both constant product algorithms.Define the function getAmountOut to calculate the output amount that the user can get after a given input amount and reserve amount; define the function getAmountsOut to calculate the series of output amounts that the user can get after a given input amount and exchange path.

The calculation principle of getAmountIn and getAmountsIn is to calculate the number of assets sold based on the constant product algorithm.

4.3.2 UniswapV2 Migrator.sol

UniswapV2Migrator.sol is used to migrate liquidity from Uniswap v1 to Uniswap v2.This contract includes the ability to receive ETH and uses Uniswap v1 and v2 routers and exchange contracts for migration.It ensures that users can safely transfer their liquidity in Uniswap v1 to Uniswap v2.There are three functions, and the specific functions of each function are as follows:

  • constructor function: constructor function, used to initialize UniswapV2Migrator contract.The input is the v1 factory address _factoryV1 and v2 router address _router; the output is none

  • receive function: receives ETH, allowing the contract to receive ETH from any v1 switch and router.Input None, output None.

  • migrate function: The input is token address token, minimum token number amountTokenMin, minimum ETH quantity amountETHMin, reception address to, deadline; the output is none

The official source code of UniswapV2Migrator is analyzed in detail as follows:

4.3.3 Interfaces folder

The interfaces folder contains interface definitions for interacting with UniswapV1 and V2 exchanges, routers, factories, and WETH and ERC20 contracts.Key features include managing and migrating liquidity, handling token trading, adding and removing liquidity, and packaging and unpacking ETH.These interfaces ensure standardized interactions between contracts.Let’s look at the contents of each file in detail.

IUniswapV1Exchange.sol

This file defines an interface IUniswapV1Exchange, which is used to interact with UniswapV1 exchange contracts.The exchange contract is mainly responsible for handling transactions and liquidity management between tokens and ETH.It contains the following four functions:

  • balanceOf function: Returns the balance of an address in the exchange contract.

  • transferFrom function: transfers tokens from one address to another.

  • tokenToEthSwapInput function: used to exchange tokens into Ether.

  • ethToTokenSwapInput function: exchange Ether into tokens.

IUniswapV1Factory.sol

IUniswapV1Factory.sol It is used to interact with the factory contracts of UniswapV1.The specific code analysis is as follows:

IERC20.sol

IERC20.sol defines an interface called IERC20, which follows the Ethereum Token Standard (ERC-20)

IUniswapV2migrator.sol

IUniswapV2migrator.sol is used to interact with the migration contract of Uniswap V2. The detailed code comments are as follows:

IUniswapV2Router01.sol

The IUniswapV2Router01 interface provides a variety of functions, including liquidity addition and removal, token exchange, etc. These functions are the core of building a decentralized trading platform and liquidity pool.Through this interface, users can easily interact with the UniswapV2 protocol.

IUniswapV2Router02.sol

The method in the IUniswapV2Router02.sol interface mainly adds support for transfer fees, which allows liquidity providers or token holders to receive a portion of the fees in return when transferring tokens.The implementation of these methods usually calls the corresponding token contract at the bottom to realize the fee charged during transfer.

IWETH.sol

IWETH.sol defines an interface called IWETH, which represents a standard interface on Ethereum for interacting with Wrapped Ether (WETH) contracts.

  • Related Posts

    Wintermute Ventures: Why do we invest in Euler?

    On April 18, 2025, market maker Wintermute announced that its investment institution Wintermute Ventures has invested in the DeFi lending agreement Euler Finance. Wintermute Ventures published the same dayEuler’s Thesis…

    Glassnode: Are we experiencing a bull-bear transition?

    Source: Glassnode; Compilation: Baishui, bitchain vision summary The macroeconomic environment remains uncertain and global trade relations are being reorganized.This uncertainty has led to increased volatility in the U.S. Treasury market…

    Leave a Reply

    Your email address will not be published. Required fields are marked *

    You Missed

    Historic Trend: Bitcoin is Being a Safe-Habiting Asset

    • By jakiro
    • April 19, 2025
    • 8 views
    Historic Trend: Bitcoin is Being a Safe-Habiting Asset

    What makes cryptocurrency rug pull events happen frequently?

    • By jakiro
    • April 18, 2025
    • 12 views
    What makes cryptocurrency rug pull events happen frequently?

    Wintermute Ventures: Why do we invest in Euler?

    • By jakiro
    • April 18, 2025
    • 12 views
    Wintermute Ventures: Why do we invest in Euler?

    Can Trump fire Powell?What economic risks will it bring?

    • By jakiro
    • April 18, 2025
    • 10 views
    Can Trump fire Powell?What economic risks will it bring?

    Glassnode: Are we experiencing a bull-bear transition?

    • By jakiro
    • April 18, 2025
    • 11 views
    Glassnode: Are we experiencing a bull-bear transition?

    The Post Web Accelerator’s first batch of 8 selected projects

    • By jakiro
    • April 17, 2025
    • 24 views
    The Post Web Accelerator’s first batch of 8 selected projects
    Home
    News
    School
    Search