Skip to content

Commit 00e0e91

Browse files
committed
Merge remote-tracking branch 'pfdocs/main' into protocol-fee
2 parents 1e5d6ae + 88a68e1 commit 00e0e91

33 files changed

+2238
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"label": "Protocol Fee",
3+
"position": 2.1,
4+
"collapsed": true
5+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
title: Deployments
3+
sidebar_position: 1.1
4+
---
5+
6+
# Deployment Addresses
7+
8+
## Ethereum Mainnet
9+
10+
| Contract | Address |
11+
|-----------------|-----------------------------------------------------------------------------------------------------------------------|
12+
| AssetSink | [0x0](https://etherscan.io/address/0x0) |
13+
| Firepit | 0x1 |
14+
| V3FeeController | 0x2 |
15+
| UNI | [0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984](https://etherscan.io/address/0x1f9840a85d5af5bf1d1762f925bdaddc4201f984) |
16+
17+
### Deployment Process
18+
19+
To enable fees for v2 and v3 on *mainnet*, the [Deployer](/technical-reference/Deployer) handles the entire set up in one transaction. Subsequent transactions
20+
will be required to set the initial fee values.
21+
22+
The `Deployer` contract performs the following steps, in its constructor:
23+
24+
1. Deploys the `AssetSink` contract
25+
2. Deploys the `Firepit` contract, with the initial UNI-threshold requirement as a parameter
26+
3. Set the `Firepit` as the releaser on the `AssetSink`
27+
4. Transfers ownership of the `AssetSink` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc)
28+
5. Sets the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc) as the initial `thresholdSetter`,
29+
giving the Governance Timelock the ability to update the UNI-threshold requirement
30+
6. Transfers ownership of the `Firepit` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc).
31+
Uniswap Governance can appoint a different `thresholdSetter` at a later time.
32+
7. Deploys the `V3FeeController` contract, with the `AssetSink` as the destination for collected fees
33+
8. Sets the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc) as the initial `feeSetter`, giving the Governance Timelock
34+
the ability to set fee values at a later date
35+
9. Transfers ownership of the `V3FeeController` to the [Uniswap Governance Timelock’s address](https://etherscan.io/address/0x1a9c8182c09f50c8318d769245bea52c32be35bc),
36+
giving the Governance Timelock the ability to appoint a different `feeSetter` at a later time
37+
38+
Enabling fee values themselves will require a Uniswap Governance vote, which will involve:
39+
- calling [`setFeeTo`](https://github.com/Uniswap/v2-core/blob/ee547b17853e71ed4e0101ccfd52e70d5acded58/contracts/UniswapV2Factory.sol#L40) on the mainnet [UniswapV2Factory](https://etherscan.io/address/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f) and passing the `AssetSink` address as the parameter
40+
- calling [`setMerkleRoot`](https://github.com/Uniswap/protocol-fees/blob/6f39580d36a75a1ed1f34daf7d3262ced4b9df3f/src/feeControllers/V3FeeController.sol#L93C12-L93C25) on the V3FeeController contract and passing a properly constructed Merkle root as a parameter
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
title: Fee Configuration
3+
sidebar_position: 3
4+
---
5+
# **Protocol Fee Configuration**
6+
7+
For v2, fees are hardcoded and can only be enabled or disabled across all pools at once. For v3, we propose turning on protocol fees first for token pairs where the Uniswap Protocol currently accounts for over 90% of total DEX volume.
8+
9+
A full list of v3 pools included in the initial rollout is available [here](https://github.com/Uniswap/protocol-fees/blob/main/merkle-generator/data/merkle-tree.json).
10+
11+
| Version | Fee Tier | LP Fee | Protocol Fee |
12+
| ----- | ----- | ----- | ----- |
13+
| v2 | All pools | 0.25% | 0.05% |
14+
| v3 | 0.01% | 0.0075% | 0.0025% |
15+
| v3 | 0.05% | 0.0375% | 0.0125% |
16+
| v3 | 0.30% | 0.25% | 0.05% |
17+
| v3 | 1.00% | 0.8334% | 0.1666% |
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"label": "Guides",
3+
"position": 8,
4+
"collapsed": true
5+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Best Practices
2+
3+
Below, please find a list of best practices when interacting with the Uniswap Protocol Fee system.
4+
5+
## Calldata Nonce
6+
7+
Recall [`Firepit.release()`](../technical-reference/IReleaser#release) requires a `_nonce` parameter that matches the contract storage [`Firepit.nonce()`](../technical-reference/INonce#nonce).
8+
9+
The nonce mechanism prevents front-running attacks, where a successful front-run would cause the victim
10+
to burn a substantial amount of UNI in exchange for little to no assets. The nonce mechanism ensures sequencing, so **if two competing transactions
11+
use the same nonce, only one will succeed**
12+
13+
:::danger
14+
Integrating *contracts* should **NEVER** read the `nonce` at the time of the transaction
15+
16+
```solidity
17+
contract Vulnerable {
18+
IFirepit public firepit;
19+
20+
function vulnerable(Currency[] memory assets) external {
21+
// ❌ THIS IS VULNERABLE TO FRONT RUNNING ❌ //
22+
uint256 nonce = firepit.nonce();
23+
firepit.release(nonce, assets, address(this));
24+
}
25+
}
26+
```
27+
:::
28+
29+
:::tip
30+
```solidity
31+
contract SafeRelease {
32+
IFirepit public firepit;
33+
34+
// ✅ THIS IS SAFE FROM FRONT RUNNING ✅ //
35+
function safeRelease(uint256 _nonce, Currency[] memory assets) external {
36+
firepit.release(_nonce, assets, address(this));
37+
}
38+
}
39+
```
40+
In the safe pattern, the caller is responsible for reading the `Firepit.nonce()` offchain, assocating it with releasable assets, and passing it as a **calldata** parameter
41+
:::
42+
43+
## Collect Uniswap v3 Fees
44+
45+
For further reading, please see [Read Asset Balance](./read-asset-balance)
46+
47+
In the Uniswap Protocol Fee system, Uniswap v3 fees *must be collected* to the `AssetSink` before they are eligible for release.
48+
49+
:::tip
50+
We recommend that integrators check and collect Uniswap v3 fees before calling `Firepit.release()`, to ensure the maximum assets are released.
51+
52+
Collection is available via
53+
[IV3FeeController.collect()](../technical-reference/IV3FeeController#collect)
54+
55+
```solidity
56+
IV3FeeController v3FeeController;
57+
58+
function collectAndRelease(
59+
IV3FeeController.CollectParams memory collectParams,
60+
uint256 _nonce,
61+
Currency[] memory assets,
62+
address recipient
63+
) external {
64+
v3FeeController.collect(collectParams);
65+
firepit.release(_nonce, assets, recipient);
66+
}
67+
```
68+
:::
69+
70+
## UNI Approvals
71+
72+
The Uniswap Protocol Fee system allows for an *updatable* [`Firepit.threshold()`](../technical-reference/IResourceManager#threshold)
73+
74+
While the system does not intend to maliciously front-run `.release()` calls, max-approving UNI allowances may lead to an unexpectedly higher burn of UNI.
75+
The risk only appears if and only if the `thresholdSetter` increases the `threshold` amount while there is a pending `.release()` call.
76+
77+
Integrators can avoid this risk by:
78+
79+
* only holding an amount of UNI they are willing to burn
80+
* approving only the amount of UNI they intend to burn
81+
* perform balance checks before and after calling `.release()`, comparing the burned amount against a calldata value
82+
83+
## Payable Contracts
84+
85+
Because the Uniswap Protocol Fee system can release native tokens (Ether), recipients of the tokens should be `payable`
86+
87+
## Pricing Uniswap v2 ERC-20 Tokens
88+
89+
For further reading, please see [Read Asset Balance](./read-asset-balance#uniswap-v2-pool-protocol-fees)
90+
91+
Uniswap v2 Protocol Fees are automatically pushed to the `AssetSink` contract, but are represented as ERC-20 LP tokens. These ERC-20 LP tokens represent a combination of `token0` and `token1`
92+
93+
To compute the underlying amount of `token0` and `token1` represented by the LP tokens, integrators can use the following formula:
94+
95+
```solidity
96+
IUniswapV2Pair pair;
97+
98+
function getUnderlyingAmounts(uint256 lpTokens) external view returns (uint256 amount0, uint256 amount1) {
99+
uint256 totalSupply = pair.totalSupply();
100+
uint256 poolBalance0 = IERC20(pair.token0()).balanceOf(address(pair));
101+
uint256 poolBalance1 = IERC20(pair.token1()).balanceOf(address(pair));
102+
103+
amount0 = (lpTokens * poolBalance0) / totalSupply;
104+
amount1 = (lpTokens * poolBalance1) / totalSupply;
105+
}
106+
```
107+
108+
:::note
109+
Integrators should perform additional validation, i.e. pool price and slippage checks as to not
110+
misrepresent the LP token's underlying token amounts
111+
:::
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
---
2+
title: Get Started
3+
sidebar_position: 1
4+
---
5+
6+
import Tabs from '@theme/Tabs';
7+
import TabItem from '@theme/TabItem';
8+
9+
# Get Started
10+
11+
In the current version of the Uniswap Protocol Fee system, searchers can *permissionlessly* capture value from the system
12+
by receiving assets valued greater than the UNI tokens provided to the system.
13+
14+
The simplest way to interact with the system is calling [`Firepit.release()`](../technical-reference/IReleaser#release) from a wallet
15+
16+
:::note
17+
It is possible to interact with the `Firepit` contract via a custom smart contract to enable:
18+
* slippage / balance checks
19+
* Uniswap v3 Fee [collection](./best-practices.mdx#collect-uniswap-v3-fees)
20+
* Uniswap v2 LP Token [redemptions](./read-asset-balance.mdx#uniswap-v2-pool-protocol-fees)
21+
* UNI token flash loans
22+
:::
23+
24+
## Acquire a sufficient amount of UNI
25+
The `Firepit` contract requires integrators to hold a minimum amount of UNI to call `release()`.
26+
Participants can view the `threshold` by calling [`Firepit.threshold()`](../technical-reference/IResourceManager#threshold)
27+
28+
<Tabs>
29+
<TabItem value="solidity" label="solidity">
30+
```solidity
31+
uint256 threshold = IFirepit(address(0x1)).threshold();
32+
```
33+
</TabItem>
34+
<TabItem value="bash" label="cast">
35+
```bash
36+
cast call 0x1 "threshold()(uint256)" --rpc-url <RPC_URL>
37+
```
38+
</TabItem>
39+
</Tabs>
40+
41+
## Approve the Firepit to spend UNI
42+
Because the `Firepit` contract transfers UNI to `address(0xdead)`, integrating addresses **must first approve** the contract to spend their UNI
43+
44+
:::warning
45+
Integrators should assess the risks of max approving the `Firepit` contract
46+
:::
47+
48+
<Tabs>
49+
<TabItem value="solidity" label="solidity">
50+
```solidity
51+
IERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984).approve(
52+
0x1,
53+
type(uint256).max
54+
);
55+
```
56+
</TabItem>
57+
<TabItem value="bash" label="cast">
58+
```bash
59+
cast send 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984 \
60+
"approve(address,uint256)(bool)" \
61+
--rpc-url <RPC_URL> \
62+
0x1 \
63+
115792089237316195423570985008687907853269984665640564039457584007913129639935
64+
```
65+
</TabItem>
66+
</Tabs>
67+
68+
## Read the Nonce
69+
70+
The `Firepit` contract uses a `nonce` as a safety mechanism to avoid malicious front-running. The value is provided to `release(...)` must be equal
71+
to the value in contract storage
72+
73+
<Tabs>
74+
<TabItem value="solidity" label="solidity">
75+
```solidity [solidity]
76+
uint256 nonce = IFirepit(address(0x1)).nonce();
77+
```
78+
</TabItem>
79+
<TabItem value="bash" label="cast">
80+
```bash
81+
cast call 0x1 "nonce()(uint256)" --rpc-url <RPC_URL>
82+
```
83+
</TabItem>
84+
</Tabs>
85+
86+
## Call `Firepit.release()`
87+
88+
Once the value of the assets exceeds the value of the UNI tokens, integrators should call [`Firepit.release()`](../technical-reference/IReleaser#release)
89+
90+
<Tabs>
91+
<TabItem value="solidity" label="solidity">
92+
```solidity
93+
uint256 _nonce = IFirepit(address(0x1)).nonce();
94+
95+
Currency[] memory _assets = new Currency[](3);
96+
_assets[0] = Currency.wrap(address(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)); // USDC
97+
_assets[1] = Currency.wrap(address(0xdAC17F958D2ee523a2206206994597C13D831ec7)); // USDT
98+
_assets[2] = Currency.wrap(address(0x0000000000000000000000000000000000000000)); // ETH
99+
100+
IFirepit(address(0x1)).release(_nonce, _assets, 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
101+
```
102+
</TabItem>
103+
104+
<TabItem value="bash" label="cast">
105+
```bash
106+
cast send 0x1 "release(uint256,address[],address)" --rpc-url <RPC_URL> \
107+
<CURRENT_NONCE> <ASSET_ADDRESSES> <RECEIVER_ADDRESS>
108+
109+
# example: release USDC, USDT, and ETH to vitalik.eth
110+
cast send 0x1 "release(uint256,address[],address)" --rpc-url <RPC_URL> \
111+
0 \
112+
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48,0xdAC17F958D2ee523a2206206994597C13D831ec7,0x0000000000000000000000000000000000000000 \
113+
0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
114+
```
115+
</TabItem>
116+
</Tabs>

0 commit comments

Comments
 (0)