I'm creating a token which when sold on a liquidity pool, takes fees and burns a certain amount.
Given that I have a recipient address, how would I check whether it is a liquidity pool?
I think I may be able to use this: https://docs.uniswap.org/protocol/V2/reference/smart-contracts/pair-erc-20 however I'm not sure which function would work or if there's another way.
You can test the address against the Uniswap Pair (V2) or Uniswap Pool (V3) interface, whether it returns expected values.
One step further, you can pass these returned values back to the Uniswap Factory contract (address can be found in V2 docs and V3 docs), which tells you a pool address based on the input values. This way you can be certain that the queried address is in fact a Uniswap Pool, and not just some other contract returning values from same-named functions.
pragma solidity ^0.8;
import "https://github.com/Uniswap/v2-core/blob/master/contracts/interfaces/IUniswapV2Factory.sol";
import "https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Factory.sol";
import "https://github.com/Uniswap/v2-core/blob/master/contracts/interfaces/IUniswapV2Pair.sol";
import "https://github.com/Uniswap/v3-core/blob/main/contracts/interfaces/IUniswapV3Pool.sol";
contract MyContract {
IUniswapV2Factory constant v2Factory = IUniswapV2Factory(address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f));
IUniswapV3Factory constant v3Factory = IUniswapV3Factory(address(0x1F98431c8aD98523631AE4a59f267346ea31F984));
/**
* true on Ethereum mainnet - 0x0d4a11d5EEaaC28EC3F61d100daF4d40471f1852
* false on Ethereum mainnet - 0xdAC17F958D2ee523a2206206994597C13D831ec7
*/
function isUniswapV2Pair(address target) external view returns (bool) {
if (target.code.length == 0) {
return false;
}
IUniswapV2Pair pairContract = IUniswapV2Pair(target);
address token0;
address token1;
try pairContract.token0() returns (address _token0) {
token0 = _token0;
} catch (bytes memory) {
return false;
}
try pairContract.token1() returns (address _token1) {
token1 = _token1;
} catch (bytes memory) {
return false;
}
return target == v2Factory.getPair(token0, token1);
}
/**
* true on Ethereum mainnet - 0x4e68Ccd3E89f51C3074ca5072bbAC773960dFa36
* false on Ethereum mainnet - 0xdAC17F958D2ee523a2206206994597C13D831ec7
*/
function isUniswapV3Pool(address target) external view returns (bool) {
if (target.code.length == 0) {
return false;
}
IUniswapV3Pool poolContract = IUniswapV3Pool(target);
address token0;
address token1;
uint24 fee;
try poolContract.token0() returns (address _token0) {
token0 = _token0;
} catch (bytes memory) {
return false;
}
try poolContract.token1() returns (address _token1) {
token1 = _token1;
} catch (bytes memory) {
return false;
}
try poolContract.fee() returns (uint24 _fee) {
fee = _fee;
} catch (bytes memory) {
return false;
}
return target == v3Factory.getPool(token0, token1, fee);
}
}
I also added a condition checking if the target address is a contract: if (target.code.length == 0)
. And if it is an end user address, it performs an early return.
Note that this snippet works only on networks where Uniswap is deployed (e.g. your local fork of the mainnet, or some of the testnets). On other networks (such as the Remix VM emulator), the Uniswap contracts are unreachable, which results in revert of the call.