-
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
0 parents
commit d6a640c
Showing
14 changed files
with
7,856 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,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
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,17 @@ | ||
node_modules | ||
.env | ||
|
||
# Hardhat files | ||
/cache | ||
/artifacts | ||
|
||
# TypeChain files | ||
/typechain | ||
/typechain-types | ||
|
||
# solidity-coverage files | ||
/coverage | ||
/coverage.json | ||
|
||
# Hardhat Ignition default folder for deployments against a local node | ||
ignition/deployments/chain-31337 |
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,3 @@ | ||
{ | ||
"solidity.compileUsingRemoteVersion": "v0.8.20+commit.a1b79de6" | ||
} |
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,13 @@ | ||
# Sample Hardhat Project | ||
|
||
This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a Hardhat Ignition module that deploys that contract. | ||
|
||
Try running some of the following tasks: | ||
|
||
```shell | ||
npx hardhat help | ||
npx hardhat test | ||
REPORT_GAS=true npx hardhat test | ||
npx hardhat node | ||
npx hardhat ignition deploy ./ignition/modules/Lock.js | ||
``` |
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: Unlicensed | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract Firsttoken is ERC20 { | ||
address private owner; | ||
|
||
constructor(uint256 initialSupply) ERC20("Firsttoken", "FT") { | ||
_mint(msg.sender, initialSupply); | ||
owner = msg.sender; | ||
} | ||
|
||
function mint(address to, uint amount) public { | ||
require(msg.sender == owner, "only owner"); | ||
_mint(to, amount); | ||
} | ||
} |
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,34 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.20; | ||
|
||
// Uncomment this line to use console.log | ||
// import "hardhat/console.sol"; | ||
|
||
contract Lock { | ||
uint public unlockTime; | ||
address payable public owner; | ||
|
||
event Withdrawal(uint amount, uint when); | ||
|
||
constructor(uint _unlockTime) payable { | ||
require( | ||
block.timestamp < _unlockTime, | ||
"Unlock time should be in the future" | ||
); | ||
|
||
unlockTime = _unlockTime; | ||
owner = payable(msg.sender); | ||
} | ||
|
||
function withdraw() public { | ||
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal | ||
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp); | ||
|
||
require(block.timestamp >= unlockTime, "You can't withdraw yet"); | ||
require(msg.sender == owner, "You aren't the owner"); | ||
|
||
emit Withdrawal(address(this).balance, block.timestamp); | ||
|
||
owner.transfer(address(this).balance); | ||
} | ||
} |
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: Unlicensed | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract Secondtoken is ERC20 { | ||
address private owner; | ||
|
||
constructor(uint256 initialSupply) ERC20("Secondtoken", "ST") { | ||
_mint(msg.sender, initialSupply); | ||
owner = msg.sender; | ||
} | ||
|
||
function mint(address to, uint amount) public { | ||
require(msg.sender == owner, "only owner"); | ||
_mint(to, amount); | ||
} | ||
} |
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,264 @@ | ||
// SPDX-License-Identifier: Unlicensed | ||
pragma solidity ^0.8.20; | ||
|
||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; | ||
|
||
|
||
contract zbytedex is ReentrancyGuard { | ||
mapping(bytes => Pool) pools; | ||
uint INITIAL_LP_BALANCE = 10_000 * 1e18; | ||
uint LP_FEE = 30; | ||
|
||
struct Pool { | ||
mapping(address => uint) tokenBalances; | ||
mapping(address => uint) lpBalances; | ||
uint totalLpTokens; | ||
} | ||
|
||
function createPool( | ||
address tokenA, | ||
address tokenB, | ||
uint amountA, | ||
uint amountB | ||
) | ||
public | ||
validTokenAddresses(tokenA, tokenB) | ||
hasBalanceAndAllowance(tokenA, tokenB, amountA, amountB) | ||
nonReentrant | ||
{ | ||
// check all values are valid | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
require(pool.tokenBalances[tokenA] == 0, "pool already exists!"); | ||
|
||
// deposit tokens into contract | ||
_transferTokens(tokenA, tokenB, amountA, amountB); | ||
|
||
// initalize the pool | ||
pool.tokenBalances[tokenA] = amountA; | ||
pool.tokenBalances[tokenB] = amountB; | ||
pool.lpBalances[msg.sender] = INITIAL_LP_BALANCE; | ||
pool.totalLpTokens = INITIAL_LP_BALANCE; | ||
} | ||
|
||
function addLiquidity( | ||
address tokenA, | ||
address tokenB, | ||
uint amountA, | ||
uint amountB | ||
) | ||
public | ||
validTokenAddresses(tokenA, tokenB) | ||
hasBalanceAndAllowance(tokenA, tokenB, amountA, amountB) | ||
nonReentrant | ||
poolMustExist(tokenA, tokenB) | ||
{ | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
uint tokenAPrice = getSpotPrice(tokenA, tokenB); | ||
require( | ||
tokenAPrice * amountA == amountB * 1e18, | ||
"must add liquidity at the current spot price" | ||
); | ||
|
||
_transferTokens(tokenA, tokenB, amountA, amountB); | ||
|
||
uint currentABalance = pool.tokenBalances[tokenA]; | ||
uint newTokens = (amountA * INITIAL_LP_BALANCE) / currentABalance; | ||
|
||
pool.tokenBalances[tokenA] += amountA; | ||
pool.tokenBalances[tokenB] += amountB; | ||
pool.totalLpTokens += newTokens; | ||
pool.lpBalances[msg.sender] += newTokens; | ||
} | ||
|
||
function removeLiquidity( | ||
address tokenA, | ||
address tokenB | ||
) | ||
public | ||
validTokenAddresses(tokenA, tokenB) | ||
nonReentrant | ||
poolMustExist(tokenA, tokenB) | ||
{ | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
uint balance = pool.lpBalances[msg.sender]; | ||
require(balance > 0, "No liquidity provided by this user"); | ||
|
||
// how much of tokenA and tokenB should we send to the LP? | ||
uint tokenAAmount = (balance * pool.tokenBalances[tokenA]) / | ||
pool.totalLpTokens; | ||
uint tokenBAmount = (balance * pool.tokenBalances[tokenB]) / | ||
pool.totalLpTokens; | ||
|
||
pool.lpBalances[msg.sender] = 0; | ||
pool.tokenBalances[tokenA] -= tokenAAmount; | ||
pool.tokenBalances[tokenB] -= tokenBAmount; | ||
pool.totalLpTokens -= balance; | ||
|
||
// send tokens to user | ||
ERC20 contractA = ERC20(tokenA); | ||
ERC20 contractB = ERC20(tokenB); | ||
|
||
require( | ||
contractA.transfer(msg.sender, tokenAAmount), | ||
"transfer failed" | ||
); | ||
require( | ||
contractB.transfer(msg.sender, tokenBAmount), | ||
"transfer failed" | ||
); | ||
} | ||
|
||
function swap( | ||
address from, | ||
address to, | ||
uint amount | ||
) | ||
public | ||
validTokenAddresses(from, to) | ||
nonReentrant | ||
poolMustExist(from, to) | ||
{ | ||
Pool storage pool = _getPool(from, to); | ||
|
||
// deltaY = y * r * deltaX / x + (r * deltaX) | ||
uint r = 10_000 - LP_FEE; | ||
uint rDeltaX = (r * amount) / 10_000; | ||
|
||
uint outputTokens = (pool.tokenBalances[to] * rDeltaX) / | ||
(pool.tokenBalances[from] + rDeltaX); | ||
|
||
pool.tokenBalances[from] += amount; | ||
pool.tokenBalances[to] -= outputTokens; | ||
|
||
// send and receive tokens | ||
ERC20 contractFrom = ERC20(from); | ||
ERC20 contractTo = ERC20(to); | ||
|
||
require( | ||
contractFrom.transferFrom(msg.sender, address(this), amount), | ||
"transfer from user failed" | ||
); | ||
require( | ||
contractTo.transfer(msg.sender, outputTokens), | ||
"transfer to user failed" | ||
); | ||
} | ||
|
||
// HELPERS | ||
function _getPool( | ||
address tokenA, | ||
address tokenB | ||
) internal view returns (Pool storage pool) { | ||
bytes memory key; | ||
if (tokenA < tokenB) { | ||
key = abi.encodePacked(tokenA, tokenB); | ||
} else { | ||
key = abi.encodePacked(tokenB, tokenA); | ||
} | ||
return pools[key]; | ||
} | ||
|
||
function _transferTokens( | ||
address tokenA, | ||
address tokenB, | ||
uint amountA, | ||
uint amountB | ||
) internal { | ||
ERC20 contractA = ERC20(tokenA); | ||
ERC20 contractB = ERC20(tokenB); | ||
|
||
require( | ||
contractA.transferFrom(msg.sender, address(this), amountA), | ||
"Transfer of tokenA failed" | ||
); | ||
require( | ||
contractB.transferFrom(msg.sender, address(this), amountB), | ||
"Transfer of tokenB failed" | ||
); | ||
} | ||
|
||
function getSpotPrice( | ||
address tokenA, | ||
address tokenB | ||
) public view returns (uint) { | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
require( | ||
pool.tokenBalances[tokenA] > 0 && pool.tokenBalances[tokenB] > 0, | ||
"balances must be non-zero" | ||
); | ||
return ((pool.tokenBalances[tokenB] * 1e18) / | ||
pool.tokenBalances[tokenA]); | ||
} | ||
|
||
function getBalances( | ||
address tokenA, | ||
address tokenB | ||
) external view returns (uint tokenABalance, uint tokenBBalance) { | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
return (pool.tokenBalances[tokenA], pool.tokenBalances[tokenB]); | ||
} | ||
|
||
function getLpBalance( | ||
address lp, | ||
address tokenA, | ||
address tokenB | ||
) external view returns (uint) { | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
return (pool.lpBalances[lp]); | ||
} | ||
|
||
function getTotalLpTokens( | ||
address tokenA, | ||
address tokenB | ||
) external view returns (uint) { | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
return (pool.totalLpTokens); | ||
} | ||
|
||
// MODIFIERS | ||
modifier validTokenAddresses(address tokenA, address tokenB) { | ||
require(tokenA != tokenB, "addresses must be different!"); | ||
require( | ||
tokenA != address(0) && tokenB != address(0), | ||
"must be valid addresses!" | ||
); | ||
_; | ||
} | ||
|
||
modifier hasBalanceAndAllowance( | ||
address tokenA, | ||
address tokenB, | ||
uint amountA, | ||
uint amountB | ||
) { | ||
ERC20 contractA = ERC20(tokenA); | ||
ERC20 contractB = ERC20(tokenB); | ||
|
||
require( | ||
contractA.balanceOf(msg.sender) >= amountA, | ||
"user doesn't have enough tokens" | ||
); | ||
require( | ||
contractB.balanceOf(msg.sender) >= amountB, | ||
"user doesn't have enough tokens" | ||
); | ||
require( | ||
contractA.allowance(msg.sender, address(this)) >= amountA, | ||
"user didn't grant allowance" | ||
); | ||
require( | ||
contractB.allowance(msg.sender, address(this)) >= amountB, | ||
"user didn't grant allowance" | ||
); | ||
|
||
_; | ||
} | ||
|
||
modifier poolMustExist(address tokenA, address tokenB) { | ||
Pool storage pool = _getPool(tokenA, tokenB); | ||
require(pool.tokenBalances[tokenA] != 0, "pool must exist"); | ||
require(pool.tokenBalances[tokenB] != 0, "pool must exist"); | ||
_; | ||
} | ||
} |
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,15 @@ | ||
require("@nomicfoundation/hardhat-toolbox"); | ||
|
||
/** @type import('hardhat/config').HardhatUserConfig */ | ||
module.exports = { | ||
solidity: { | ||
version: "0.8.20", | ||
settings: { | ||
viaIR: true, | ||
optimizer: { | ||
enabled: true, | ||
runs: 200 | ||
} | ||
} | ||
} | ||
}; |
Oops, something went wrong.