Skip to content

Commit

Permalink
demo-router: contract + task
Browse files Browse the repository at this point in the history
  • Loading branch information
Rolaman committed Sep 26, 2024
1 parent 863d080 commit c7fb044
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 73 deletions.
23 changes: 9 additions & 14 deletions contracts/UniswapV2Router01.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase {
function addLiquidity(
address pair,
address to
) external override {
) public override {
Nil.Token[] memory tokens = Nil.msgTokens();
if (tokens.length != 2) {
revert("Send only 2 tokens to add liquidity");
Expand All @@ -44,14 +44,15 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase {
smartCall(pair, tokens, abi.encodeWithSignature("burn(address)", to));
}

function swap(address to, address pair) public override {
function swap(address to, address pair, uint amount0Out, uint amount1Out) public override {
Nil.Token[] memory tokens = Nil.msgTokens();
if (tokens.length != 1) {
revert("UniswapV2Router: should contains only pair token");
}
// TODO
smartCall(pair, tokens, abi.encodeWithSignature("swap(address)", amount0Out, amount1Out, to));
}

// TODO: This method are used for swapping via multiple pairs. Not supported in nil for now
// **** SWAP ****
// requires the initial amount to have already been sent to the first pair
function _swap(uint[] memory amounts, address[] memory path, address _to) private {
Expand All @@ -66,13 +67,14 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase {
}
}

// TODO: This method are used for swapping via multiple pairs. Not supported in nil for now
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
) external override returns (uint[] memory amounts) {
amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
address pair = IUniswapV2Factory(factory).getTokenPair(path[0], path[1]);
Expand All @@ -81,13 +83,14 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase {
_swap(amounts, path, to);
}

// TODO: This method are used for swapping via multiple pairs. Not supported in nil for now
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external override ensure(deadline) returns (uint[] memory amounts) {
) external override returns (uint[] memory amounts) {
amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
address pair = IUniswapV2Factory(factory).getTokenPair(path[0], path[1]);
Expand All @@ -108,20 +111,12 @@ contract UniswapV2Router01 is IUniswapV2Router01, NilCurrencyBase {
return UniswapV2Library.getAmountOut(amountOut, reserveIn, reserveOut);
}

function getAmountsOut(uint amountIn, address[] memory path) public view override returns (uint[] memory amounts) {
return UniswapV2Library.getAmountsOut(factory, amountIn, path);
}

function getAmountsIn(uint amountOut, address[] memory path) public view override returns (uint[] memory amounts) {
return UniswapV2Library.getAmountsIn(factory, amountOut, path);
}

receive() external payable {
}

function smartCall(address dst, Nil.Token[] memory tokens, bytes memory callData) private returns (bool) {
if (Nil.getShardId(dst) == Nil.getShardId(address(dst))) {
(bool success, ) = Nil.syncCall(dst, gasleft(), 0, tokens, callData);
(bool success,) = Nil.syncCall(dst, gasleft(), 0, tokens, callData);
return success;
} else {
Nil.asyncCall(dst, address(0), address(0), 0, Nil.FORWARD_REMAINING, false, 0, tokens, callData);
Expand Down
27 changes: 12 additions & 15 deletions contracts/interfaces/IUniswapV2Router01.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@ pragma solidity ^0.8.0;
interface IUniswapV2Router01 {

function addLiquidity(
address tokenA,
address tokenB,
address to,
uint deadline,
uint salt
) external returns (uint amountA, uint amountB, uint liquidity);
address pair,
address to
) external;
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address pair,
address to
) external;
function swap(
address to,
uint deadline
) external returns (uint amountA, uint amountB);
address pair,
uint amount0Out,
uint amount1Out
) external;

function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
Expand All @@ -38,6 +37,4 @@ interface IUniswapV2Router01 {
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
1 change: 1 addition & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import "./tasks/core/factory/create-pair";

// Demo Tasks
import "./tasks/core/demo";
import "./tasks/core/demo-router";

dotenv.config();

Expand Down
100 changes: 56 additions & 44 deletions tasks/core/demo-router.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import { shardNumber } from "@nilfoundation/hardhat-plugin/dist/utils/conversion";
import { waitTillCompleted } from "@nilfoundation/niljs";
import { task } from "hardhat/config";
import {shardNumber} from "@nilfoundation/hardhat-plugin/dist/utils/conversion";
import {waitTillCompleted} from "@nilfoundation/niljs";
import {task} from "hardhat/config";
import {encodeFunctionData} from "viem";
import type {
Currency,
UniswapV2Factory,
UniswapV2Pair,
} from "../../typechain-types";
import { createClient } from "../util/client";
import {createClient} from "../util/client";
import {
faucetWithdrawal,
mintAndSendCurrency,
sleep,
} from "../util/currencyUtils";
import { deployNilContract } from "../util/deploy";
import { calculateOutputAmount } from "../util/math";
import {deployNilContract} from "../util/deploy";
import {calculateOutputAmount} from "../util/math";

task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
task("demo-router", "Run demo with Uniswap Router").setAction(
async (taskArgs, hre) => {
const walletAddress = process.env.WALLET_ADDR;
if (!walletAddress) {
Expand All @@ -30,16 +31,12 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
const mintCurrency1Amount = 10000;
const swapAmount = 1000;

const { wallet, publicClient, signer } = await createClient();
const {wallet, publicClient, signer} = await createClient();

const {
deployedContract: factoryContract,
contractAddress: factoryAddress,
} = await deployNilContract(hre, "UniswapV2Factory", [walletAddress]);
const {
deployedContract: routerContract,
contractAddress: routerAddress,
} = await deployNilContract(hre, "UniswapV2Router01", [walletAddress, "0x0000000000000000000000000000000000000000"]);
const {
deployedContract: Currency0Contract,
contractAddress: currency0Address,
Expand All @@ -59,6 +56,15 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
console.log("Currency0 deployed " + currency0Address);
console.log("Currency1 deployed " + currency1Address);

const {
deployedContract: RouterContract,
contractAddress: routerAddress,
} = await deployNilContract(hre, "UniswapV2Router01", [
factoryAddress.toLowerCase(),
]);

console.log("Router deployed " + routerAddress);

const factory = factoryContract as UniswapV2Factory;

// 1. CREATE PAIR
Expand Down Expand Up @@ -160,18 +166,23 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
`Recipient balance after transfer - Currency0: ${recipientBalanceCurrency0}, Currency1: ${recipientBalanceCurrency1}`,
);

// 3. PAIR: MINT
// 3. ROUTER: ADD LIQUIDITY
const pairArtifact = await hre.artifacts.readArtifact("UniswapV2Pair");
const routerArtifact = await hre.artifacts.readArtifact("UniswapV2Router01");

// Mint liquidity
console.log("Adding liquidity...");

// Send currency amounts to the pair contract
console.log(
`Sending ${mintCurrency0Amount} currency0 and ${mintCurrency1Amount} currency1 to ${pairAddress}...`,
);
const hash = await wallet.sendMessage({
// @ts-ignore
to: pairAddress,
to: routerAddress,
feeCredit: BigInt(10_000_000),
value: BigInt(0),
refundTo: wallet.address,
data: encodeFunctionData({
abi: routerArtifact.abi,
functionName: "addLiquidity",
args: [pairAddress, walletAddress],
}),
tokens: [
{
id: await firstCurrency.getCurrencyId(),
Expand All @@ -195,32 +206,29 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
await secondCurrency.getCurrencyBalanceOf(pairAddress);
console.log("Pair Balance of Currency1:", pairCurrency1Balance.toString());

// Mint liquidity
console.log("Minting pair tokens...");
await pair.mint(walletAddress);
console.log("Liquidity added...");

// Retrieve and log reserves from the pair
const [reserve0, reserve1] = await pair.getReserves();
console.log(
`MINT RESULT: Reserves - Currency0: ${reserve0.toString()}, Currency1: ${reserve1.toString()}`,
`ADDLIQUIDITY RESULT: Reserves - Currency0: ${reserve0.toString()}, Currency1: ${reserve1.toString()}`,
);

// Check and log liquidity provider balance
const lpBalance = await pair.getCurrencyBalanceOf(walletAddress);
console.log(
"MINT RESULT: Liquidity provider balance in wallet:",
"ADDLIQUIDITY RESULT: Liquidity provider balance in wallet:",
lpBalance.toString(),
);

// Retrieve and log total supply for the pair
const totalSupply = await pair.getCurrencyTotalSupply();
console.log(
"MINT RESULT: Total supply of pair tokens:",
"ADDLIQUIDITY RESULT: Total supply of pair tokens:",
totalSupply.toString(),
);

// 4. PAIR: SWAP
// 4. ROUTER: SWAP
const expectedOutputAmount = calculateOutputAmount(
BigInt(swapAmount),
reserve0,
Expand All @@ -245,14 +253,19 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
balanceCurrency1Before.toString(),
);

// Attach to the UserWallet contract
// Execute the swap
console.log("Executing swap...");

// Send currency0 to the pair contract
const hash2 = await wallet.sendMessage({
// @ts-ignore
to: pairAddress,
to: routerAddress,
feeCredit: BigInt(10_000_000),
value: BigInt(0),
data: encodeFunctionData({
abi: routerArtifact.abi,
functionName: "swap",
args: [walletAddress, pairAddress, 0, expectedOutputAmount],
}),
refundTo: wallet.address,
tokens: [
{
Expand All @@ -268,9 +281,6 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
`Sent ${swapAmount.toString()} of currency0 to the pair contract.`,
);

// Execute the swap
console.log("Executing swap...");
await pair.swap(0, expectedOutputAmount, walletAddress);
console.log("Swap executed successfully.");

// Log balances after the swap
Expand All @@ -287,7 +297,7 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
balanceCurrency1After.toString(),
);

// 5. PAIR: BURN
// 5. ROUTER: REMOVE LIQUIDITY
const total = await pair.getCurrencyTotalSupply();
console.log("Total supply:", total.toString());

Expand Down Expand Up @@ -324,13 +334,19 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
const lpAddress = await pair.getCurrencyId();
const userLpBalance = await pair.getCurrencyBalanceOf(walletAddress);
console.log("Total LP balance for user wallet:", userLpBalance.toString());

// Execute burn
console.log("Executing burn...");
// Send LP tokens to the user wallet
const hash3 = await wallet.sendMessage({
// @ts-ignore
to: pairAddress,
to: routerAddress,
feeCredit: BigInt(10_000_000),
value: BigInt(0),
data: encodeFunctionData({
abi: routerArtifact.abi,
functionName: "removeLiquidity",
args: [pairAddress, walletAddress],
}),
refundTo: walletAddress,
tokens: [
{
Expand All @@ -342,11 +358,7 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(

await waitTillCompleted(publicClient, shardNumber(walletAddress), hash3);

// Execute burn
console.log("Executing burn...");
await pair.burn(walletAddress);
console.log("Burn executed.");
console.log("Built tokens");

// Log balances after burn
const balanceToken0 = await firstCurrency.getCurrencyBalanceOf(
Expand All @@ -356,30 +368,30 @@ task("demo-router", "Run demo for Uniswap Pairs and Factory").setAction(
pairAddress.toLowerCase(),
);
console.log(
"BURN RESULT: Pair Balance token0 after burn:",
"REMOVELIQUIDITY RESULT: Pair Balance token0 after burn:",
balanceToken0.toString(),
);
console.log(
"BURN RESULT: Pair Balance token1 after burn:",
"REMOVELIQUIDITY RESULT: Pair Balance token1 after burn:",
balanceToken1.toString(),
);

userBalanceToken0 = await firstCurrency.getCurrencyBalanceOf(walletAddress);
userBalanceToken1 =
await secondCurrency.getCurrencyBalanceOf(walletAddress);
console.log(
"BURN RESULT: User Balance token0 after burn:",
"REMOVELIQUIDITY RESULT: User Balance token0 after burn:",
userBalanceToken0.toString(),
);
console.log(
"BURN RESULT: User Balance token1 after burn:",
"REMOVELIQUIDITY RESULT: User Balance token1 after burn:",
userBalanceToken1.toString(),
);

// Fetch and log reserves after burn
const reserves = await pair.getReserves();
console.log(
"BURN RESULT: Reserves from pair after burn:",
"REMOVELIQUIDITY RESULT: Reserves from pair after burn:",
reserves[0].toString(),
reserves[1].toString(),
);
Expand Down

0 comments on commit c7fb044

Please sign in to comment.