In this tutorial we will be creating a permissionless, ERC20 memecoin and deploying it with a Uniswap v3 liquidity pool so users can buy it on the decentralized exchange.
This tutorial is for demonstration purposes, don’t speculate on memecoins.
The full source code for this is at: https://github.com/jamesbachini/Memecoin
Here’s the token I deployed in the video:
Contract: https://polygonscan.com/token/0x9485f13195791eF84dbfE6B12E15404Ad2a6cE32
Uniswap: https://app.uniswap.org/#/swap?outputCurrency=0x9485f13195791eF84dbfE6B12E15404Ad2a6cE32
Solidity Memecoin Code
Assuming you have Git and NodeJS installed you can fork this code and then get a local test suite setup with:
git clone https://github.com/jamesbachini/Memecoin.git
npm install hardhat
npm install --save-dev "hardhat@^2.12.2" "@nomicfoundation/hardhat-toolbox@^2.0.0"
npm install --save-dev dotenv
npm install --save-dev @openzeppelin/contracts
npx hardhat test
Now let’s dive into the code and see how it works:
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.18;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
interface INonfungiblePositionManager {
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function mint(MintParams calldata params) external payable returns (
uint256 tokenId,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}
contract Meme is ERC20 {
INonfungiblePositionManager posMan = INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
address constant weth = 0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889; // polygon mumbai testnet
uint supply = 1_000_000 * 10 ** decimals();
uint24 constant fee = 500;
uint160 constant sqrtPriceX96 = 79228162514264337593543950336; // ~ 1:1
int24 minTick;
int24 maxTick;
address public pool;
address token0;
address token1;
uint amount0Desired;
uint amount1Desired;
constructor() ERC20("Meme Token", "MEME") {
_mint(address(this), supply);
fixOrdering();
pool = posMan.createAndInitializePoolIfNecessary(token0, token1, fee, sqrtPriceX96);
}
function addLiquidity() public {
IERC20(address(this)).approve(address(posMan), supply);
posMan.mint(INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: fee,
tickLower: minTick,
tickUpper: maxTick,
amount0Desired: amount0Desired,
amount1Desired: amount1Desired,
amount0Min: 0,
amount1Min: 0,
recipient: address(this),
deadline: block.timestamp + 1200
}));
}
function fixOrdering() private {
if (address(this) < weth) {
token0 = address(this);
token1 = weth;
amount0Desired = supply;
amount1Desired = 0;
minTick = 0;
maxTick = 887270;
} else {
token0 = weth;
token1 = address(this);
amount0Desired = 0;
amount1Desired = supply;
minTick = -887270;
maxTick = 0;
}
}
}
We start by setting up libraries for the ERC20 token which comes from OpenZeppelin and then create a makeshift interface for the INonfungiblePositionManager from Uniswap.
The contract will be the token itself but it also includes the functionality to setup a fair launch Uniswap liquidity pool.
We mint 1m tokens to the contract itself and set a price of 1:1 with sqrtPriceX96. From the constructor argument we call fixOrdering() which is a function to appease the required order settings for the Uniswap liquidity pool. We need to set token0 to the lower hex value of the two tokens being traded. In this case wETH (wrapped Ethereum, or actually wrapped Matic in this case) and the Meme token which we created address(this) because this is the token contract.
We then setup a liquidity pool using the createAndInitializePoolIfNecessary() function and then we need to call the addLiquidity() function separately to add the entire token supply to the liquidity pool.
NB. For some reason I couldn’t call addLiquidity() to the constructor argument so it needs to be called as a separate function, maybe something to do with the pool contract being created in the same transaction…?
This means that all the tokens are on Uniswap and available for anyone to buy. The tokens start at a price of 1 token = 1 ETH/Matic depending on where you deploy it and it will go up in value from there as people buy the token.
When someone buys the token it adds the base asset to the other side of the pool which creates exit liquidity for anyone looking to sell their holdings. The liquidity provider receipt (NFT) is held by the token contract so it can never be used to remove liquidity.
This creates a permissionless digital asset which is available for anyone to purchase on Uniswap with all users being treated equally.
Instructions For Deploying Your Memecoin
You can either deploy this in Remix like in the memecoin video or you can do it with hardhat which I’ll go through here.
Once you’ve forked the code and got hardhat installed you’ll need to edit .env-sample and add in your wallet addresses and API keys (probably only need Alchemy).
The contract address for INonfungiblePositionManager should be the same across most networks where Uniswap v3 is installed but the weth address will change depending on where you deploy it. There is a list of wrapped native token addresses at the bottom of this page: https://docs.uniswap.org/contracts/v3/reference/deployments
Add the correct address for the network you wish to use (make sure it’s a testnet initially) and then enter the network detail in /hardhat.config.js, there are a couple of examples for Goerli and Mumbai already in there.
We can then deploy the token by running the following command which will give you a contract address. Note you will need some native tokens in your wallet to pay the gas fees to deploy the contract.
npx hardhat run .\scripts\deploy.js --network mumbai
If you then connect Metamask to the network you deployed the contract too (in this case the Polygon Mumbai testnet) we can purchase the token by entering the contract address.
Now all you need is a half baked narrative and a great meme ❤️