Skip to content

Commit

Permalink
first push
Browse files Browse the repository at this point in the history
  • Loading branch information
keeganlee committed Sep 5, 2023
1 parent 12e9a6d commit e80e453
Show file tree
Hide file tree
Showing 11 changed files with 10,185 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
node_modules
.env
coverage
coverage.json
typechain
typechain-types

# Hardhat files
cache
artifacts

171 changes: 171 additions & 0 deletions contracts/ZkAmmPair.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "./interfaces/IZkAmmPair.sol";
import "./libraries/TransferHelper.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract ZkAmmPair is IZkAmmPair, ERC20 {
address public immutable token0;
address public immutable token1;
address public immutable zkGraph;

uint public reserve0;
uint public reserve1;

modifier onlyZkGraph() {
require(msg.sender == zkGraph, "only zkGraph");
_;
}

constructor(address tokenA, address tokenB, address zkGraph_) ERC20("ZK AMM", "ZK-AMM") {
require(tokenA != tokenB, 'IDENTICAL_ADDRESSES');
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
zkGraph = zkGraph_;
}

function addInitLiquidity(
address recipient,
uint amount0,
uint amount1
) external override {
require(recipient != address(0), "zero address");
require(amount0 > 0 && amount1 > 0, "zero amount");
require(reserve0 == 0 && reserve1 == 0, "already init");

uint balance0 = IERC20(token0).balanceOf(msg.sender);
uint balance1 = IERC20(token1).balanceOf(msg.sender);
require(balance0 >= amount0 && balance1 >= amount1, "not enough balance");

emit AddInitLiquidity(msg.sender, recipient, amount0, amount1);
}

function addInitLiquidityCallback(
address payer,
address recipient,
uint amount0,
uint amount1,
uint liquidity
) external override onlyZkGraph {
require(payer != address(0) && recipient != address(0), "zero address");
require(amount0 > 0 && amount1 > 0, "zero amount");
require(liquidity > 0, "zero liquidity");
require(reserve0 == 0 && reserve1 == 0, "already init");

TransferHelper.safeTransferFrom(token0, payer, address(this), amount0);
TransferHelper.safeTransferFrom(token1, payer, address(this), amount1);

reserve0 = amount0;
reserve1 = amount1;

_mint(recipient, liquidity);
emit LiquidityAdded(payer, recipient, amount0, amount1, liquidity);
}

function addLiquidity(address recipient, uint amount0) external override {
require(recipient != address(0), "zero address");
require(amount0 > 0, "zero amount");
require(reserve0 > 0 && reserve1 > 0, "not init");

uint balance0 = IERC20(token0).balanceOf(msg.sender);
require(balance0 >= amount0, "not enough balance");

uint _totalSupply = totalSupply();
emit AddLiquidity(msg.sender, recipient, amount0, reserve0, reserve1, _totalSupply);
}

function addLiquidityCallback(
address payer,
address recipient,
uint amount0,
uint amount1,
uint liquidity
) external override onlyZkGraph {
require(payer != address(0) && recipient != address(0), "zero address");
require(amount0 > 0 && amount1 > 0, "zero amount");
require(liquidity > 0, "zero liquidity");
require(reserve0 > 0 && reserve1 > 0, "not init");

TransferHelper.safeTransferFrom(token0, payer, address(this), amount0);
TransferHelper.safeTransferFrom(token1, payer, address(this), amount1);

reserve0 += amount0;
reserve1 += amount1;

_mint(recipient, liquidity);
emit LiquidityAdded(payer, recipient, amount0, amount1, liquidity);
}

function removeLiquidity(address recipient, uint liquidity) external override {
require(recipient != address(0), "zero address");
require(balanceOf(msg.sender) >= liquidity, "insufficient liquidity");
uint _totalSupply = totalSupply();
emit RemoveLiquidity(msg.sender, recipient, liquidity, reserve0, reserve1, _totalSupply);
}

