Initial commit
This commit is contained in:
commit
843daad632
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
# directories
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/releases
|
||||
!.yarn/plugins
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
**/artifacts
|
||||
**/build
|
||||
**/cache
|
||||
**/coverage
|
||||
**/.coverage_artifacts
|
||||
**/.coverage_cache
|
||||
**/.coverage_contracts
|
||||
**/dist
|
||||
**/node_modules
|
||||
.idea/*
|
||||
|
||||
# files
|
||||
*.env
|
||||
*.log
|
||||
.pnp.*
|
||||
coverage.json
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# types
|
||||
src/types/*
|
||||
!src/types/common.ts
|
||||
!src/types/Greeter.ts
|
||||
|
||||
# factories
|
||||
!src/types/factories
|
||||
src/types/factories/*
|
||||
!src/types/factories/Greeter__factory.ts
|
||||
|
||||
typechain/*
|
||||
.vscode/
|
6
contracts/furnace.sol
Normal file
6
contracts/furnace.sol
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
contract RedFurnace {}
|
7
contracts/interfaces/INFT.sol
Normal file
7
contracts/interfaces/INFT.sol
Normal file
@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
interface INFT {
|
||||
function raiseRewardPool(uint256 amount) external;
|
||||
}
|
316
contracts/interfaces/IPancake.sol
Normal file
316
contracts/interfaces/IPancake.sol
Normal file
@ -0,0 +1,316 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
interface IPancakeSwapPair {
|
||||
event Approval(address indexed owner, address indexed spender, uint256 value);
|
||||
event Transfer(address indexed from, address indexed to, uint256 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 (uint256);
|
||||
|
||||
function balanceOf(address owner) external view returns (uint256);
|
||||
|
||||
function allowance(address owner, address spender) external view returns (uint256);
|
||||
|
||||
function approve(address spender, uint256 value) external returns (bool);
|
||||
|
||||
function transfer(address to, uint256 value) external returns (bool);
|
||||
|
||||
function transferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 value
|
||||
) external returns (bool);
|
||||
|
||||
function DOMAIN_SEPARATOR() external view returns (bytes32);
|
||||
|
||||
function PERMIT_TYPEHASH() external pure returns (bytes32);
|
||||
|
||||
function nonces(address owner) external view returns (uint256);
|
||||
|
||||
function permit(
|
||||
address owner,
|
||||
address spender,
|
||||
uint256 value,
|
||||
uint256 deadline,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external;
|
||||
|
||||
event Mint(address indexed sender, uint256 amount0, uint256 amount1);
|
||||
event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to);
|
||||
event Swap(
|
||||
address indexed sender,
|
||||
uint256 amount0In,
|
||||
uint256 amount1In,
|
||||
uint256 amount0Out,
|
||||
uint256 amount1Out,
|
||||
address indexed to
|
||||
);
|
||||
event Sync(uint112 reserve0, uint112 reserve1);
|
||||
|
||||
function MINIMUM_LIQUIDITY() external pure returns (uint256);
|
||||
|
||||
function factory() external view returns (address);
|
||||
|
||||
function token0() external view returns (address);
|
||||
|
||||
function token1() external view returns (address);
|
||||
|
||||
function getReserves()
|
||||
external
|
||||
view
|
||||
returns (
|
||||
uint112 reserve0,
|
||||
uint112 reserve1,
|
||||
uint32 blockTimestampLast
|
||||
);
|
||||
|
||||
function price0CumulativeLast() external view returns (uint256);
|
||||
|
||||
function price1CumulativeLast() external view returns (uint256);
|
||||
|
||||
function kLast() external view returns (uint256);
|
||||
|
||||
function mint(address to) external returns (uint256 liquidity);
|
||||
|
||||
function burn(address to) external returns (uint256 amount0, uint256 amount1);
|
||||
|
||||
function swap(
|
||||
uint256 amount0Out,
|
||||
uint256 amount1Out,
|
||||
address to,
|
||||
bytes calldata data
|
||||
) external;
|
||||
|
||||
function skim(address to) external;
|
||||
|
||||
function sync() external;
|
||||
|
||||
function initialize(address, address) external;
|
||||
}
|
||||
|
||||
interface IPancakeSwapRouter {
|
||||
function factory() external pure returns (address);
|
||||
|
||||
function WETH() external pure returns (address);
|
||||
|
||||
function addLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 amountADesired,
|
||||
uint256 amountBDesired,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
returns (
|
||||
uint256 amountA,
|
||||
uint256 amountB,
|
||||
uint256 liquidity
|
||||
);
|
||||
|
||||
function addLiquidityETH(
|
||||
address token,
|
||||
uint256 amountTokenDesired,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
)
|
||||
external
|
||||
payable
|
||||
returns (
|
||||
uint256 amountToken,
|
||||
uint256 amountETH,
|
||||
uint256 liquidity
|
||||
);
|
||||
|
||||
function removeLiquidity(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 liquidity,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountA, uint256 amountB);
|
||||
|
||||
function removeLiquidityETH(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountToken, uint256 amountETH);
|
||||
|
||||
function removeLiquidityWithPermit(
|
||||
address tokenA,
|
||||
address tokenB,
|
||||
uint256 liquidity,
|
||||
uint256 amountAMin,
|
||||
uint256 amountBMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountA, uint256 amountB);
|
||||
|
||||
function removeLiquidityETHWithPermit(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountToken, uint256 amountETH);
|
||||
|
||||
function swapExactTokensForTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapTokensForExactTokens(
|
||||
uint256 amountOut,
|
||||
uint256 amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapExactETHForTokens(
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256[] memory amounts);
|
||||
|
||||
function swapTokensForExactETH(
|
||||
uint256 amountOut,
|
||||
uint256 amountInMax,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapExactTokensForETH(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256[] memory amounts);
|
||||
|
||||
function swapETHForExactTokens(
|
||||
uint256 amountOut,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable returns (uint256[] memory amounts);
|
||||
|
||||
function quote(
|
||||
uint256 amountA,
|
||||
uint256 reserveA,
|
||||
uint256 reserveB
|
||||
) external pure returns (uint256 amountB);
|
||||
|
||||
function getAmountOut(
|
||||
uint256 amountIn,
|
||||
uint256 reserveIn,
|
||||
uint256 reserveOut
|
||||
) external pure returns (uint256 amountOut);
|
||||
|
||||
function getAmountIn(
|
||||
uint256 amountOut,
|
||||
uint256 reserveIn,
|
||||
uint256 reserveOut
|
||||
) external pure returns (uint256 amountIn);
|
||||
|
||||
function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts);
|
||||
|
||||
function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts);
|
||||
|
||||
function removeLiquidityETHSupportingFeeOnTransferTokens(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external returns (uint256 amountETH);
|
||||
|
||||
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
|
||||
address token,
|
||||
uint256 liquidity,
|
||||
uint256 amountTokenMin,
|
||||
uint256 amountETHMin,
|
||||
address to,
|
||||
uint256 deadline,
|
||||
bool approveMax,
|
||||
uint8 v,
|
||||
bytes32 r,
|
||||
bytes32 s
|
||||
) external returns (uint256 amountETH);
|
||||
|
||||
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external;
|
||||
|
||||
function swapExactETHForTokensSupportingFeeOnTransferTokens(
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external payable;
|
||||
|
||||
function swapExactTokensForETHSupportingFeeOnTransferTokens(
|
||||
uint256 amountIn,
|
||||
uint256 amountOutMin,
|
||||
address[] calldata path,
|
||||
address to,
|
||||
uint256 deadline
|
||||
) external;
|
||||
}
|
||||
|
||||
interface IPancakeSwapFactory {
|
||||
event PairCreated(address indexed token0, address indexed token1, address pair, uint256);
|
||||
|
||||
function feeTo() external view returns (address);
|
||||
|
||||
function feeToSetter() external view returns (address);
|
||||
|
||||
function getPair(address tokenA, address tokenB) external view returns (address pair);
|
||||
|
||||
function allPairs(uint256) external view returns (address pair);
|
||||
|
||||
function allPairsLength() external view returns (uint256);
|
||||
|
||||
function createPair(address tokenA, address tokenB) external returns (address pair);
|
||||
|
||||
function setFeeTo(address) external;
|
||||
|
||||
function setFeeToSetter(address) external;
|
||||
}
|
8
contracts/interfaces/ITreasury.sol
Normal file
8
contracts/interfaces/ITreasury.sol
Normal file
@ -0,0 +1,8 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
interface ITreasury {
|
||||
function isAdmin(address who) external returns (bool);
|
||||
function isOperator(address who) external returns (bool);
|
||||
}
|
148
contracts/market.sol
Normal file
148
contracts/market.sol
Normal file
@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
//mostly done
|
||||
//смерть русні
|
||||
pragma solidity ^0.8.14;
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
import "./interfaces/ITreasury.sol";
|
||||
|
||||
contract Marketplace is Initializable {
|
||||
//variables
|
||||
uint256 currentSaleId;
|
||||
uint256 fee;
|
||||
ITreasury treasury;
|
||||
IERC721Upgradeable nft;
|
||||
IERC20Upgradeable BoM;
|
||||
struct Sale {
|
||||
address payable seller;
|
||||
uint256 tokenId;
|
||||
uint256 price;
|
||||
}
|
||||
mapping(uint256 => Sale) saleIdToSale;
|
||||
|
||||
//events
|
||||
event SaleCreated(
|
||||
uint256 indexed saleId,
|
||||
address indexed seller,
|
||||
uint256 price,
|
||||
uint256 indexed tokenId
|
||||
);
|
||||
|
||||
event SaleCancelled(uint256 indexed saleId);
|
||||
|
||||
event SaleUpdated(
|
||||
uint256 indexed saleId,
|
||||
uint256 oldPrice,
|
||||
uint256 newPrice
|
||||
);
|
||||
|
||||
event TokenBought(
|
||||
uint256 indexed saleId,
|
||||
address indexed buyer,
|
||||
uint256 indexed tokenId,
|
||||
uint256 price
|
||||
);
|
||||
|
||||
event FeesChanged(address indexed admin, uint256 oldFee, uint256 newFee);
|
||||
|
||||
//modifiers
|
||||
modifier isExistingSale(uint256 saleId) {
|
||||
require(saleIdToSale[saleId].price != 0);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlySaleOwner(uint256 saleId) {
|
||||
require(saleIdToSale[saleId].seller == msg.sender);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyValidPrice(uint256 price) {
|
||||
require(price > 0);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(treasury.isAdmin(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
// constructor(
|
||||
// uint256 _fee,
|
||||
// ITreasury _treasury,
|
||||
// IERC721Upgradeable _nft,
|
||||
// IERC20Upgradeable token
|
||||
// ) {
|
||||
// fee = _fee;
|
||||
// treasury = _treasury;
|
||||
// nft = _nft;
|
||||
// BoM = token;
|
||||
// }
|
||||
|
||||
function initialize(
|
||||
uint256 _fee,
|
||||
ITreasury _treasury,
|
||||
IERC721Upgradeable _nft,
|
||||
IERC20Upgradeable token
|
||||
) public initializer {
|
||||
fee = _fee;
|
||||
treasury = _treasury;
|
||||
nft = _nft;
|
||||
BoM = token;
|
||||
}
|
||||
|
||||
//internal functions
|
||||
function _distributeFunds(uint256 amount, address seller) internal {
|
||||
uint256 _fee = (amount * fee) / 10000;
|
||||
uint256 goesToSeller = amount - _fee;
|
||||
BoM.transferFrom(msg.sender, address(this), _fee);
|
||||
BoM.transferFrom(msg.sender, seller, goesToSeller);
|
||||
}
|
||||
|
||||
//external functions
|
||||
function createSale(uint256 tokenId, uint256 price)
|
||||
public
|
||||
onlyValidPrice(price)
|
||||
{
|
||||
currentSaleId++;
|
||||
Sale memory sale = saleIdToSale[currentSaleId];
|
||||
sale.seller = payable(msg.sender);
|
||||
sale.price = price;
|
||||
sale.tokenId = tokenId;
|
||||
nft.transferFrom(msg.sender, address(this), tokenId);
|
||||
emit SaleCreated(currentSaleId, msg.sender, price, tokenId);
|
||||
}
|
||||
|
||||
function cancelSale(uint256 saleId) public onlySaleOwner(saleId) {
|
||||
Sale memory sale = saleIdToSale[saleId];
|
||||
nft.transferFrom(address(this), msg.sender, sale.tokenId);
|
||||
delete sale;
|
||||
emit SaleCancelled(saleId);
|
||||
}
|
||||
|
||||
function updateSale(uint256 saleId, uint256 price)
|
||||
public
|
||||
onlySaleOwner(saleId)
|
||||
onlyValidPrice(price)
|
||||
isExistingSale(saleId)
|
||||
{
|
||||
Sale memory sale = saleIdToSale[saleId];
|
||||
uint256 oldPrice = sale.price;
|
||||
sale.price = price;
|
||||
emit SaleUpdated(saleId, oldPrice, price);
|
||||
}
|
||||
|
||||
function buyToken(uint256 saleId) public isExistingSale(saleId) {
|
||||
Sale memory sale = saleIdToSale[saleId];
|
||||
nft.transferFrom(address(this), msg.sender, sale.tokenId);
|
||||
_distributeFunds(sale.price, sale.seller);
|
||||
emit TokenBought(saleId, msg.sender, sale.tokenId, sale.price);
|
||||
}
|
||||
|
||||
//admin functions
|
||||
function changeFees(uint256 _fee) public onlyAdmin {
|
||||
require(_fee > 0 && _fee < 2000);
|
||||
emit FeesChanged(msg.sender, fee, _fee);
|
||||
fee = _fee;
|
||||
}
|
||||
}
|
102
contracts/mixins/SafeMathInt.sol
Normal file
102
contracts/mixins/SafeMathInt.sol
Normal file
@ -0,0 +1,102 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
pragma solidity ^0.8.15;
|
||||
import "@openzeppelin/contracts-upgradeable/utils/math/SafeMathUpgradeable.sol";
|
||||
|
||||
/**
|
||||
* @title SafeMathInt
|
||||
* @dev Math operations for int256 with overflow safety checks.
|
||||
*/
|
||||
library SafeMathInt {
|
||||
int256 private constant MIN_INT256 = int256(1) << 255;
|
||||
int256 private constant MAX_INT256 = ~(int256(1) << 255);
|
||||
|
||||
/**
|
||||
* @dev Multiplies two int256 variables and fails on overflow.
|
||||
*/
|
||||
function mul(int256 a, int256 b) internal pure returns (int256) {
|
||||
int256 c = a * b;
|
||||
|
||||
// Detect overflow when multiplying MIN_INT256 with -1
|
||||
require(c != MIN_INT256 || (a & MIN_INT256) != (b & MIN_INT256));
|
||||
require((b == 0) || (c / b == a));
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Division of two int256 variables and fails on overflow.
|
||||
*/
|
||||
function div(int256 a, int256 b) internal pure returns (int256) {
|
||||
// Prevent overflow when dividing MIN_INT256 by -1
|
||||
require(b != -1 || a != MIN_INT256);
|
||||
|
||||
// Solidity already throws when dividing by 0.
|
||||
return a / b;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Subtracts two int256 variables and fails on overflow.
|
||||
*/
|
||||
function sub(int256 a, int256 b) internal pure returns (int256) {
|
||||
int256 c = a - b;
|
||||
require((b >= 0 && c <= a) || (b < 0 && c > a));
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Adds two int256 variables and fails on overflow.
|
||||
*/
|
||||
function add(int256 a, int256 b) internal pure returns (int256) {
|
||||
int256 c = a + b;
|
||||
require((b >= 0 && c >= a) || (b < 0 && c < a));
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Converts to absolute value, and fails on overflow.
|
||||
*/
|
||||
function abs(int256 a) internal pure returns (int256) {
|
||||
require(a != MIN_INT256);
|
||||
return a < 0 ? -a : a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Computes 2^exp with limited precision where -100 <= exp <= 100 * one
|
||||
* @param one 1.0 represented in the same fixed point number format as exp
|
||||
* @param exp The power to raise 2 to -100 <= exp <= 100 * one
|
||||
* @return 2^exp represented with same number of decimals after the point as one
|
||||
*/
|
||||
function twoPower(int256 exp, int256 one) internal pure returns (int256) {
|
||||
bool reciprocal = false;
|
||||
if (exp < 0) {
|
||||
reciprocal = true;
|
||||
exp = abs(exp);
|
||||
}
|
||||
|
||||
// Precomputed values for 2^(1/2^i) in 18 decimals fixed point numbers
|
||||
int256[5] memory ks = [
|
||||
int256(1414213562373095049),
|
||||
1189207115002721067,
|
||||
1090507732665257659,
|
||||
1044273782427413840,
|
||||
1021897148654116678
|
||||
];
|
||||
int256 whole = div(exp, one);
|
||||
require(whole <= 100);
|
||||
int256 result = mul(int256(uint256(1) << uint256(whole)), one);
|
||||
int256 remaining = sub(exp, mul(whole, one));
|
||||
|
||||
int256 current = div(one, 2);
|
||||
for (uint256 i = 0; i < 5; i++) {
|
||||
if (remaining >= current) {
|
||||
remaining = sub(remaining, current);
|
||||
result = div(mul(result, ks[i]), 10**18); // 10**18 to match hardcoded ks values
|
||||
}
|
||||
current = div(current, 2);
|
||||
}
|
||||
if (reciprocal) {
|
||||
result = div(mul(one, one), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
40
contracts/mixins/role-control.sol
Normal file
40
contracts/mixins/role-control.sol
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
|
||||
|
||||
abstract contract RoleControl is AccessControlUpgradeable {
|
||||
|
||||
bytes32 OPERATOR_ROLE = bytes32("OPERATOR_ROLE");
|
||||
|
||||
function isAdmin(address account) public view returns(bool) {
|
||||
return hasRole(DEFAULT_ADMIN_ROLE, account);
|
||||
}
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(isAdmin(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
function grantOperator(address account) public onlyAdmin {
|
||||
grantRole(OPERATOR_ROLE, account);
|
||||
}
|
||||
|
||||
function isOperator(address account) public view returns(bool) {
|
||||
return hasRole(OPERATOR_ROLE, account);
|
||||
}
|
||||
|
||||
modifier onlyOperator() {
|
||||
require(isAdmin(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
function grantAdminRole(address account) public onlyAdmin {
|
||||
grantRole(DEFAULT_ADMIN_ROLE, account);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
31
contracts/mixins/signature-control.sol
Normal file
31
contracts/mixins/signature-control.sol
Normal file
@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
|
||||
|
||||
contract SignatureControl {
|
||||
function _toEthSignedMessage(bytes memory message) internal pure returns (bytes32) {
|
||||
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(message.length), message));
|
||||
}
|
||||
|
||||
function _toAsciiString(address x) internal pure returns (string memory) {
|
||||
bytes memory s = new bytes(42);
|
||||
s[0] = "0";
|
||||
s[1] = "x";
|
||||
for (uint256 i = 0; i < 20; i++) {
|
||||
bytes1 b = bytes1(uint8(uint256(uint160(x)) / (2**(8 * (19 - i)))));
|
||||
bytes1 hi = bytes1(uint8(b) / 16);
|
||||
bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
|
||||
s[2 * i + 2] = _char(hi);
|
||||
s[2 * i + 3] = _char(lo);
|
||||
}
|
||||
return string(s);
|
||||
}
|
||||
|
||||
function _char(bytes1 b) private pure returns (bytes1 c) {
|
||||
if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
|
||||
else return bytes1(uint8(b) + 0x57);
|
||||
}
|
||||
}
|
711
contracts/nft.sol
Normal file
711
contracts/nft.sol
Normal file
@ -0,0 +1,711 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/Base64Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol";
|
||||
import "./interfaces/ITreasury.sol";
|
||||
import "./interfaces/IPancake.sol";
|
||||
import "./mixins/signature-control.sol";
|
||||
|
||||
contract NFT is ERC721EnumerableUpgradeable, SignatureControl {
|
||||
using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet;
|
||||
using SafeERC20Upgradeable for IERC20Upgradeable;
|
||||
|
||||
//variables
|
||||
bool pause = true;
|
||||
uint256 nonce;
|
||||
ITreasury treasury;
|
||||
IERC20Upgradeable BoM;
|
||||
address redTrustFund;
|
||||
IPancakeSwapPair public pairContract;
|
||||
IPancakeSwapRouter public swapRouter;
|
||||
|
||||
uint256 treasuryFee;
|
||||
uint256 redTrustFee;
|
||||
uint256 rewardPoolFee;
|
||||
|
||||
uint256 supply;
|
||||
|
||||
uint256 wethInRewardPool;
|
||||
uint256 busdInLotteryPool;
|
||||
|
||||
mapping(uint256 => uint256) mintCapOfToken;
|
||||
mapping(uint256 => address[]) tokenIdToType;
|
||||
mapping(address => mapping(uint256 => uint256)) tokensOwnedOfType;
|
||||
mapping(uint256 => bool) usedNonces;
|
||||
|
||||
//mint prices, caps, addresses of reward tokens(shiba,floki,doggy,doge)
|
||||
uint256[4] prices = [
|
||||
300 * 10**18,
|
||||
250 * 10**18,
|
||||
200 * 10**18,
|
||||
250 * 10**18
|
||||
];
|
||||
IERC20Upgradeable BUSD;
|
||||
IERC20Upgradeable WETH;
|
||||
mapping(address => uint256) tokenToPrices;
|
||||
uint256[4] nftCaps = [1000, 1500, 2000, 1500];
|
||||
address[4] public rewardTokens = [
|
||||
0x0000000000000000000000000000000000000000,
|
||||
0x0000000000000000000000000000000000000000,
|
||||
0x0000000000000000000000000000000000000000,
|
||||
0x0000000000000000000000000000000000000000
|
||||
];
|
||||
mapping(address => uint256) addressToType;
|
||||
mapping(address => uint256) totalRatesForType;
|
||||
mapping(address => mapping(address => uint256)) addrerssToRatesForType;
|
||||
|
||||
uint256[5] rewardPoolForToken;
|
||||
uint256[5] rewardsPerShareStored;
|
||||
uint256[5] lastUpdatePools;
|
||||
mapping(address => uint256[5]) accountShares;
|
||||
uint256[5] totalShares;
|
||||
mapping(address => mapping(uint256 => uint256)) accountRewards;
|
||||
mapping(address => mapping(uint256 => uint256)) accountRewardsPerTokenPaid;
|
||||
|
||||
//whitelist shit
|
||||
bool public isPresale = true;
|
||||
uint256 whitelistPrice = 200 ether;
|
||||
uint256 maxMintPerAddressDuringWhitelist = 5;
|
||||
uint256 whitelistMintCapPerType = 250;
|
||||
mapping(address => bool) isWhitelisted;
|
||||
mapping(address => uint256) mintedOnWhitelist;
|
||||
mapping(uint256 => uint256) whitelistMintCapOfToken;
|
||||
|
||||
EnumerableSetUpgradeable.AddressSet _holders; // TODO: migrate?
|
||||
|
||||
uint256 statMultiplier = 100000;
|
||||
mapping(uint256 => TokenInfo) tokenIdToInfo;
|
||||
struct TokenInfo {
|
||||
string image;
|
||||
address[] tokens;
|
||||
uint256 rate;
|
||||
uint256 attack;
|
||||
uint256 defense;
|
||||
uint256 health;
|
||||
uint256 critChance;
|
||||
uint256 critDmg;
|
||||
uint256 recover;
|
||||
}
|
||||
|
||||
mapping(address => Tokenomic) addressToToken;
|
||||
struct Tokenomic {
|
||||
uint256 stakingRate;
|
||||
uint256 attack;
|
||||
uint256 defense;
|
||||
uint256 health;
|
||||
uint256 critChance;
|
||||
uint256 critDmg;
|
||||
uint256 recovery;
|
||||
}
|
||||
|
||||
event TokenMinted(
|
||||
address indexed minter,
|
||||
uint256 tokenId,
|
||||
address token,
|
||||
uint256 rate
|
||||
);
|
||||
|
||||
event TokenCombined(
|
||||
address indexed user,
|
||||
uint256[] burntTokens,
|
||||
address[] tokens,
|
||||
uint256 tokenId,
|
||||
uint256 rate
|
||||
);
|
||||
|
||||
event RewardPoolRaised(uint256 amount);
|
||||
event LotteryPoolRaised(uint256 amount);
|
||||
|
||||
//modifiers
|
||||
modifier onlyAdmin() {
|
||||
require(treasury.isAdmin(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
modifier isUnpaused() {
|
||||
require(!pause);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier isValidWhitelistMint(uint256 amount, uint256 tokenType) {
|
||||
require(isPresale);
|
||||
require(isWhitelisted[msg.sender]);
|
||||
require(
|
||||
mintedOnWhitelist[msg.sender] + amount <=
|
||||
maxMintPerAddressDuringWhitelist
|
||||
);
|
||||
require(tokenType <= 3);
|
||||
require(
|
||||
whitelistMintCapOfToken[tokenType] + amount <=
|
||||
whitelistMintCapPerType
|
||||
);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier isValidMint(uint256 amount, uint256 tokenType) {
|
||||
require(!isPresale);
|
||||
require(tokenType <= 3);
|
||||
require(mintCapOfToken[tokenType] + amount <= nftCaps[tokenType]);
|
||||
_;
|
||||
}
|
||||
|
||||
modifier isValidCombine(uint256[] memory tokenIds) {
|
||||
require(tokenIds.length > 1 && tokenIds.length <= 4);
|
||||
bool hasDupes;
|
||||
for (uint256 i = 0; i < tokenIds.length; i++) {
|
||||
require(ownerOf(tokenIds[i]) == msg.sender);
|
||||
require(tokenIdToType[tokenIds[i]].length == 1);
|
||||
for (uint256 index = i; index < tokenIds.length; index++) {
|
||||
if (
|
||||
index != i &&
|
||||
tokenIdToType[tokenIds[i]][0] ==
|
||||
tokenIdToType[tokenIds[index]][0]
|
||||
) {
|
||||
hasDupes = true;
|
||||
}
|
||||
}
|
||||
require(!hasDupes);
|
||||
}
|
||||
_;
|
||||
}
|
||||
|
||||
function initialize(
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
ITreasury _treasury,
|
||||
address _redTrustFund,
|
||||
IERC20Upgradeable token,
|
||||
uint256 _rewardFee,
|
||||
uint256 _treasuryFee,
|
||||
uint256 _redTrustFee,
|
||||
IPancakeSwapPair _pair,
|
||||
IPancakeSwapRouter _router,
|
||||
IERC20Upgradeable _BUSD,
|
||||
IERC20Upgradeable _WETH
|
||||
) public initializer {
|
||||
treasury = _treasury;
|
||||
BoM = token;
|
||||
__ERC721_init(name, symbol);
|
||||
redTrustFund = _redTrustFund;
|
||||
rewardPoolFee = _rewardFee;
|
||||
redTrustFee = _redTrustFee;
|
||||
treasuryFee = _treasuryFee;
|
||||
uint16[4] memory attack = [1000, 1000, 700, 850];
|
||||
uint16[4] memory defense = [800, 1000, 850, 1000];
|
||||
uint16[4] memory health = [800, 800, 1000, 850];
|
||||
uint16[4] memory critChance = [400, 200, 400, 300];
|
||||
uint16[4] memory critDmg = [600, 500, 800, 500];
|
||||
uint16[4] memory recovery = [600, 700, 1000, 900];
|
||||
for (uint256 i = 0; i < 4; i++) {
|
||||
addressToToken[rewardTokens[i]].attack = attack[i];
|
||||
addressToToken[rewardTokens[i]].defense = defense[i];
|
||||
addressToToken[rewardTokens[i]].health = health[i];
|
||||
addressToToken[rewardTokens[i]].critChance = critChance[i];
|
||||
addressToToken[rewardTokens[i]].critDmg = critDmg[i];
|
||||
addressToToken[rewardTokens[i]].recovery = recovery[i];
|
||||
tokenToPrices[rewardTokens[i]] = prices[i];
|
||||
addressToType[rewardTokens[i]] = i;
|
||||
}
|
||||
pairContract = _pair;
|
||||
swapRouter = _router;
|
||||
BUSD = _BUSD;
|
||||
WETH = _WETH;
|
||||
}
|
||||
|
||||
function addWhitelist(address[] memory whitelist) public onlyAdmin {
|
||||
for (uint256 i = 0; i < whitelist.length; i++) {
|
||||
isWhitelisted[whitelist[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
function mintPresale(uint256 amount, uint256 tokenType)
|
||||
public
|
||||
isUnpaused
|
||||
isValidWhitelistMint(amount, tokenType)
|
||||
{
|
||||
for (uint256 i = 0; i < amount; i++) {
|
||||
supply++;
|
||||
_mint(msg.sender, supply);
|
||||
tokenIdToType[supply].push(rewardTokens[tokenType]);
|
||||
uint256 rate = createTokenStats(supply);
|
||||
emit TokenMinted(msg.sender, supply, rewardTokens[tokenType], rate);
|
||||
}
|
||||
mintedOnWhitelist[msg.sender] += amount;
|
||||
whitelistMintCapOfToken[tokenType] += amount;
|
||||
mintCapOfToken[tokenType] += amount;
|
||||
//TBD: swap to bnb
|
||||
_distributeFunds(amount * whitelistPrice);
|
||||
}
|
||||
|
||||
function mint(uint256 amount, uint256 tokenType)
|
||||
public
|
||||
isUnpaused
|
||||
isValidMint(amount, tokenType)
|
||||
{
|
||||
for (uint256 i = 0; i < amount; i++) {
|
||||
supply++;
|
||||
_mint(msg.sender, supply);
|
||||
tokenIdToInfo[supply].tokens.push(rewardTokens[tokenType]);
|
||||
uint256 rate = createTokenStats(supply);
|
||||
emit TokenMinted(msg.sender, supply, rewardTokens[tokenType], rate);
|
||||
}
|
||||
mintCapOfToken[tokenType] += amount;
|
||||
//TBD: swap to bnb
|
||||
_distributeFunds(amount * prices[tokenType]);
|
||||
}
|
||||
|
||||
function combineTokens(uint256[] memory tokenIds)
|
||||
public
|
||||
payable
|
||||
isUnpaused
|
||||
{
|
||||
require(msg.value == 0.05 ether);
|
||||
supply++;
|
||||
uint256 price;
|
||||
_mint(msg.sender, supply);
|
||||
address[] memory tokens = new address[](tokenIds.length);
|
||||
for (uint256 i = 0; i < tokenIds.length; i++) {
|
||||
TokenInfo storage token = tokenIdToInfo[tokenIds[i]];
|
||||
address intermediateStorageForToken = tokenIdToType[tokenIds[i]][0];
|
||||
tokenIdToInfo[supply].tokens.push(intermediateStorageForToken);
|
||||
tokens[i] = intermediateStorageForToken;
|
||||
_burn(tokenIds[i]);
|
||||
price += tokenToPrices[tokenIdToType[tokenIds[i]][0]] / 4;
|
||||
for (uint256 index = 0; index < token.tokens.length; index++) {
|
||||
totalRatesForType[token.tokens[index]] -= token.rate;
|
||||
}
|
||||
}
|
||||
uint256 rate = createTokenStats(supply);
|
||||
emit TokenCombined(msg.sender, tokenIds, tokens, supply, rate);
|
||||
//TBD: calculate price with usd in mind
|
||||
_distributeFunds(price);
|
||||
}
|
||||
|
||||
function createTokenStats(uint256 tokenId) internal returns (uint256 rate) {
|
||||
TokenInfo storage token = tokenIdToInfo[tokenId];
|
||||
uint256[7] memory seeds;
|
||||
for (uint8 i = 0; i < 7; i++) {
|
||||
nonce++;
|
||||
seeds[i] = (
|
||||
uint256(
|
||||
keccak256(
|
||||
abi.encodePacked(msg.sender, block.timestamp, nonce)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (token.tokens.length == 1) {
|
||||
uint256 rateMod = (seeds[0] % 100) + 1;
|
||||
if (rateMod == 1) {
|
||||
token.rate = 2000;
|
||||
} else if (rateMod <= 3) {
|
||||
token.rate = 1750;
|
||||
} else if (rateMod <= 10) {
|
||||
token.rate = 1500;
|
||||
} else if (rateMod <= 20) {
|
||||
token.rate = 1250;
|
||||
} else {
|
||||
token.rate = 1000;
|
||||
}
|
||||
} else if (token.tokens.length == 2) {
|
||||
token.rate = 2250;
|
||||
} else if (token.tokens.length == 3) {
|
||||
token.rate = 2500;
|
||||
} else {
|
||||
token.rate = 3000;
|
||||
}
|
||||
rate = token.rate;
|
||||
for (uint256 i = 0; i < token.tokens.length; i++) {
|
||||
totalRatesForType[token.tokens[i]] += token.rate;
|
||||
}
|
||||
createBattleStats(seeds, token, getStatMultipliers(token));
|
||||
}
|
||||
|
||||
function createBattleStats(
|
||||
uint256[7] memory seeds,
|
||||
TokenInfo storage token,
|
||||
uint256[6] memory statMultipliers
|
||||
) internal {
|
||||
token.attack =
|
||||
(((seeds[1] % 71) + 30) * (token.rate * statMultipliers[0])) /
|
||||
statMultiplier;
|
||||
token.defense =
|
||||
(((seeds[2] % 71) + 30) * (token.rate * statMultipliers[1])) /
|
||||
statMultiplier;
|
||||
token.health =
|
||||
(((seeds[3] % 51) + 50) * (token.rate * statMultipliers[2])) /
|
||||
statMultiplier;
|
||||
token.critChance =
|
||||
(((seeds[4] % 41) + 30) * (token.rate * statMultipliers[3])) /
|
||||
statMultiplier;
|
||||
token.critDmg =
|
||||
(((seeds[5] % 61) + 10) * (token.rate * statMultipliers[4])) /
|
||||
statMultiplier;
|
||||
token.recover =
|
||||
(((seeds[6] % 51) + 50) * (token.rate * statMultipliers[5])) /
|
||||
statMultiplier;
|
||||
}
|
||||
|
||||
function getStatMultipliers(TokenInfo storage token)
|
||||
internal
|
||||
view
|
||||
returns (uint256[6] memory statMultipliers)
|
||||
{
|
||||
uint16[4] memory megaStatMultipliers = [1000, 1250, 1500, 2000];
|
||||
for (uint8 i = 0; i < token.tokens.length; i++) {
|
||||
Tokenomic memory tokenomic = addressToToken[token.tokens[i]];
|
||||
statMultipliers[0] += tokenomic.attack;
|
||||
statMultipliers[1] += tokenomic.defense;
|
||||
statMultipliers[2] += tokenomic.health;
|
||||
statMultipliers[3] += tokenomic.critChance;
|
||||
statMultipliers[4] += tokenomic.critDmg;
|
||||
statMultipliers[5] += tokenomic.recovery;
|
||||
}
|
||||
for (uint8 i = 0; i < 6; i++) {
|
||||
statMultipliers[i] =
|
||||
(statMultipliers[i] / token.tokens.length) *
|
||||
megaStatMultipliers[token.tokens.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
function _distributeFunds(uint256 amount) internal {
|
||||
uint256[] memory requiredAmountOfTokens = _getAmountsIn(
|
||||
amount,
|
||||
address(BoM),
|
||||
address(WETH)
|
||||
);
|
||||
require(
|
||||
BoM.allowance(msg.sender, address(this)) >=
|
||||
requiredAmountOfTokens[0],
|
||||
"allowance too low"
|
||||
);
|
||||
BoM.transferFrom(msg.sender, address(this), requiredAmountOfTokens[0]);
|
||||
uint256 bnbAmount = _swap(
|
||||
amount,
|
||||
requiredAmountOfTokens[0],
|
||||
address(BoM),
|
||||
address(WETH)
|
||||
)[0];
|
||||
uint256 _treasuryFee = (bnbAmount * treasuryFee) / 1000;
|
||||
uint256 _redTrustFee = (bnbAmount * redTrustFee) / 1000;
|
||||
uint256 _rewardPoolFee = bnbAmount - _treasuryFee - _redTrustFee;
|
||||
BoM.transferFrom(msg.sender, address(treasury), _treasuryFee);
|
||||
BoM.transferFrom(msg.sender, redTrustFund, _redTrustFee);
|
||||
BoM.transferFrom(msg.sender, address(this), _rewardPoolFee);
|
||||
_addToPool(_rewardPoolFee);
|
||||
}
|
||||
|
||||
function lottery() public onlyAdmin {
|
||||
require(busdInLotteryPool > 0, "no funds in lottery pool");
|
||||
|
||||
uint256[10] memory seeds;
|
||||
|
||||
uint256 holdersCount = _holders.length();
|
||||
uint256 winnersCount = holdersCount;
|
||||
if (winnersCount > 10) {
|
||||
winnersCount = 10;
|
||||
|
||||
for (uint8 i = 0; i < winnersCount; i++) {
|
||||
nonce++;
|
||||
uint256 idx = (
|
||||
uint256(
|
||||
keccak256(
|
||||
abi.encodePacked(msg.sender, block.timestamp, blockhash(block.number - 1), nonce)
|
||||
)
|
||||
)
|
||||
) % holdersCount;
|
||||
|
||||
while (true) {
|
||||
bool retry = false;
|
||||
for (uint8 j=0; j < i; j++) {
|
||||
if (idx == seeds[j]) {
|
||||
retry = true;
|
||||
idx = (idx + 1) % holdersCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!retry)
|
||||
break;
|
||||
}
|
||||
|
||||
seeds[i] = idx;
|
||||
}
|
||||
} else {
|
||||
for (uint8 i=0; i < winnersCount; i++) {
|
||||
seeds[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
uint256 winEach = 100 ether;
|
||||
if (busdInLotteryPool < (100 ether * winnersCount))
|
||||
winEach = busdInLotteryPool / winnersCount;
|
||||
|
||||
for (uint256 i = 0; i < winnersCount; i++) {
|
||||
BUSD.safeTransfer(_holders.at(seeds[i]), winEach);
|
||||
}
|
||||
}
|
||||
|
||||
function _getAmountsIn(
|
||||
uint256 price,
|
||||
address path0,
|
||||
address path1
|
||||
) internal view returns (uint256[] memory) {
|
||||
address[] memory path;
|
||||
path[0] = path0;
|
||||
path[1] = path1;
|
||||
return swapRouter.getAmountsIn(price, path);
|
||||
}
|
||||
|
||||
function _swap(
|
||||
uint256 amount,
|
||||
uint256 price,
|
||||
address path0,
|
||||
address path1
|
||||
) internal returns (uint256[] memory) {
|
||||
address[] memory path;
|
||||
path[0] = path0;
|
||||
path[1] = path1;
|
||||
return
|
||||
swapRouter.swapTokensForExactTokens(
|
||||
amount,
|
||||
price,
|
||||
path,
|
||||
address(this),
|
||||
block.timestamp
|
||||
);
|
||||
// return
|
||||
// swapRouter.swapTokensForExactETH(
|
||||
// price,
|
||||
// amount,
|
||||
// path,
|
||||
// address(this),
|
||||
// block.timestamp
|
||||
// );
|
||||
}
|
||||
|
||||
// function isValidClaim(
|
||||
// uint256[] memory tokenAmounts,
|
||||
// bytes memory signature,
|
||||
// uint256 userNonce,
|
||||
// uint256 timestamp
|
||||
// ) internal returns (bool) {
|
||||
// bytes memory data = abi.encodePacked(
|
||||
// _toAsciiString(msg.sender),
|
||||
// " is authorized to claim ",
|
||||
// StringsUpgradeable.toString(tokenAmounts[0]),
|
||||
// " shiba, ",
|
||||
// StringsUpgradeable.toString(tokenAmounts[1]),
|
||||
// " floki, ",
|
||||
// StringsUpgradeable.toString(tokenAmounts[2]),
|
||||
// " doggy, ",
|
||||
// StringsUpgradeable.toString(tokenAmounts[3]),
|
||||
// " doge before ",
|
||||
// StringsUpgradeable.toString(timestamp),
|
||||
// ", ",
|
||||
// StringsUpgradeable.toString(userNonce)
|
||||
// );
|
||||
// bytes32 hash = _toEthSignedMessage(data);
|
||||
// address signer = ECDSAUpgradeable.recover(hash, signature);
|
||||
// require(treasury.isOperator(signer), "Mint not verified by operator");
|
||||
// require(block.timestamp <= timestamp, "Outdated signed message");
|
||||
// require(!usedNonces[userNonce], "Used nonce");
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// function claimRewards(
|
||||
// uint256[] memory amounts,
|
||||
// bytes memory signature,
|
||||
// uint256 userNonce,
|
||||
// uint256 timestamp
|
||||
// ) public {
|
||||
// require(isValidClaim(amounts, signature, userNonce, timestamp), "");
|
||||
// for (uint256 i = 0; i < 4; i++) {
|
||||
// if (amounts[i] > 0) {
|
||||
// address[] memory path;
|
||||
// path[0] = address(WETH);
|
||||
// path[1] = rewardTokens[i];
|
||||
// swapRouter.swapExactTokensForTokens(
|
||||
// amounts[i],
|
||||
// 1,
|
||||
// path,
|
||||
// msg.sender,
|
||||
// block.timestamp
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function walletOfOwner(address _owner)
|
||||
public
|
||||
view
|
||||
returns (uint256[] memory)
|
||||
{
|
||||
uint256 ownerTokenCount = balanceOf(_owner);
|
||||
uint256[] memory tokenIds = new uint256[](ownerTokenCount);
|
||||
for (uint256 i; i < ownerTokenCount; i++) {
|
||||
tokenIds[i] = tokenOfOwnerByIndex(_owner, i);
|
||||
}
|
||||
return tokenIds;
|
||||
}
|
||||
|
||||
function tokenURI(uint256 tokenId)
|
||||
public
|
||||
view
|
||||
override
|
||||
returns (string memory)
|
||||
{
|
||||
require(_exists(tokenId), "Non-existant token");
|
||||
TokenInfo storage token = tokenIdToInfo[tokenId];
|
||||
return
|
||||
string(
|
||||
abi.encodePacked(
|
||||
"data:application/json;base64,",
|
||||
Base64Upgradeable.encode(
|
||||
bytes(
|
||||
abi.encodePacked(
|
||||
'{"name": Babies of Mars #',
|
||||
tokenId,
|
||||
', "description": "adasdasdasd", "image": ',
|
||||
token.image,
|
||||
',{ "attributes": [ {"trait_type": "tokens", "value": ',
|
||||
token.tokens,
|
||||
'}, { "trait_type": "attack", "value": ',
|
||||
token.attack,
|
||||
'}, { "trait_type": "defense", "value": ',
|
||||
token.defense,
|
||||
'}, { "trait_type": "health", "value": ',
|
||||
token.health,
|
||||
'}, { "trait_type": "critical rate", "value": ',
|
||||
token.critChance,
|
||||
'}, { "trait_type": "critical damage", "value": ',
|
||||
token.critDmg,
|
||||
'}, { "trait_type": "recovery", "value": ',
|
||||
token.recover,
|
||||
"} ] }"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function raiseRewardPool(uint256 amount) public {
|
||||
require(
|
||||
BoM.allowance(msg.sender, address(this)) >= amount,
|
||||
"Not enough allowance"
|
||||
);
|
||||
BoM.transferFrom(msg.sender, address(this), amount);
|
||||
_addToPool(amount);
|
||||
}
|
||||
|
||||
function _addToPool(uint256 amount) internal {
|
||||
uint256 lotteryPoolAmount = amount / 10;
|
||||
|
||||
address[] memory path;
|
||||
path[0] = address(BoM);
|
||||
|
||||
path[1] = address(BUSD);
|
||||
uint256 swappedFor = swapRouter.swapExactTokensForTokens(
|
||||
lotteryPoolAmount, // 10% to lottery
|
||||
0,
|
||||
path,
|
||||
address(this),
|
||||
block.timestamp
|
||||
)[0];
|
||||
busdInLotteryPool += swappedFor;
|
||||
emit LotteryPoolRaised(swappedFor);
|
||||
|
||||
|
||||
path[1] = address(WETH);
|
||||
swappedFor = swapRouter.swapExactTokensForTokens(
|
||||
amount - lotteryPoolAmount,
|
||||
0,
|
||||
path,
|
||||
address(this),
|
||||
block.timestamp
|
||||
)[0];
|
||||
wethInRewardPool += swappedFor;
|
||||
for (uint8 i=0; i < 4; i++) {
|
||||
rewardPoolForToken[i] += swappedFor * 2 / 9; // 20% of total each
|
||||
}
|
||||
rewardPoolForToken[4] += swappedFor - swappedFor * 8 / 9; // remaining 10%
|
||||
emit RewardPoolRaised(swappedFor);
|
||||
}
|
||||
|
||||
function _afterTokenTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 tokenId
|
||||
) internal virtual override {
|
||||
super._afterTokenTransfer(from, to, tokenId);
|
||||
TokenInfo memory info = tokenIdToInfo[tokenId];
|
||||
if (from != address(0)) {
|
||||
if (ERC721Upgradeable.balanceOf(from) == 0) {
|
||||
_holders.remove(from);
|
||||
}
|
||||
if (info.tokens.length > 1) {
|
||||
totalShares[4] -= info.rate;
|
||||
accountShares[from][4] -= info.rate;
|
||||
} else {
|
||||
uint256 idx = addressToType[info.tokens[0]];
|
||||
totalShares[idx] -= info.rate;
|
||||
accountShares[from][idx] -= info.rate;
|
||||
}
|
||||
}
|
||||
if (to != address(0)) {
|
||||
_holders.add(to);
|
||||
if (info.tokens.length > 1) {
|
||||
totalShares[4] += info.rate;
|
||||
accountShares[to][4] += info.rate;
|
||||
} else {
|
||||
uint256 idx = addressToType[info.tokens[0]];
|
||||
totalShares[idx] += info.rate;
|
||||
accountShares[from][idx] += info.rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pendingReward(uint256 idx, address account) public view returns (uint256) {
|
||||
return accountShares[account][idx] + (_rewardPerShare(idx) - accountRewardsPerTokenPaid[account][idx]) / 1e18 + accountRewards[account][idx];
|
||||
}
|
||||
|
||||
function _rewardPerShare(uint256 idx) internal view returns (uint256) {
|
||||
if (totalShares[idx] == 0)
|
||||
return rewardsPerShareStored[idx];
|
||||
return rewardsPerShareStored[idx] + (rewardPoolForToken[idx] - lastUpdatePools[idx]) / totalShares[idx];
|
||||
}
|
||||
|
||||
function _updateRewards(address account) internal {
|
||||
for (uint256 i=0; i < 5; i++) {
|
||||
rewardsPerShareStored[i] = _rewardPerShare(i);
|
||||
lastUpdatePools[i] = rewardPoolForToken[i];
|
||||
if (account != address(0)) {
|
||||
accountRewards[account][i] = pendingReward(i, account);
|
||||
accountRewardsPerTokenPaid[account][i] = rewardsPerShareStored[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function claimReward(uint256 idx) external {
|
||||
_updateRewards(msg.sender);
|
||||
uint256 reward = accountRewards[msg.sender][idx];
|
||||
require(reward > 0, "nothing to claim");
|
||||
accountRewards[msg.sender][idx] = 0;
|
||||
|
||||
address[] memory path;
|
||||
path[0] = address(WETH);
|
||||
path[1] = rewardTokens[idx];
|
||||
uint256 swappedFor = swapRouter.swapExactTokensForTokens(
|
||||
reward,
|
||||
0,
|
||||
path,
|
||||
msg.sender,
|
||||
block.timestamp
|
||||
)[0];
|
||||
}
|
||||
}
|
36
contracts/proxies/proxies.sol
Normal file
36
contracts/proxies/proxies.sol
Normal file
@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
pragma solidity ^0.8.15;
|
||||
|
||||
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
|
||||
|
||||
contract BoMNFTProxy is TransparentUpgradeableProxy {
|
||||
constructor(
|
||||
address _logic,
|
||||
address _admin,
|
||||
bytes memory _data
|
||||
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
|
||||
}
|
||||
|
||||
contract BoMMarketProxy is TransparentUpgradeableProxy {
|
||||
constructor(
|
||||
address _logic,
|
||||
address _admin,
|
||||
bytes memory _data
|
||||
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
|
||||
}
|
||||
|
||||
contract BoMTreasuryProxy is TransparentUpgradeableProxy {
|
||||
constructor(
|
||||
address _logic,
|
||||
address _admin,
|
||||
bytes memory _data
|
||||
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
|
||||
}
|
||||
|
||||
contract BoMTokenProxy is TransparentUpgradeableProxy {
|
||||
constructor(
|
||||
address _logic,
|
||||
address _admin,
|
||||
bytes memory _data
|
||||
) public payable TransparentUpgradeableProxy(_logic, _admin, _data) {}
|
||||
}
|
7
contracts/proxies/proxyadmin.sol
Normal file
7
contracts/proxies/proxyadmin.sol
Normal file
@ -0,0 +1,7 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
pragma solidity ^0.8.15;
|
||||
|
||||
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
|
||||
|
||||
contract BoMProxyAdmin is ProxyAdmin {
|
||||
}
|
28
contracts/red_treasury.sol
Normal file
28
contracts/red_treasury.sol
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
pragma solidity ^0.8.14;
|
||||
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
|
||||
import "./mixins/role-control.sol";
|
||||
|
||||
|
||||
contract Treasury is RoleControl {
|
||||
|
||||
event TokenWithdraw(
|
||||
address indexed account,
|
||||
address indexed token,
|
||||
uint256 amount
|
||||
);
|
||||
|
||||
function initialize(address admin) public initializer {
|
||||
_setupRole(DEFAULT_ADMIN_ROLE, admin);
|
||||
}
|
||||
|
||||
function withdrawToken(IERC20Upgradeable token, address to, uint256 amount) public onlyAdmin {
|
||||
token.transfer(to, amount);
|
||||
emit TokenWithdraw(to, address(token), amount);
|
||||
}
|
||||
|
||||
}
|
673
contracts/token.sol
Normal file
673
contracts/token.sol
Normal file
@ -0,0 +1,673 @@
|
||||
// SPDX-License-Identifier: Unlicensed
|
||||
|
||||
pragma solidity ^0.8.15;
|
||||
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
|
||||
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
|
||||
import "./interfaces/IPancake.sol";
|
||||
import "./mixins/SafeMathInt.sol";
|
||||
import "./interfaces/ITreasury.sol";
|
||||
import "./interfaces/INFT.sol";
|
||||
|
||||
contract BabiesOfMars is ERC20Upgradeable {
|
||||
using SafeMathUpgradeable for uint256;
|
||||
using SafeMathInt for int256;
|
||||
|
||||
event LogRebase(uint256 indexed epoch, uint256 totalSupply);
|
||||
|
||||
string public _name = "BabiesOfMars";
|
||||
string public _symbol = "BoM";
|
||||
uint8 public _decimals = 5;
|
||||
|
||||
IPancakeSwapPair public pairContract;
|
||||
mapping(address => bool) _isFeeExempt;
|
||||
|
||||
modifier validRecipient(address to) {
|
||||
require(to != address(0x0));
|
||||
_;
|
||||
}
|
||||
|
||||
modifier onlyAdmin() {
|
||||
require(treasury.isAdmin(msg.sender));
|
||||
_;
|
||||
}
|
||||
|
||||
uint256 public constant DECIMALS = 5;
|
||||
uint256 public constant MAX_UINT256 = ~uint256(0);
|
||||
uint8 public constant RATE_DECIMALS = 7;
|
||||
|
||||
uint256 public liquidityFee = 300;
|
||||
uint256 public gameTreasuryFee = 300;
|
||||
uint256 public redTrustFee = 300;
|
||||
uint256 public redFurnaceFee = 800;
|
||||
uint256 public nftRewardFee = 300;
|
||||
uint256 public totalFee =
|
||||
liquidityFee
|
||||
.add(gameTreasuryFee)
|
||||
.add(redTrustFee)
|
||||
.add(redFurnaceFee)
|
||||
.add(nftRewardFee);
|
||||
uint256 public sellFee = 200;
|
||||
uint256 public feeDenominator = 10000;
|
||||
|
||||
address DEAD = 0x000000000000000000000000000000000000dEaD;
|
||||
address ZERO = 0x0000000000000000000000000000000000000000;
|
||||
|
||||
address public autoLiquidityReceiver;
|
||||
ITreasury public treasury;
|
||||
address public redTrustWallet;
|
||||
address public redFurnace;
|
||||
address public pairAddress;
|
||||
INFT public nftRewardPool;
|
||||
bool public swapEnabled = true;
|
||||
IPancakeSwapRouter public router;
|
||||
IPancakeSwapPair public pair;
|
||||
bool inSwap = false;
|
||||
|
||||
uint256 lastPrice;
|
||||
uint256 defenderTimer;
|
||||
bool rdStatus;
|
||||
|
||||
modifier swapping() {
|
||||
inSwap = true;
|
||||
_;
|
||||
inSwap = false;
|
||||
}
|
||||
|
||||
uint256 private constant TOTAL_GONS =
|
||||
MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY);
|
||||
uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 50_000 * 10**DECIMALS;
|
||||
uint256 private constant MAX_SUPPLY = 500_000 * 10**DECIMALS;
|
||||
|
||||
bool public _autoRebase;
|
||||
bool public _autoAddLiquidity;
|
||||
uint256 public _initRebaseStartTime;
|
||||
uint256 public _lastRebasedTime;
|
||||
uint256 public _lastAddLiquidityTime;
|
||||
uint256 public _totalSupply;
|
||||
uint256 private _gonsPerFragment;
|
||||
|
||||
uint256 public rebaseInterval = 15 minutes;
|
||||
|
||||
mapping(address => uint256) private _gonBalances;
|
||||
mapping(address => mapping(address => uint256)) private _allowedFragments;
|
||||
mapping(address => bool) public blacklist;
|
||||
|
||||
function initialize(
|
||||
address _router,
|
||||
address _owner,
|
||||
ITreasury _treasury,
|
||||
address _redTrustWallet,
|
||||
INFT _nftRewardPool,
|
||||
address _redFurnace
|
||||
) public initializer {
|
||||
__ERC20_init(_name, _symbol);
|
||||
router = IPancakeSwapRouter(_router);
|
||||
pair = IPancakeSwapPair(
|
||||
IPancakeSwapFactory(router.factory()).createPair(
|
||||
router.WETH(),
|
||||
address(this)
|
||||
)
|
||||
);
|
||||
|
||||
autoLiquidityReceiver = DEAD;
|
||||
treasury = _treasury;
|
||||
redTrustWallet = _redTrustWallet;
|
||||
redFurnace = _redFurnace;
|
||||
nftRewardPool = _nftRewardPool;
|
||||
|
||||
_allowedFragments[address(this)][address(router)] = type(uint256).max;
|
||||
pairContract = IPancakeSwapPair(pair);
|
||||
|
||||
_totalSupply = INITIAL_FRAGMENTS_SUPPLY;
|
||||
_gonBalances[_owner] = TOTAL_GONS;
|
||||
_gonsPerFragment = TOTAL_GONS.div(_totalSupply);
|
||||
_initRebaseStartTime = block.timestamp;
|
||||
_lastRebasedTime = block.timestamp;
|
||||
_autoRebase = false;
|
||||
_autoAddLiquidity = true;
|
||||
_isFeeExempt[_owner] = true;
|
||||
_isFeeExempt[address(this)] = true;
|
||||
_isFeeExempt[redTrustWallet] = true;
|
||||
_isFeeExempt[address(nftRewardPool)] = true;
|
||||
_isFeeExempt[address(treasury)] = true;
|
||||
|
||||
defenderTimer = block.timestamp;
|
||||
|
||||
emit Transfer(address(0x0), _owner, _totalSupply);
|
||||
}
|
||||
|
||||
function rebase() internal {
|
||||
if (inSwap) return;
|
||||
uint256 rebaseRate;
|
||||
uint256 deltaTimeFromInit = block.timestamp - _initRebaseStartTime;
|
||||
uint256 deltaTime = block.timestamp - _lastRebasedTime;
|
||||
uint256 times = deltaTime.div(rebaseInterval);
|
||||
uint256 epoch = times.mul(15);
|
||||
|
||||
if (deltaTimeFromInit < (365 days)) {
|
||||
rebaseRate = 2731;
|
||||
} else if (deltaTimeFromInit >= (365 days)) {
|
||||
rebaseRate = 211;
|
||||
} else if (deltaTimeFromInit >= ((15 * 365 days) / 10)) {
|
||||
rebaseRate = 14;
|
||||
} else if (deltaTimeFromInit >= (7 * 365 days)) {
|
||||
rebaseRate = 2;
|
||||
}
|
||||
|
||||
for (uint256 i = 0; i < times; i++) {
|
||||
_totalSupply = _totalSupply
|
||||
.mul((10**RATE_DECIMALS).add(rebaseRate))
|
||||
.div(10**RATE_DECIMALS);
|
||||
}
|
||||
|
||||
_gonsPerFragment = TOTAL_GONS.div(_totalSupply);
|
||||
_lastRebasedTime = _lastRebasedTime.add(times.mul(rebaseInterval));
|
||||
|
||||
pairContract.sync();
|
||||
|
||||
emit LogRebase(epoch, _totalSupply);
|
||||
}
|
||||
|
||||
function transfer(address to, uint256 value)
|
||||
public
|
||||
override
|
||||
validRecipient(to)
|
||||
returns (bool)
|
||||
{
|
||||
_transferFrom(msg.sender, to, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFrom(
|
||||
address from,
|
||||
address to,
|
||||
uint256 value
|
||||
) public override validRecipient(to) returns (bool) {
|
||||
if (_allowedFragments[from][msg.sender] != type(uint256).max) {
|
||||
_allowedFragments[from][msg.sender] = _allowedFragments[from][
|
||||
msg.sender
|
||||
].sub(value, "Insufficient Allowance");
|
||||
}
|
||||
_transferFrom(from, to, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function _basicTransfer(
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount
|
||||
) internal returns (bool) {
|
||||
uint256 gonAmount = amount.mul(_gonsPerFragment);
|
||||
_gonBalances[from] = _gonBalances[from].sub(gonAmount);
|
||||
_gonBalances[to] = _gonBalances[to].add(gonAmount);
|
||||
return true;
|
||||
}
|
||||
|
||||
function _transferFrom(
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 amount
|
||||
) internal returns (bool) {
|
||||
require(!blacklist[sender] && !blacklist[recipient], "in_blacklist");
|
||||
|
||||
if (inSwap) {
|
||||
return _basicTransfer(sender, recipient, amount);
|
||||
}
|
||||
if (shouldRebase()) {
|
||||
rebase();
|
||||
}
|
||||
|
||||
if (shouldAddLiquidity()) {
|
||||
addLiquidity();
|
||||
}
|
||||
|
||||
if (shouldSwapBack()) {
|
||||
swapBack();
|
||||
}
|
||||
|
||||
uint256 gonAmount = amount.mul(_gonsPerFragment);
|
||||
_gonBalances[sender] = _gonBalances[sender].sub(gonAmount);
|
||||
uint256 gonAmountReceived = shouldTakeFee(sender, recipient)
|
||||
? takeFee(sender, recipient, gonAmount)
|
||||
: gonAmount;
|
||||
_gonBalances[recipient] = _gonBalances[recipient].add(
|
||||
gonAmountReceived
|
||||
);
|
||||
|
||||
emit Transfer(
|
||||
sender,
|
||||
recipient,
|
||||
gonAmountReceived.div(_gonsPerFragment)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function takeFee(
|
||||
address sender,
|
||||
address recipient,
|
||||
uint256 gonAmount
|
||||
) internal returns (uint256) {
|
||||
uint256 _totalFee = totalFee;
|
||||
uint256 _gameTreasuryFee = gameTreasuryFee;
|
||||
(uint256 impact, uint256 oldPrice, uint256 newPrice) = getImpact(
|
||||
gonAmount
|
||||
);
|
||||
uint256 impactTax = ((gonAmount * impact) / 1000) * 4;
|
||||
|
||||
if (rdStatus == true) {
|
||||
if (block.timestamp - defenderTimer > 0) {
|
||||
// while (block.timestamp - defenderTimer > 1 hours) {
|
||||
// defenderTimer += 1 hours;
|
||||
// }
|
||||
rdStatus = false;
|
||||
} else {
|
||||
runDefender(gonAmount, impactTax, sender, recipient);
|
||||
}
|
||||
}
|
||||
|
||||
if (recipient == address(pair)) {
|
||||
if (impact > 100) {
|
||||
runDefender(gonAmount, impactTax, sender, recipient);
|
||||
}
|
||||
_totalFee = totalFee.add(sellFee).add(impactTax);
|
||||
_gameTreasuryFee = gameTreasuryFee.add(sellFee).add(impactTax);
|
||||
}
|
||||
|
||||
uint256 feeAmount = gonAmount.div(feeDenominator).mul(_totalFee);
|
||||
|
||||
_gonBalances[redFurnace] = _gonBalances[redFurnace].add(
|
||||
gonAmount.div(feeDenominator).mul(redFurnaceFee)
|
||||
);
|
||||
_gonBalances[address(treasury)] = _gonBalances[address(treasury)].add(
|
||||
gonAmount.div(feeDenominator).mul(_gameTreasuryFee)
|
||||
);
|
||||
_gonBalances[redTrustWallet] = _gonBalances[redTrustWallet].add(
|
||||
gonAmount.div(feeDenominator).mul(redTrustFee)
|
||||
);
|
||||
_gonBalances[autoLiquidityReceiver] = _gonBalances[
|
||||
autoLiquidityReceiver
|
||||
].add(gonAmount.div(feeDenominator).mul(liquidityFee));
|
||||
approve(address(nftRewardPool), nftRewardFee);
|
||||
nftRewardPool.raiseRewardPool(nftRewardFee);
|
||||
emit Transfer(sender, address(treasury), _gameTreasuryFee.div(_gonsPerFragment));
|
||||
emit Transfer(sender, redTrustWallet, redTrustFee.div(_gonsPerFragment));
|
||||
emit Transfer(sender, redFurnace, redFurnaceFee.div(_gonsPerFragment));
|
||||
return gonAmount.sub(feeAmount);
|
||||
}
|
||||
|
||||
function getImpact(uint256 amount)
|
||||
internal
|
||||
view
|
||||
returns (
|
||||
uint256,
|
||||
uint256,
|
||||
uint256
|
||||
)
|
||||
{
|
||||
uint256 price0 = pair.price0CumulativeLast();
|
||||
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
|
||||
uint256 constProduct = reserve0 * reserve1;
|
||||
uint256 new1Amount = reserve1 + amount;
|
||||
uint256 new0Amount = constProduct / new1Amount;
|
||||
uint256 amountTradedFor = reserve1 - new0Amount;
|
||||
uint256 new0Price = amount / amountTradedFor;
|
||||
return (((new0Price - price0) / price0) * 10000, price0, new0Price);
|
||||
// return (amount*priceImpact/1000)*4;
|
||||
}
|
||||
|
||||
function runDefender(
|
||||
uint256 amount,
|
||||
uint256 impact,
|
||||
address sender,
|
||||
address recipient
|
||||
) internal {
|
||||
rdStatus = true;
|
||||
uint256 _liquidityFee = 200;
|
||||
uint256 _redTrustFee = 500;
|
||||
uint256 _treasuryFee = 300;
|
||||
if (recipient == address(pair)) {
|
||||
uint256 hourlyImpact;
|
||||
require(
|
||||
impact <= 500,
|
||||
"selling with price impact of more than 5% is forbidden"
|
||||
);
|
||||
if (impact >= 100) {
|
||||
if(block.timestamp - defenderTimer > 0) {
|
||||
defenderTimer = block.timestamp;
|
||||
}
|
||||
defenderTimer += 1 hours;
|
||||
}
|
||||
uint256 impactTax = ((amount * impact) / 1000) * 4;
|
||||
_treasuryFee.add(impactTax).add(700);
|
||||
_liquidityFee.add(300);
|
||||
_redTrustFee.add(500);
|
||||
}
|
||||
// uint256 _totalFee = _liquidityFee.add(_redTrustFee).add(_treasuryFee);
|
||||
// uint256 feeAmount = amount.div(feeDenominator).mul(_totalFee);
|
||||
|
||||
_gonBalances[address(treasury)] = _gonBalances[address(treasury)].add(
|
||||
amount.div(feeDenominator).mul(_treasuryFee)
|
||||
);
|
||||
_gonBalances[redTrustWallet] = _gonBalances[redTrustWallet].add(
|
||||
amount.div(feeDenominator).mul(_redTrustFee)
|
||||
);
|
||||
_gonBalances[autoLiquidityReceiver] = _gonBalances[
|
||||
autoLiquidityReceiver
|
||||
].add(amount.div(feeDenominator).mul(liquidityFee));
|
||||
|
||||
emit Transfer(sender, address(treasury), _treasuryFee.div(_gonsPerFragment));
|
||||
emit Transfer(sender, redTrustWallet, _redTrustFee.div(_gonsPerFragment));
|
||||
// emit Transfer(
|
||||
// sender,
|
||||
// nftRewardPool,
|
||||
// nftRewardFee.div(_gonsPerFragment)
|
||||
// );
|
||||
return;
|
||||
}
|
||||
|
||||
function addLiquidity() internal swapping {
|
||||
uint256 autoLiquidityAmount = _gonBalances[autoLiquidityReceiver].div(
|
||||
_gonsPerFragment
|
||||
);
|
||||
_gonBalances[address(this)] = _gonBalances[address(this)].add(
|
||||
_gonBalances[autoLiquidityReceiver]
|
||||
);
|
||||
_gonBalances[autoLiquidityReceiver] = 0;
|
||||
uint256 amountToLiquify = autoLiquidityAmount.div(2);
|
||||
uint256 amountToSwap = autoLiquidityAmount.sub(amountToLiquify);
|
||||
|
||||
if (amountToSwap == 0) {
|
||||
return;
|
||||
}
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = address(this);
|
||||
path[1] = router.WETH();
|
||||
|
||||
uint256 balanceBefore = address(this).balance;
|
||||
|
||||
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
|
||||
amountToSwap,
|
||||
0,
|
||||
path,
|
||||
address(this),
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
uint256 amountETHLiquidity = address(this).balance.sub(balanceBefore);
|
||||
|
||||
if (amountToLiquify > 0 && amountETHLiquidity > 0) {
|
||||
router.addLiquidityETH{value: amountETHLiquidity}(
|
||||
address(this),
|
||||
amountToLiquify,
|
||||
0,
|
||||
0,
|
||||
DEAD,
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
_lastAddLiquidityTime = block.timestamp;
|
||||
}
|
||||
|
||||
function swapBack() internal swapping {
|
||||
uint256 amountToSwap = _gonBalances[address(this)].div(
|
||||
_gonsPerFragment
|
||||
);
|
||||
|
||||
if (amountToSwap == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint256 balanceBefore = address(this).balance;
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = address(this);
|
||||
path[1] = router.WETH();
|
||||
|
||||
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
|
||||
amountToSwap,
|
||||
0,
|
||||
path,
|
||||
address(this),
|
||||
block.timestamp
|
||||
);
|
||||
|
||||
uint256 amountETHTogameTreasuryAndSIF = address(this).balance.sub(
|
||||
balanceBefore
|
||||
);
|
||||
|
||||
(bool success, ) = address(treasury).call{
|
||||
value: amountETHTogameTreasuryAndSIF.mul(gameTreasuryFee).div(
|
||||
gameTreasuryFee.add(redTrustFee)
|
||||
),
|
||||
gas: 30000
|
||||
}("");
|
||||
(success, ) = payable(redTrustWallet).call{
|
||||
value: amountETHTogameTreasuryAndSIF.mul(redTrustFee).div(
|
||||
gameTreasuryFee.add(redTrustFee)
|
||||
),
|
||||
gas: 30000
|
||||
}("");
|
||||
}
|
||||
|
||||
function withdrawAllToTreasury() public swapping onlyAdmin {
|
||||
uint256 amountToSwap = _gonBalances[address(this)].div(
|
||||
_gonsPerFragment
|
||||
);
|
||||
require(
|
||||
amountToSwap > 0,
|
||||
"There is no token deposited in token contract"
|
||||
);
|
||||
address[] memory path = new address[](2);
|
||||
path[0] = address(this);
|
||||
path[1] = router.WETH();
|
||||
router.swapExactTokensForETHSupportingFeeOnTransferTokens(
|
||||
amountToSwap,
|
||||
0,
|
||||
path,
|
||||
address(treasury),
|
||||
block.timestamp
|
||||
);
|
||||
}
|
||||
|
||||
function shouldTakeFee(address from, address to)
|
||||
internal
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return
|
||||
(address(pair) == from || address(pair) == to) &&
|
||||
!_isFeeExempt[from] &&
|
||||
!_isFeeExempt[to];
|
||||
}
|
||||
|
||||
function shouldRebase() internal view returns (bool) {
|
||||
return
|
||||
_autoRebase &&
|
||||
(_totalSupply < MAX_SUPPLY) &&
|
||||
msg.sender != address(pair) &&
|
||||
!inSwap &&
|
||||
block.timestamp >= (_lastRebasedTime + rebaseInterval);
|
||||
}
|
||||
|
||||
function shouldAddLiquidity() internal view returns (bool) {
|
||||
return
|
||||
_autoAddLiquidity &&
|
||||
!inSwap &&
|
||||
msg.sender != address(pair) &&
|
||||
block.timestamp >= (_lastAddLiquidityTime + 48 hours);
|
||||
}
|
||||
|
||||
function shouldSwapBack() internal view returns (bool) {
|
||||
return !inSwap && msg.sender != address(pair);
|
||||
}
|
||||
|
||||
function setAutoRebase(bool _flag) public onlyAdmin {
|
||||
if (_flag) {
|
||||
_autoRebase = _flag;
|
||||
_lastRebasedTime = block.timestamp;
|
||||
} else {
|
||||
_autoRebase = _flag;
|
||||
}
|
||||
}
|
||||
|
||||
function setAutoAddLiquidity(bool _flag) public onlyAdmin {
|
||||
if (_flag) {
|
||||
_autoAddLiquidity = _flag;
|
||||
_lastAddLiquidityTime = block.timestamp;
|
||||
} else {
|
||||
_autoAddLiquidity = _flag;
|
||||
}
|
||||
}
|
||||
|
||||
function allowance(address owner_, address spender)
|
||||
public
|
||||
view
|
||||
override
|
||||
returns (uint256)
|
||||
{
|
||||
return _allowedFragments[owner_][spender];
|
||||
}
|
||||
|
||||
function decreaseAllowance(address spender, uint256 subtractedValue)
|
||||
public
|
||||
override
|
||||
returns (bool)
|
||||
{
|
||||
uint256 oldValue = _allowedFragments[msg.sender][spender];
|
||||
if (subtractedValue >= oldValue) {
|
||||
_allowedFragments[msg.sender][spender] = 0;
|
||||
} else {
|
||||
_allowedFragments[msg.sender][spender] = oldValue.sub(
|
||||
subtractedValue
|
||||
);
|
||||
}
|
||||
emit Approval(
|
||||
msg.sender,
|
||||
spender,
|
||||
_allowedFragments[msg.sender][spender]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function increaseAllowance(address spender, uint256 addedValue)
|
||||
public
|
||||
override
|
||||
returns (bool)
|
||||
{
|
||||
_allowedFragments[msg.sender][spender] = _allowedFragments[msg.sender][
|
||||
spender
|
||||
].add(addedValue);
|
||||
emit Approval(
|
||||
msg.sender,
|
||||
spender,
|
||||
_allowedFragments[msg.sender][spender]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
function approve(address spender, uint256 value)
|
||||
public
|
||||
override
|
||||
returns (bool)
|
||||
{
|
||||
_allowedFragments[msg.sender][spender] = value;
|
||||
emit Approval(msg.sender, spender, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkFeeExempt(address _addr) public view returns (bool) {
|
||||
return _isFeeExempt[_addr];
|
||||
}
|
||||
|
||||
function getCirculatingSupply() public view returns (uint256) {
|
||||
return
|
||||
(TOTAL_GONS.sub(_gonBalances[DEAD]).sub(_gonBalances[ZERO])).div(
|
||||
_gonsPerFragment
|
||||
);
|
||||
}
|
||||
|
||||
function isNotInSwap() public view returns (bool) {
|
||||
return !inSwap;
|
||||
}
|
||||
|
||||
function manualSync() public {
|
||||
IPancakeSwapPair(pair).sync();
|
||||
}
|
||||
|
||||
function setFeeReceivers(
|
||||
ITreasury _treasury,
|
||||
address _redTrustWallet,
|
||||
address _redFurnace
|
||||
) public onlyAdmin {
|
||||
treasury = _treasury;
|
||||
redTrustWallet = _redTrustWallet;
|
||||
redFurnace = _redFurnace;
|
||||
}
|
||||
|
||||
function setFee(
|
||||
uint256 _liquidityFee,
|
||||
uint256 _gameTreasuryFee,
|
||||
uint256 _redTrustFee,
|
||||
uint256 _redFurnaceFee,
|
||||
uint256 _sellFee,
|
||||
uint256 _nftRewardFee
|
||||
) public onlyAdmin {
|
||||
liquidityFee = _liquidityFee;
|
||||
gameTreasuryFee = _gameTreasuryFee;
|
||||
redTrustFee = _redTrustFee;
|
||||
redFurnaceFee = _redFurnaceFee;
|
||||
nftRewardFee = _nftRewardFee;
|
||||
totalFee = liquidityFee
|
||||
.add(gameTreasuryFee)
|
||||
.add(redTrustFee)
|
||||
.add(redFurnaceFee)
|
||||
.add(nftRewardFee);
|
||||
sellFee = _sellFee;
|
||||
require(totalFee.add(sellFee) <= 250, "Fee too high!");
|
||||
}
|
||||
|
||||
function getLiquidityBacking(uint256 accuracy)
|
||||
public
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
uint256 liquidityBalance = _gonBalances[address(pair)].div(
|
||||
_gonsPerFragment
|
||||
);
|
||||
return
|
||||
accuracy.mul(liquidityBalance.mul(2)).div(getCirculatingSupply());
|
||||
}
|
||||
|
||||
function setWhitelist(address _addr) public onlyAdmin {
|
||||
_isFeeExempt[_addr] = true;
|
||||
}
|
||||
|
||||
function setBotBlacklist(address _botAddress, bool _flag) public onlyAdmin {
|
||||
require(
|
||||
isContract(_botAddress),
|
||||
"only contract address, not allowed exteranlly owned account"
|
||||
);
|
||||
blacklist[_botAddress] = _flag;
|
||||
}
|
||||
|
||||
function totalSupply() public view override returns (uint256) {
|
||||
return _totalSupply;
|
||||
}
|
||||
|
||||
function balanceOf(address who) public view override returns (uint256) {
|
||||
return _gonBalances[who].div(_gonsPerFragment);
|
||||
}
|
||||
|
||||
function isContract(address addr) internal view returns (bool) {
|
||||
uint256 size;
|
||||
assembly {
|
||||
size := extcodesize(addr)
|
||||
}
|
||||
return size > 0;
|
||||
}
|
||||
|
||||
function decimals() public view override returns (uint8) {
|
||||
return _decimals;
|
||||
}
|
||||
|
||||
receive() external payable {}
|
||||
}
|
9
deploy/00_treasury.ts
Normal file
9
deploy/00_treasury.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { ethers, run } from "hardhat";
|
||||
import * as crypto from "crypto"
|
||||
|
||||
//TBD
|
||||
//@ts-ignore
|
||||
module.exports = async ({ getNamedAccounts, deployments, getChainId }) => {
|
||||
|
||||
};
|
||||
module.exports.tags = ['Treasury']
|
123
hardhat.config.ts
Normal file
123
hardhat.config.ts
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
|
||||
import { task } from "hardhat/config";
|
||||
import "@nomiclabs/hardhat-ethers";
|
||||
import "@nomiclabs/hardhat-waffle";
|
||||
import "@nomiclabs/hardhat-etherscan";
|
||||
import "@openzeppelin/hardhat-upgrades";
|
||||
import "@typechain/hardhat";
|
||||
import "hardhat-tracer";
|
||||
import "hardhat-gas-reporter";
|
||||
import "hardhat-dependency-compiler";
|
||||
import "hardhat-deploy";
|
||||
|
||||
require('dotenv').config();
|
||||
|
||||
let {
|
||||
ETHERSCAN_TOKEN,
|
||||
BSCSCAN_TOKEN,
|
||||
PRIVATE_KEY
|
||||
} = process.env;
|
||||
|
||||
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
||||
|
||||
task("accounts", "Prints the list of accounts", async (args, { ethers }) => {
|
||||
const accounts = await ethers.getSigners();
|
||||
|
||||
for (const account of accounts) {
|
||||
console.log(account.address);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @type import('hardhat/config').HardhatUserConfig
|
||||
*/
|
||||
let networks;
|
||||
|
||||
if(PRIVATE_KEY) {
|
||||
let accounts = [
|
||||
PRIVATE_KEY
|
||||
]
|
||||
networks = {
|
||||
hardhat: {},
|
||||
bsc: {
|
||||
url: "https://bsc-dataseed.binance.org/",
|
||||
chainId: 56,
|
||||
gasPrice: 5000000000,
|
||||
accounts
|
||||
},
|
||||
bscTestnet: {
|
||||
url: "https://data-seed-prebsc-1-s1.binance.org:8545",
|
||||
chainId: 97,
|
||||
gasPrice: 10000000000,
|
||||
accounts
|
||||
},
|
||||
rinkeby: {
|
||||
url: "https://eth-rinkeby.alchemyapi.io/v2/v92DVe9FFvr2lzRB4wjtk-z4DdsQjBhs",
|
||||
gasPrice: 5000000000,
|
||||
accounts
|
||||
},
|
||||
ropsten: {
|
||||
url: "https://eth-ropsten.alchemyapi.io/v2/v92DVe9FFvr2lzRB4wjtk-z4DdsQjBhs",
|
||||
gasPrice: 20000000000,
|
||||
accounts
|
||||
},
|
||||
kovan: {
|
||||
url: "https://eth-kovan.alchemyapi.io/v2/v92DVe9FFvr2lzRB4wjtk-z4DdsQjBhs",
|
||||
gasPrice: 20000000000,
|
||||
accounts
|
||||
},
|
||||
goerli: {
|
||||
url: "https://eth-goerli.alchemyapi.io/v2/v92DVe9FFvr2lzRB4wjtk-z4DdsQjBhs",
|
||||
gasPrice: 20000000000,
|
||||
accounts
|
||||
},
|
||||
};
|
||||
} else {
|
||||
networks = {
|
||||
hardhat: {},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
defaultNetwork: "hardhat",
|
||||
networks: networks,
|
||||
etherscan: {
|
||||
apiKey: {
|
||||
mainnet:ETHERSCAN_TOKEN,
|
||||
rinkeby:ETHERSCAN_TOKEN,
|
||||
ropsten:ETHERSCAN_TOKEN,
|
||||
kovan:ETHERSCAN_TOKEN,
|
||||
bscTestnet:BSCSCAN_TOKEN,
|
||||
goerli:ETHERSCAN_TOKEN
|
||||
}
|
||||
},
|
||||
dependencyCompiler: {
|
||||
},
|
||||
namedAccounts: {
|
||||
deployer: 0,
|
||||
},
|
||||
abiExporter: {
|
||||
path: './artifacts/abi',
|
||||
clear: true,
|
||||
flat: true,
|
||||
only: [':Bithotel'],
|
||||
spacing: 2
|
||||
},
|
||||
solidity: {
|
||||
version: "0.8.15",
|
||||
settings: {
|
||||
optimizer: {
|
||||
enabled: true,
|
||||
runs: 200
|
||||
}
|
||||
}
|
||||
},
|
||||
typechain: {
|
||||
outDir: "typechain",
|
||||
target: "ethers-v5",
|
||||
},
|
||||
};
|
||||
|
35813
package-lock.json
generated
Normal file
35813
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
43
package.json
Normal file
43
package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "atech-contracts",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@metamask/eth-sig-util": "^4.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
||||
"@openzeppelin/contracts": "^4.6.0",
|
||||
"@openzeppelin/contracts-upgradeable": "^4.6.0",
|
||||
"@openzeppelin/hardhat-upgrades": "^1.13.0",
|
||||
"@remix-project/remixd": "^0.5.5",
|
||||
"dotenv": "^12.0.3",
|
||||
"eth-sig-util": "^3.0.1",
|
||||
"ethereumjs-abi": "^0.6.8",
|
||||
"hardhat": "^2.8.2",
|
||||
"hardhat-dependency-compiler": "^1.1.2",
|
||||
"hardhat-deploy": "^0.9.24",
|
||||
"hardhat-gas-reporter": "^1.0.7",
|
||||
"hardhat-tracer": "^1.0.0-alpha.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@defi-wonderland/smock": "^2.2.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.3",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.4",
|
||||
"@nomiclabs/hardhat-waffle": "^2.0.1",
|
||||
"@typechain/ethers-v5": "^10.1.0",
|
||||
"@typechain/hardhat": "^6.1.2",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"chai": "^4.3.4",
|
||||
"ethereum-waffle": "^3.4.0",
|
||||
"ethers": "^5.5.3",
|
||||
"mocha": "^10.0.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": "^4.5.4"
|
||||
}
|
||||
}
|
24
test/nft.ts
Normal file
24
test/nft.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { MockContract, smock } from '@defi-wonderland/smock';
|
||||
import { expect, use } from 'chai';
|
||||
import { NFT, NFT__factory } from '../typechain';
|
||||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
|
||||
import { ethers } from 'hardhat';
|
||||
|
||||
use(smock.matchers);
|
||||
|
||||
|
||||
describe('nft', async () => {
|
||||
let nft: MockContract<NFT>;
|
||||
let admin: SignerWithAddress;
|
||||
|
||||
beforeEach(async () => {
|
||||
[ admin ] = await ethers.getSigners();
|
||||
|
||||
const nftMockFactory = await smock.mock<NFT__factory>('NFT');
|
||||
nft = await nftMockFactory.deploy();
|
||||
});
|
||||
|
||||
it('should initially have isPresale = true', async () => {
|
||||
expect(await nft.isPresale()).to.be.true;
|
||||
})
|
||||
});
|
9
tsconfig.json
Normal file
9
tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "commonjs",
|
||||
"strict": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user