├── .gitignore ├── README.md ├── contracts ├── ERC314.sol ├── LongSwap.sol ├── Staking.sol ├── X314.sol ├── interfaces │ └── IERC314.sol └── libs │ └── Linear.sol ├── package.json ├── scripts ├── addLiquidity.js ├── deploy_staking.js ├── deploy_token.js ├── extendLiquidityLock.js ├── transferOwnership.js └── upgrade_staking.js ├── test └── Lock.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample Hardhat Project 2 | 3 | This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract. 4 | 5 | Try running some of the following tasks: 6 | 7 | ```shell 8 | npx hardhat help 9 | npx hardhat test 10 | REPORT_GAS=true npx hardhat test 11 | npx hardhat node 12 | npx hardhat run scripts/deploy.js 13 | ``` 14 | -------------------------------------------------------------------------------- /contracts/ERC314.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/access/Ownable.sol"; 5 | import "@openzeppelin/contracts/utils/Context.sol"; 6 | 7 | interface IEERC314 { 8 | event Transfer(address indexed from, address indexed to, uint256 value); 9 | event Approval( 10 | address indexed owner, 11 | address indexed spender, 12 | uint256 value 13 | ); 14 | event AddLiquidity(uint32 _blockToUnlockLiquidity, uint256 value); 15 | event RemoveLiquidity(uint256 value); 16 | event Swap( 17 | address indexed sender, 18 | uint amount0In, 19 | uint amount1In, 20 | uint amount0Out, 21 | uint amount1Out 22 | ); 23 | } 24 | 25 | abstract contract ERC314 is Context, Ownable, IEERC314 { 26 | mapping(address account => uint256) private _balances; 27 | mapping(address => mapping(address => uint256)) private _allowances; 28 | 29 | uint256 private _totalSupply; 30 | uint256 public _maxWallet; 31 | uint32 public blockToUnlockLiquidity; 32 | uint32 public coolingBlock; 33 | uint32 public sellTax; 34 | uint32 public buyTax; 35 | 36 | mapping(address => bool) public excludeCoolingOf; 37 | 38 | string private _name; 39 | string private _symbol; 40 | 41 | address public liquidityProvider; 42 | 43 | bool public liquidityAdded; 44 | 45 | uint256 public constant _rebaseDuration = 1 hours; 46 | uint256 public _rebaseRate = 25; 47 | uint256 public _lastRebaseTime; 48 | 49 | mapping(address account => uint32) private lastTransaction; 50 | 51 | uint256 presaleAmount; 52 | bool public presaleEnable = false; 53 | 54 | modifier onlyLiquidityProvider() { 55 | require( 56 | _msgSender() == liquidityProvider, 57 | "You are not the liquidity provider" 58 | ); 59 | _; 60 | } 61 | 62 | constructor( 63 | string memory name_, 64 | string memory symbol_, 65 | uint256 totalSupply_, 66 | uint32 _coolingBlock, 67 | uint32 _sellTax, 68 | uint32 _buyTax 69 | ) { 70 | _name = name_; 71 | _symbol = symbol_; 72 | _totalSupply = totalSupply_; 73 | 74 | _maxWallet = totalSupply_ / 200; 75 | 76 | coolingBlock = _coolingBlock; 77 | sellTax = _sellTax; 78 | buyTax = _buyTax; 79 | 80 | _lastRebaseTime = block.timestamp + 1 days; 81 | 82 | presaleAmount = (totalSupply_ * 2) / 10; 83 | 84 | uint256 liquidityAmount = totalSupply_ - presaleAmount; 85 | _balances[address(this)] = liquidityAmount; 86 | } 87 | 88 | function presale(address[] memory _investors) public onlyOwner { 89 | require(presaleEnable == false, "Presale already enabled"); 90 | uint256 _amount = presaleAmount / _investors.length; 91 | for (uint256 i = 0; i < _investors.length; i++) { 92 | _balances[_investors[i]] += _amount; 93 | } 94 | presaleEnable = true; 95 | } 96 | 97 | function name() public view virtual returns (string memory) { 98 | return _name; 99 | } 100 | 101 | function symbol() public view virtual returns (string memory) { 102 | return _symbol; 103 | } 104 | 105 | function decimals() public view virtual returns (uint8) { 106 | return 18; 107 | } 108 | 109 | function totalSupply() public view virtual returns (uint256) { 110 | return _totalSupply; 111 | } 112 | 113 | function balanceOf(address account) public view virtual returns (uint256) { 114 | return _balances[account]; 115 | } 116 | 117 | function allowance( 118 | address owner, 119 | address spender 120 | ) public view virtual returns (uint256) { 121 | return _allowances[owner][spender]; 122 | } 123 | 124 | function approve( 125 | address spender, 126 | uint256 amount 127 | ) public virtual returns (bool) { 128 | address owner = _msgSender(); 129 | _approve(owner, spender, amount); 130 | return true; 131 | } 132 | 133 | function transferFrom( 134 | address from, 135 | address to, 136 | uint256 amount 137 | ) public virtual returns (bool) { 138 | address spender = _msgSender(); 139 | _spendAllowance(from, spender, amount); 140 | 141 | if (to == address(this)) { 142 | sell(from, amount); 143 | } else { 144 | _transfer(from, to, amount); 145 | } 146 | 147 | return true; 148 | } 149 | 150 | function transfer(address to, uint256 value) public virtual returns (bool) { 151 | // sell or transfer 152 | if (to == address(this)) { 153 | sell(_msgSender(), value); 154 | } else { 155 | _transfer(_msgSender(), to, value); 156 | } 157 | return true; 158 | } 159 | 160 | function _approve( 161 | address owner, 162 | address spender, 163 | uint256 amount 164 | ) internal virtual { 165 | require(owner != address(0), "ERC20: approve from the zero address"); 166 | require(spender != address(0), "ERC20: approve to the zero address"); 167 | 168 | _allowances[owner][spender] = amount; 169 | emit Approval(owner, spender, amount); 170 | } 171 | 172 | function _spendAllowance( 173 | address owner, 174 | address spender, 175 | uint256 amount 176 | ) internal virtual { 177 | uint256 currentAllowance = allowance(owner, spender); 178 | if (currentAllowance != type(uint256).max) { 179 | require( 180 | currentAllowance >= amount, 181 | "ERC20: insufficient allowance" 182 | ); 183 | unchecked { 184 | _approve(owner, spender, currentAllowance - amount); 185 | } 186 | } 187 | } 188 | 189 | function _transfer( 190 | address from, 191 | address to, 192 | uint256 amount 193 | ) internal virtual { 194 | require(to != address(0), "ERC20: transfer to the zero address"); 195 | 196 | if (!excludeCoolingOf[_msgSender()]) { 197 | require( 198 | lastTransaction[_msgSender()] + coolingBlock < block.number, 199 | "You can't make two transactions in the cooling block" 200 | ); 201 | lastTransaction[_msgSender()] = uint32(block.number); 202 | } 203 | 204 | uint256 fromBalance = _balances[from]; 205 | require( 206 | fromBalance >= amount, 207 | "ERC20: transfer amount exceeds balance" 208 | ); 209 | unchecked { 210 | _balances[from] = fromBalance - amount; 211 | _balances[to] += amount; 212 | } 213 | 214 | emit Transfer(from, to, amount); 215 | } 216 | 217 | function _basicTransfer(address from, address to, uint256 amount) internal { 218 | require( 219 | _balances[from] >= amount, 220 | "ERC20: transfer amount exceeds balance" 221 | ); 222 | 223 | unchecked { 224 | _balances[from] -= amount; 225 | _balances[to] += amount; 226 | } 227 | 228 | emit Transfer(from, to, amount); 229 | } 230 | 231 | function getReserves() public view returns (uint256, uint256) { 232 | return (address(this).balance, _balances[address(this)]); 233 | } 234 | 235 | function setMaxWallet(uint256 _maxWallet_) external onlyOwner { 236 | _maxWallet = _maxWallet_; 237 | } 238 | 239 | function setLastTransaction( 240 | address[] memory accounts, 241 | uint32 _block 242 | ) external onlyOwner { 243 | for (uint i = 0; i < accounts.length; i++) { 244 | lastTransaction[accounts[i]] = _block; 245 | } 246 | } 247 | 248 | function setExcludeCoolingOf( 249 | address[] memory accounts, 250 | bool _ok 251 | ) external onlyOwner { 252 | for (uint i = 0; i < accounts.length; i++) { 253 | excludeCoolingOf[accounts[i]] = _ok; 254 | } 255 | } 256 | 257 | function setBuyTax(uint32 _buyTax) external onlyOwner { 258 | require(_buyTax <= 50, "Tax is too big"); 259 | buyTax = _buyTax; 260 | } 261 | 262 | function setSellTax(uint32 _sellTax) external onlyOwner { 263 | require(_sellTax <= 50, "Tax is too big"); 264 | sellTax = _sellTax; 265 | } 266 | 267 | function setCooling(uint32 _coolingBlock) external onlyOwner { 268 | require(_coolingBlock <= 100, "Cooling is too big"); 269 | coolingBlock = _coolingBlock; 270 | } 271 | 272 | function addLiquidity( 273 | uint32 _blockToUnlockLiquidity 274 | ) public payable onlyOwner { 275 | require(liquidityAdded == false, "Liquidity already added"); 276 | 277 | liquidityAdded = true; 278 | 279 | require(msg.value > 0, "No ETH sent"); 280 | require(block.number < _blockToUnlockLiquidity, "Block number too low"); 281 | 282 | blockToUnlockLiquidity = _blockToUnlockLiquidity; 283 | liquidityProvider = _msgSender(); 284 | 285 | emit AddLiquidity(_blockToUnlockLiquidity, msg.value); 286 | } 287 | 288 | function removeLiquidity() public onlyLiquidityProvider { 289 | require(block.number > blockToUnlockLiquidity, "Liquidity locked"); 290 | 291 | liquidityAdded = false; 292 | 293 | payable(liquidityProvider).transfer(address(this).balance); 294 | 295 | emit RemoveLiquidity(address(this).balance); 296 | } 297 | 298 | function extendLiquidityLock( 299 | uint32 _blockToUnlockLiquidity 300 | ) public onlyLiquidityProvider { 301 | require( 302 | blockToUnlockLiquidity < _blockToUnlockLiquidity, 303 | "You can't shorten duration" 304 | ); 305 | 306 | blockToUnlockLiquidity = _blockToUnlockLiquidity; 307 | } 308 | 309 | function getAmountOut( 310 | uint256 value, 311 | bool _buy 312 | ) public view returns (uint256) { 313 | (uint256 reserveETH, uint256 reserveToken) = getReserves(); 314 | 315 | if (_buy) { 316 | return (value * reserveToken) / (reserveETH + value); 317 | } else { 318 | return (value * reserveETH) / (reserveToken + value); 319 | } 320 | } 321 | 322 | function buy() internal { 323 | require(liquidityAdded, "Trading not enable"); 324 | require(msg.sender == tx.origin, "Only external calls allowed"); 325 | 326 | address owner = _msgSender(); 327 | 328 | uint256 tokenAmount = (msg.value * _balances[address(this)]) / 329 | (address(this).balance); 330 | 331 | require( 332 | tokenAmount + _balances[owner] <= _maxWallet, 333 | "Max wallet exceeded" 334 | ); 335 | 336 | uint256 fee = (tokenAmount * buyTax) / 100; 337 | _transfer(address(this), owner, tokenAmount - fee); 338 | _basicTransfer(address(this), address(0xdead), fee); 339 | 340 | emit Swap(owner, msg.value, 0, 0, tokenAmount); 341 | } 342 | 343 | function sell(address owner, uint256 amount) internal { 344 | require(liquidityAdded, "Trading not enable"); 345 | require(msg.sender == tx.origin, "Only external calls allowed"); 346 | 347 | uint256 fee = (amount * sellTax) / 100; 348 | uint256 sellAmount = amount - fee; 349 | 350 | uint256 ethAmount = (sellAmount * address(this).balance) / 351 | (_balances[address(this)] + sellAmount); 352 | 353 | require(ethAmount > 0, "Sell amount too low"); 354 | require( 355 | address(this).balance >= ethAmount, 356 | "Insufficient ETH in reserves" 357 | ); 358 | 359 | _transfer(owner, address(this), amount); 360 | _basicTransfer(address(this), address(0xdead), fee); 361 | 362 | payable(owner).transfer(ethAmount); 363 | 364 | emit Swap(owner, 0, amount, ethAmount, 0); 365 | } 366 | 367 | function rebase() external { 368 | uint256 lastRebaseTime = _lastRebaseTime; 369 | if (0 == lastRebaseTime) { 370 | return; 371 | } 372 | 373 | uint256 nowTime = block.timestamp; 374 | if (nowTime < lastRebaseTime + _rebaseDuration) { 375 | return; 376 | } 377 | 378 | _lastRebaseTime = nowTime; 379 | 380 | uint256 poolBalance = _balances[address(this)]; 381 | uint256 rebaseAmount = (((poolBalance * _rebaseRate) / 10000) * 382 | (nowTime - lastRebaseTime)) / _rebaseDuration; 383 | 384 | if (rebaseAmount > poolBalance / 2) { 385 | rebaseAmount = poolBalance / 2; 386 | } 387 | 388 | if (rebaseAmount > 0) { 389 | _basicTransfer(address(this), address(0xdead), rebaseAmount); 390 | } 391 | } 392 | 393 | receive() external payable { 394 | buy(); 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /contracts/LongSwap.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 5 | 6 | library TransferHelper { 7 | function safeTransfer(address token, address to, uint value) internal { 8 | (bool success, ) = token.call( 9 | abi.encodeWithSelector(0xa9059cbb, to, value) 10 | ); 11 | require(success, "TransferHelper: TRANSFER_FAILED"); 12 | } 13 | 14 | function safeTransferFrom( 15 | address token, 16 | address from, 17 | address to, 18 | uint value 19 | ) internal { 20 | (bool success, ) = token.call( 21 | abi.encodeWithSelector(0x23b872dd, from, to, value) 22 | ); 23 | require(success, "TransferHelper: TRANSFER_FROM_FAILED"); 24 | } 25 | 26 | function safeTransferETH(address to, uint value) internal { 27 | (bool success, ) = to.call{value: value}(new bytes(0)); 28 | require(success, "TransferHelper: ETH_TRANSFER_FAILED"); 29 | } 30 | } 31 | 32 | interface ISwapV2Router { 33 | function factory() external pure returns (address); 34 | 35 | function getAmountOut( 36 | uint amountIn, 37 | uint reserveIn, 38 | uint reserveOut 39 | ) external pure returns (uint amountOut); 40 | 41 | function getAmountIn( 42 | uint amountOut, 43 | uint reserveIn, 44 | uint reserveOut 45 | ) external pure returns (uint amountIn); 46 | 47 | function getAmountsOut( 48 | uint amountIn, 49 | address[] calldata path 50 | ) external view returns (uint[] memory amounts); 51 | 52 | function getAmountsIn( 53 | uint amountOut, 54 | address[] calldata path 55 | ) external view returns (uint[] memory amounts); 56 | } 57 | 58 | interface ISwapV2Factory { 59 | function getPair( 60 | address tokenA, 61 | address tokenB 62 | ) external view returns (address pair); 63 | } 64 | 65 | interface ISwapV2Pair { 66 | function getReserves() 67 | external 68 | view 69 | returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 70 | 71 | function token0() external view returns (address); 72 | 73 | function swap( 74 | uint amount0Out, 75 | uint amount1Out, 76 | address to, 77 | bytes calldata data 78 | ) external; 79 | } 80 | 81 | interface IERC20 { 82 | function balanceOf(address owner) external view returns (uint); 83 | 84 | function transfer(address to, uint value) external returns (bool); 85 | } 86 | 87 | interface IWETH is IERC20 { 88 | function deposit() external payable; 89 | 90 | function withdraw(uint) external; 91 | } 92 | 93 | contract LongSwap { 94 | using SafeMath for uint256; 95 | 96 | modifier ensure(uint deadline) { 97 | require(deadline >= block.timestamp, "LongSwap: EXPIRED"); 98 | _; 99 | } 100 | 101 | receive() external payable {} 102 | 103 | function getAmountsOut( 104 | address _router, 105 | uint256 amountIn, 106 | address[] memory path 107 | ) public view returns (uint[] memory amounts) { 108 | return ISwapV2Router(_router).getAmountsOut(amountIn, path); 109 | } 110 | 111 | function getAmountOut( 112 | address _router, 113 | uint amountIn, 114 | uint reserveIn, 115 | uint reserveOut 116 | ) public pure returns (uint amountOut) { 117 | return 118 | ISwapV2Router(_router).getAmountOut( 119 | amountIn, 120 | reserveIn, 121 | reserveOut 122 | ); 123 | } 124 | 125 | function getAmountsIn( 126 | address _router, 127 | uint amountOut, 128 | address[] memory path 129 | ) public view returns (uint[] memory amounts) { 130 | return ISwapV2Router(_router).getAmountsIn(amountOut, path); 131 | } 132 | 133 | function getPair( 134 | address _router, 135 | address tokenA, 136 | address tokenB 137 | ) public view returns (address pair) { 138 | ISwapV2Factory _factory = ISwapV2Factory( 139 | ISwapV2Router(_router).factory() 140 | ); 141 | return _factory.getPair(tokenA, tokenB); 142 | } 143 | 144 | function getReserves( 145 | address _router, 146 | address tokenA, 147 | address tokenB 148 | ) public view returns (uint256, uint256) { 149 | ISwapV2Pair pair = ISwapV2Pair(getPair(_router, tokenA, tokenB)); 150 | address token0 = pair.token0(); 151 | 152 | (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); 153 | (uint256 reserveIn, uint256 reserveOut) = tokenA == token0 154 | ? (reserve0, reserve1) 155 | : (reserve1, reserve0); 156 | 157 | return (reserveIn, reserveOut); 158 | } 159 | 160 | function sortTokens( 161 | address tokenA, 162 | address tokenB 163 | ) public pure returns (address token0, address token1) { 164 | require(tokenA != tokenB); 165 | (token0, token1) = tokenA < tokenB 166 | ? (tokenA, tokenB) 167 | : (tokenB, tokenA); 168 | require(token0 != address(0)); 169 | } 170 | 171 | function _swap( 172 | address _router, 173 | address[] memory path, 174 | address _to 175 | ) private { 176 | ISwapV2Factory factory = ISwapV2Factory( 177 | ISwapV2Router(_router).factory() 178 | ); 179 | 180 | for (uint256 i; i < path.length - 1; i++) { 181 | (address input, address output) = (path[i], path[i + 1]); 182 | (address token0, ) = sortTokens(input, output); 183 | ISwapV2Pair pair = ISwapV2Pair(factory.getPair(input, output)); 184 | uint256 amountInput; 185 | uint256 amountOutput; 186 | // scope to avoid stack too deep errors 187 | { 188 | (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); 189 | (uint256 reserveInput, uint256 reserveOutput) = input == token0 190 | ? (reserve0, reserve1) 191 | : (reserve1, reserve0); 192 | amountInput = IERC20(input).balanceOf(address(pair)).sub( 193 | reserveInput 194 | ); 195 | amountOutput = getAmountOut( 196 | _router, 197 | amountInput, 198 | reserveInput, 199 | reserveOutput 200 | ); 201 | } 202 | (uint256 amount0Out, uint256 amount1Out) = input == token0 203 | ? (uint256(0), amountOutput) 204 | : (amountOutput, uint256(0)); 205 | address to = i < path.length - 2 206 | ? factory.getPair(output, path[i + 2]) 207 | : _to; 208 | pair.swap(amount0Out, amount1Out, to, new bytes(0)); 209 | } 210 | } 211 | 212 | function checkSwap( 213 | address _router, 214 | address[] memory path, 215 | uint256 amount, 216 | uint256 amountOutMin, 217 | uint256 taxBuy, 218 | uint256 taxSell 219 | ) public virtual { 220 | address srcToken = path[0]; 221 | address dstToken = path[path.length - 1]; 222 | 223 | uint256 beforeBal = IERC20(dstToken).balanceOf(msg.sender); 224 | 225 | safeSwap( 226 | _router, 227 | path, 228 | amount, 229 | amountOutMin, 230 | taxBuy, 231 | msg.sender, 232 | block.timestamp 233 | ); 234 | 235 | uint256 afterBal = IERC20(dstToken).balanceOf(msg.sender) - beforeBal; 236 | 237 | path[0] = dstToken; 238 | path[path.length - 1] = srcToken; 239 | 240 | safeSwap( 241 | _router, 242 | path, 243 | afterBal.div(100), 244 | 0, 245 | taxSell, 246 | msg.sender, 247 | block.timestamp 248 | ); 249 | } 250 | 251 | function checkSwapETHForToken( 252 | address _router, 253 | address[] memory path, 254 | uint256 amountOutMin, 255 | uint256 taxBuy, 256 | uint256 taxSell 257 | ) public payable virtual { 258 | address srcToken = path[0]; 259 | address dstToken = path[path.length - 1]; 260 | 261 | uint256 beforeBal = IERC20(dstToken).balanceOf(msg.sender); 262 | 263 | safeSwapETHForToken( 264 | _router, 265 | path, 266 | amountOutMin, 267 | taxBuy, 268 | msg.sender, 269 | block.timestamp 270 | ); 271 | 272 | uint256 afterBal = IERC20(dstToken).balanceOf(msg.sender) - beforeBal; 273 | 274 | path[0] = dstToken; 275 | path[path.length - 1] = srcToken; 276 | 277 | safeSwap( 278 | _router, 279 | path, 280 | afterBal.div(100), 281 | 0, 282 | taxSell, 283 | msg.sender, 284 | block.timestamp 285 | ); 286 | } 287 | 288 | function safeSwap( 289 | address _router, 290 | address[] memory path, 291 | uint256 amountIn, 292 | uint256 amountOutMin, 293 | uint256 tax, 294 | address to, 295 | uint256 deadline 296 | ) public virtual ensure(deadline) { 297 | address srcToken = path[0]; 298 | bool hasPaid; 299 | 300 | if (amountIn == 0) { 301 | hasPaid = true; 302 | amountIn = IERC20(srcToken).balanceOf(address(this)); 303 | } 304 | 305 | pay( 306 | srcToken, 307 | hasPaid ? address(this) : msg.sender, 308 | getPair(_router, srcToken, path[1]), 309 | amountIn 310 | ); 311 | 312 | uint amountOut = getAmountsOut(_router, amountIn, path)[ 313 | path.length - 1 314 | ]; 315 | require(amountOut >= amountOutMin, "LongSwap: Slippage is too low"); 316 | 317 | IERC20 dstToken = IERC20(path[path.length - 1]); 318 | uint256 beforeBal = dstToken.balanceOf(to); 319 | 320 | _swap(_router, path, to); 321 | 322 | uint256 afterBal = dstToken.balanceOf(to); 323 | 324 | uint256 checkAmountOut = amountOut.sub(amountOut.mul(tax).div(1000)); 325 | 326 | require( 327 | afterBal.sub(beforeBal) >= checkAmountOut, 328 | "LongSwap: Taxes are too low" 329 | ); 330 | } 331 | 332 | function safeSwapETHForToken( 333 | address _router, 334 | address[] memory path, 335 | uint256 amountOutMin, 336 | uint256 tax, 337 | address to, 338 | uint256 deadline 339 | ) public payable virtual { 340 | IWETH wrapETH = IWETH(path[0]); 341 | wrapETH.deposit{value: msg.value}(); 342 | safeSwap(_router, path, 0, amountOutMin, tax, to, deadline); 343 | } 344 | 345 | function safeSwapTokenForETH( 346 | address _router, 347 | address[] memory path, 348 | uint256 amountIn, 349 | uint256 amountOutMin, 350 | uint256 tax, 351 | address to, 352 | uint256 deadline 353 | ) public virtual { 354 | safeSwap( 355 | _router, 356 | path, 357 | amountIn, 358 | amountOutMin, 359 | tax, 360 | address(this), 361 | deadline 362 | ); 363 | 364 | IWETH wrapETH = IWETH(path[path.length - 1]); 365 | uint256 balance = wrapETH.balanceOf(address(this)); 366 | wrapETH.withdraw(balance); 367 | TransferHelper.safeTransferETH(to, balance); 368 | } 369 | 370 | function safeSwapAndTransfer( 371 | address _router, 372 | address[] memory path, 373 | uint256 amountIn, 374 | uint256 amountOutMin, 375 | uint256 tax, 376 | address to, 377 | uint256 deadline 378 | ) public virtual { 379 | safeSwap(_router, path, amountIn, amountOutMin, tax, to, deadline); 380 | 381 | TransferHelper.safeTransferFrom( 382 | path[path.length - 1], 383 | to, 384 | address(0xdead), 385 | 1 386 | ); 387 | } 388 | 389 | function safeSwapETHForTokenAndTransfer( 390 | address _router, 391 | address[] memory path, 392 | uint256 amountOutMin, 393 | uint256 tax, 394 | address to, 395 | uint256 deadline 396 | ) public payable virtual { 397 | safeSwapETHForToken(_router, path, amountOutMin, tax, to, deadline); 398 | 399 | TransferHelper.safeTransferFrom( 400 | path[path.length - 1], 401 | to, 402 | address(0xdead), 403 | 1 404 | ); 405 | } 406 | 407 | function pay( 408 | address token, 409 | address payer, 410 | address recipient, 411 | uint256 value 412 | ) internal { 413 | if (payer == address(this)) { 414 | TransferHelper.safeTransfer(token, recipient, value); 415 | } else { 416 | TransferHelper.safeTransferFrom(token, payer, recipient, value); 417 | } 418 | } 419 | } 420 | -------------------------------------------------------------------------------- /contracts/Staking.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 8 | import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; 9 | 10 | import "./libs/Linear.sol"; 11 | import "./interfaces/IERC314.sol"; 12 | 13 | contract Staking is Initializable, UUPSUpgradeable, OwnableUpgradeable { 14 | using SafeERC20 for IERC20; 15 | using Linear for Linear.Account; 16 | 17 | IERC20 public x314; 18 | 19 | uint256 public duration; 20 | mapping(address => Linear.Account) public accountOf; 21 | 22 | IERC20 public usdt; 23 | bool public lotteryEnable; 24 | mapping(address => mapping(uint256 => bool)) public lotteryOf; 25 | 26 | function initialize(IERC20 _x314) public initializer { 27 | x314 = _x314; 28 | duration = 30 days; 29 | 30 | __Ownable_init(); 31 | __UUPSUpgradeable_init(); 32 | } 33 | 34 | function _authorizeUpgrade(address) internal override onlyOwner {} 35 | 36 | function deposit(uint256 amount) public { 37 | require(amount > 0, "Amount error"); 38 | _deposit(msg.sender, amount); 39 | } 40 | 41 | function depositByReferrer(uint256 amount, address referrer) public { 42 | require(amount > 0, "Amount error"); 43 | _deposit(msg.sender, amount); 44 | 45 | if (referrer != address(0) && referrer != msg.sender) { 46 | accountOf[referrer].add( 47 | (amount * 10) / 100, 48 | block.timestamp, 49 | duration 50 | ); 51 | } 52 | } 53 | 54 | function _deposit(address user, uint256 amount) internal { 55 | _unlock(); 56 | x314.safeTransferFrom(user, address(this), amount); 57 | accountOf[user].add(amount * 2, block.timestamp, duration); 58 | } 59 | 60 | function claim() public { 61 | uint256 amount = accountOf[msg.sender].release(); 62 | if (amount > 0) { 63 | _unlock(); 64 | _safeTransfer(msg.sender, amount); 65 | } 66 | } 67 | 68 | function tryLottery() public { 69 | require(lotteryEnable, "Not open"); 70 | 71 | uint256 today = block.timestamp / 86400; 72 | require(!lotteryOf[msg.sender][today], "Has Lottery"); 73 | 74 | lotteryOf[msg.sender][today] = true; 75 | 76 | uint256 randomness = _generateRandomNumber(); 77 | uint256 rewardChance = randomness % 100; 78 | 79 | if (rewardChance < 5) { 80 | if (accountOf[msg.sender].total >= 100000 * 1e18) { 81 | usdt.safeTransfer(msg.sender, 100 * 1e18); 82 | } else if (accountOf[msg.sender].total >= 10000 * 1e18) { 83 | usdt.safeTransfer(msg.sender, 20 * 1e18); 84 | } 85 | } 86 | } 87 | 88 | function pending(address user) public view returns (uint256) { 89 | return accountOf[user].pendingRelease(); 90 | } 91 | 92 | function hasLotteryToday(address user) public view returns (bool) { 93 | uint256 today = block.timestamp / 86400; 94 | return lotteryOf[user][today]; 95 | } 96 | 97 | function _generateRandomNumber() public view returns (uint256) { 98 | return 99 | uint256( 100 | keccak256( 101 | abi.encodePacked( 102 | block.timestamp, 103 | msg.sender, 104 | block.coinbase, 105 | x314.balanceOf(address(this)), 106 | block.number 107 | ) 108 | ) 109 | ); 110 | } 111 | 112 | function _safeTransfer(address to, uint256 amount) internal { 113 | x314.safeTransfer(to, amount); 114 | } 115 | 116 | function setUsdt(IERC20 _usdt) public onlyOwner { 117 | usdt = _usdt; 118 | } 119 | 120 | function setLotteryEnable(bool _lotteryEnable) public onlyOwner { 121 | lotteryEnable = _lotteryEnable; 122 | } 123 | 124 | function _unlock() internal { 125 | address[] memory accounts = new address[](1); 126 | accounts[0] = address(this); 127 | IERC314(address(x314)).setLastTransaction(accounts, 0); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /contracts/X314.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | import "./ERC314.sol"; 5 | 6 | contract X314 is ERC314 { 7 | uint256 private _totalSupply = 21_000_000 * 10 ** 18; 8 | 9 | constructor() ERC314("X-314", "X314", _totalSupply, 20 * 2, 2, 2) {} 10 | } 11 | -------------------------------------------------------------------------------- /contracts/interfaces/IERC314.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | interface IERC314 { 5 | function setLastTransaction( 6 | address[] memory accounts, 7 | uint32 _block 8 | ) external; 9 | } 10 | -------------------------------------------------------------------------------- /contracts/libs/Linear.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 6 | 7 | library Linear { 8 | using SafeMath for uint256; 9 | 10 | struct Account { 11 | uint256 total; 12 | uint256 released; 13 | uint256 relay; 14 | uint256 lastRelease; 15 | uint256 duration; 16 | } 17 | 18 | function add( 19 | Account storage a, 20 | uint256 value, 21 | uint256 startTimestamp, 22 | uint256 duration 23 | ) internal { 24 | if (a.duration == 0) { 25 | a.duration = duration; 26 | } 27 | if (a.lastRelease == 0) { 28 | a.lastRelease = startTimestamp; 29 | } 30 | 31 | uint256 _pending = pending(a); 32 | 33 | if (_pending > 0) { 34 | a.lastRelease = block.timestamp; 35 | a.relay = a.relay.add(_pending); 36 | } 37 | 38 | a.total = a.total.add(value); 39 | } 40 | 41 | function release(Account storage a) internal returns (uint256) { 42 | if (a.total == 0) { 43 | return 0; 44 | } 45 | 46 | uint256 _pending = pending(a); 47 | uint256 paid = a.relay.add(_pending); 48 | 49 | if (paid > 0) { 50 | a.relay = 0; 51 | a.lastRelease = block.timestamp; 52 | a.released = a.released.add(paid); 53 | } 54 | 55 | return paid; 56 | } 57 | 58 | function locked(Account storage a) internal view returns (uint256) { 59 | uint256 _pending = pending(a); 60 | return a.total.sub(a.released).sub(a.relay).sub(_pending); 61 | } 62 | 63 | function pendingRelease(Account storage a) internal view returns (uint256) { 64 | uint256 _pending = pending(a); 65 | return a.relay.add(_pending); 66 | } 67 | 68 | function pending(Account storage a) internal view returns (uint256) { 69 | if (a.lastRelease > block.timestamp || a.duration == 0) { 70 | return 0; 71 | } 72 | 73 | uint256 totalSec = block.timestamp.sub(a.lastRelease); 74 | uint256 paid = a.total.div(a.duration).mul(totalSec); 75 | 76 | if (a.released.add(a.relay) > 0) { 77 | paid = paid.sub( 78 | a.released.add(a.relay).div(a.duration).mul(totalSec) 79 | ); 80 | } 81 | 82 | uint256 amount = a.released.add(a.relay).add(paid); 83 | 84 | if (amount > a.total) { 85 | return paid.sub(amount.sub(a.total)); 86 | } 87 | 88 | return paid; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hardhat-project", 3 | "devDependencies": { 4 | "@nomicfoundation/hardhat-chai-matchers": "^2.0.2", 5 | "@nomicfoundation/hardhat-ethers": "^3.0.5", 6 | "@nomicfoundation/hardhat-network-helpers": "^1.0.10", 7 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 8 | "@nomicfoundation/hardhat-verify": "^2.0.3", 9 | "@openzeppelin/contracts": "^4.9.5", 10 | "@openzeppelin/contracts-upgradeable": "^4.9.5", 11 | "@typechain/ethers-v6": "^0.5.1", 12 | "@typechain/hardhat": "^9.1.0", 13 | "chai": "^4.3.10", 14 | "ethers": "^6.9.0", 15 | "hardhat": "^2.19.3", 16 | "hardhat-gas-reporter": "^1.0.9", 17 | "solidity-coverage": "^0.8.5", 18 | "typechain": "^8.3.2" 19 | } 20 | } -------------------------------------------------------------------------------- /scripts/addLiquidity.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node