function removeLiquidityCallback(
address sender,
address recipient,
uint liquidity,
uint amount0,
uint amount1
) external override onlyZkGraph {
require(sender != address(0) && recipient != address(0), "zero address");
require(liquidity > 0, "zero liquidity");
require(amount0 > 0 && amount1 > 0, "zero amount");
require(balanceOf(sender) >= liquidity, "insufficient liquidity");

_burn(sender, liquidity);
reserve0 -= amount0;
reserve1 -= amount1;

TransferHelper.safeTransfer(token0, recipient, amount0);
TransferHelper.safeTransfer(token1, recipient, amount1);
emit LiquidityRemoved(sender, recipient, liquidity, amount0, amount1);
}

function swap(address recipient, bool zeroForOne, uint amountIn) external override {
require(recipient != address(0), "zero address");
require(amountIn > 0, "zero amountIn");

uint balance;
if (zeroForOne) {
balance = IERC20(token0).balanceOf(msg.sender);
} else {
balance = IERC20(token1).balanceOf(msg.sender);
}
require(balance >= amountIn, "not enough balance");

emit Swap(msg.sender, recipient, zeroForOne, amountIn, reserve0, reserve1);
}

function swapCallback(
address payer,
address recipient,
bool zeroForOne,
uint amountIn,
uint amountOut
) external override onlyZkGraph {
require(payer != address(0) && recipient != address(0), "zero address");

address tokenIn;
address tokenOut;
if (zeroForOne) {
tokenIn = token0;
tokenOut = token1;

reserve0 += amountIn;
reserve1 -= amountOut;
} else {
tokenIn = token1;
tokenOut = token0;

reserve1 += amountIn;
reserve0 -= amountOut;
}
TransferHelper.safeTransferFrom(tokenIn, payer, address(this), amountIn);
TransferHelper.safeTransfer(tokenOut, recipient, amountOut);

emit Swapped(payer, recipient, zeroForOne, amountIn, amountOut);
}
}
18 changes: 18 additions & 0 deletions contracts/interfaces/IERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);

function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);

function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
58 changes: 58 additions & 0 deletions contracts/interfaces/IZkAmmPair.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IZkAmmPair {
event AddInitLiquidity(address payer, address recipient, uint amount0, uint amount1);
event AddLiquidity(address payer, address recipient, uint amount0, uint reserve0, uint reserve1, uint totalSupply);
event LiquidityAdded(address payer, address recipient, uint amount0, uint amount1, uint liquidity);
event RemoveLiquidity(address sender, address recipient, uint liquidity, uint reserve0, uint reserve1, uint totalSupply);
event LiquidityRemoved(address sender, address recipient, uint liquidity, uint amount0, uint amount1);
event Swap(address payer, address recipient, bool zeroForOne, uint amountIn, uint reserve0, uint reserve1);
event Swapped(address payer, address recipient, bool zeroForOne, uint amountIn, uint amountOut);

function token0() external view returns (address);
function token1() external view returns (address);
function zkGraph() external view returns (address);
function reserve0() external view returns (uint);
function reserve1() external view returns (uint);

function addInitLiquidity(
address recipient,
uint amount0,
uint amount1
) external;
function addInitLiquidityCallback(
address payer,
address recipient,
uint amount0,
uint amount1,
uint liquidity
) external;

function addLiquidity(address recipient, uint amount0) external;
function addLiquidityCallback(
address payer,
address recipient,
uint amount0,
uint amount1,
uint liquidity
) external;

function removeLiquidity(address recipient, uint liquidity) external;
function removeLiquidityCallback(
address sender,
address recipient,
uint liquidity,
uint amount0,
uint amount1
) external;

function swap(address recipient, bool zeroForOne, uint amountIn) external;
function swapCallback(
address payer,
address recipient,
bool zeroForOne,
uint amountIn,
uint amountOut
) external;
}
50 changes: 50 additions & 0 deletions contracts/libraries/TransferHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeApprove: approve failed"
);
}

function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::safeTransfer: transfer failed"
);
}

function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
"TransferHelper::transferFrom: transferFrom failed"
);
}

function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, "TransferHelper::safeTransferETH: ETH transfer failed");
}
}
8 changes: 8 additions & 0 deletions hardhat.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
solidity: "0.8.19",
};

export default config;

0 comments on commit e80e453

Please sign in to comment.