uniswap / v3-periphery Goto Github PK
View Code? Open in Web Editor NEW๐ฆ ๐ฆ ๐ฆ Peripheral smart contracts for interacting with Uniswap v3
Home Page: https://uniswap.org
License: GNU General Public License v2.0
๐ฆ ๐ฆ ๐ฆ Peripheral smart contracts for interacting with Uniswap v3
Home Page: https://uniswap.org
License: GNU General Public License v2.0
Every time we trigger modifyPosition, e.g. in increaseLiquidity
or decreaseLiquidity
, we lose up to 1 wei of fees, exclusive, of both token0/token1
By the time we compute the fees owed to a specific NFT token ID, this loss of precision can accumulate to more than 1 wei of loss to rounding, i.e. is dependent on how many times that position has been poked and the aggregate loss due to rounding for each of those pokes
Thus the NFT contract can be 'insolvent' in that the sum of all positions' computed fees owed is greater than the total number of fees the NFT contract can collect, by greater than 1 wei * number of positions
This amount of loss is acceptable, but should be documented
The Multicall.multicall
pattern is getting a lot of love lately. I first saw it used by ENS and now in Uniswap-V3-periphery. Yet, I see that your implementation includes the payable keyword. I do believe this is potentially dangerous and should be removed. People might copy that and not see the side-effect.
Example:
pragma solidity >=0.8.2;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/token/ERC20/ERC20.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/utils/Address.sol";
abstract contract Multicall {
function multicall(bytes[] calldata data) external payable returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
if (result.length < 68) revert();
assembly {
result := add(result, 0x04)
}
revert(abi.decode(result, (string)));
}
results[i] = result;
}
}
}
contract VulnerableWETH is ERC20, Multicall {
constructor() ERC20('VulnerableWETH', 'VWETH') {}
receive() external payable {
_mint(msg.sender, msg.value);
}
function deposit() external payable {
_mint(msg.sender, msg.value);
}
function withdraw(uint256 value) external {
_burn(msg.sender, value);
Address.sendValue(payable(msg.sender), value);
}
function withdrawAll() external {
uint256 value = balanceOf(msg.sender);
_burn(msg.sender, value);
Address.sendValue(payable(msg.sender), value);
}
}
Exploit:
If I batch multiple payable subcall, they will all consider the msg.value. So calling
instance.multicall(["0x","0x","0x","0x","0x"], {value : 10**18 })
will credit me 5eth, despite only having deposited one.
Thus I could drain 4eth from the contract in a single call by doing :
instance.multicall(["0x","0x","0x","0x","0x", "0x853828b6" ], {value : 10**18 })
Beyond this simple example, any contract that considers msg.value, without comparing it to address(this).balance - interbookkeppingBalance
is sensible ... this include almost all contracts with payable functions
Advice:
Add extensive warnings in the Multicall contract, or remove payability from it.
Attempting compilation of INonfungiblePositionManager with no optimization fails with:
"CompilerError: Stack too deep when compiling inline assembly: Variable headStart is 1 slot(s) too deep inside the stack.".
This is probably caused by the function positions(uint256 tokenId) returning too many variables and accessing a slot in the stack that is deeper than its 16th element.
That is a problem for code testing contracts which use the position manager - the solidity coverage plugin tests all code by compiling without optimization, and including the INonfungiblePositionManager in another contract causes the compilation to fail.
Possible solutions are refactoring the function positions(uint256 tokenId) by returning it's variables in a struct, or splitting it in two different functions.
npm version:
"@uniswap/v3-periphery": "^1.0.0-beta.21"
These functions will revert when combined with ETH methods
for gas savings
we use multicall everywhere for batch operations
mint contains duplicated logic with increaseLiquidity
however we cannot mint a token and then increase its liquidity because we do not know the minted token id until after the mint succeeds
we could reduce the duplication of logic if the token id were counterfactually computed from the sender address + mint nonce, but it would be costly and introduce mint ordering dependency
The PeripheryPayments contract allows any intermediately called untrusted contract (e.g. an ERC777 token gets transferred) to call back into the SwapRouter or NFT contract, taking up to the difference between the current balance of WETH9/WETH10 token and the minimum amount expected by the multicall user
even using the identical routing contract, best-case swaps in periphery cost ~15k more gas than in core. there's almost certainly some SSTORE in a slot that isn't being correctly initialized, let's investigate.
can burn more liquidity than a position has
apply fix from #83
DAI permit has different function signature
i'm interested in exploring pool creation and initialization as two separate functions. this would let us neatly bundle things with multicall
, and is more flexible. e.g. right now if a pool is created outside of the router but not initialized, we're stuck
support swapping or minting without actually sending the transaction from the router
this would involve creating a new eip 712 domain for the different actions
potentially we want something generic like the OZ context module
end result is the router is perfectly usable with something like infura transactions
https://infura.io/docs/transactions
Need a library that returns TWAP for base currency/quote currency over some time period as a FixedPoint128
number
Considerations
the swap router will be called much more frequently and should get the maximum number of optimizer runs
we should configure hardhat to compile it with more runs
https://hardhat.org/config/
Hi all ,
I according to the core and periphery contracts, there consist of 2 methods of getting the pool address. First is through the factory, another method is through computing the pool address.
UniswapV3Factory.sol
IUniswapV3Factory(factory).getPool(
params.token0,
params.token1,
params.fee
)
SwapRouter.sol
PoolAddress.computeAddress(
factory,
PoolAddress.getPoolKey(tokenA, tokenB, fee))
However , through my observsation that the return addresses for both are different.
I gently twited the function in swapRouter to public and return the computedAddress for debugging purpose to find out the return address from computeAddress.
I found out that the Pool address return from the Library PoolAddress,compute is not providing the same address as the function getPool in the Factory contract. Therefore , the computeAddress in PoolAddress library is not usable ?
The pool address return from the Factory contract is the actual pool address that contains the bytecode.
Maybe I am wrong. Looking forward to your reply.
Thank you.
e.g., you cannot mint
, approve
in a single transaction
considerations:
we should make sure that SelfPermit
can also handle ERC721 permits for native positions
we could either overload unwrapWETH9
/sweepToken
with extra methods to take a fee to a specified recipient
or, we could make another contract altogether
Complete first iteration of NFT svg images.
Attributes of Note: Tokens, Fee, tick range (& liquidity?)
notes of decreaseLiquidity in
INonfungiblePositionManager.sol.
/// amount0Min The minimum amount of token0 that should be received in the burn,
/// amount1Min The minimum amount of token1 that should be received in the burn,
they are only added to tokenOwed, not sent to recipient, those notes may cause misunderstanding.
/// @return amount0 The amount of token0 sent to recipient
/// @return amount1 The amount of token1 sent to recipient
ditto.
mvp:
fun ideas:
need this support in the non fungible token contract. this will save us the trouble of doing some kind of batch call from argent
right now, the current one works quite well, but requires wrapping the entire ETH balance of the contract instead of just the required value. this imposes a small cost on exact output swappers who are paying in ETH in the case when the contract's ETH balance is greater than the value (anytime they're not pushed to their max slippage tolerance).
it's possible there are improvements to be made here.
while these would be redundant with core events, they would allow separating the liquidity of a particular nft from the aggregate liquidity of the position manager in a subgraph
i guess tests are passing because all tokens are approved
this will fail because pool key will be all 0 which will not ever be a valid pool
that way, we are guaranteed that behavior is the same, and can just use the interfaces to interact
https://github.com/Uniswap/uniswap-v3-periphery/blob/main/testnet-deploys.md#kovan
The contracts on testnets have been redeployed 2 times and no documentation is to found.
some swapping integrations wish to collect a fee from the user input
we can create an example contract that enables this use case
should allow uniswap v2-compatible LPs to safely migrate their liquidity to v3
i think this eats them. need unit tests to show
https://github.com/Uniswap/uniswap-v3-periphery/blob/ede9e08bd4401160c5cc8d2b72d9be23cc269fa4/contracts/base/Multicall.sol#L14-L15
compared to the v2 router interface, which takes the desired amounts and min amounts then computes the liquidity amount to mint, this is interface is not as nice because it makes it difficult to spend the desired amount in the case of 0 slippage.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.