-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
10,185 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
Oops, something went wrong.