// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;interfaceAggregatorV3Interface{
functiondecimals() externalviewreturns (uint8);
functiondescription() externalviewreturns (stringmemory);
functionversion() externalviewreturns (uint256);
// getRoundData and latestRoundData should both raise "No data present"// if they do not have data to report, instead of returning unset values// which could be misinterpreted as actual reported values.functiongetRoundData(uint80 _roundId)
externalviewreturns (uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
functionlatestRoundData()
externalviewreturns (uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}
Contract Source Code
File 2 of 19: BankXToken.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../ERC20/ERC20Custom.sol";
import"@openzeppelin/contracts/utils/Context.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"../XSD/XSDStablecoin.sol";
contractBankXTokenisERC20Custom{
/* ========== STATE VARIABLES ========== */stringpublic symbol;
stringpublic name;
uint8publicconstant decimals =18;
uint256public genesis_supply; // 2B is printed upon genesisaddresspublic pool_address; //points to BankX pool addressaddresspublic treasury; //stores the genesis supplyaddresspublic router;
XSDStablecoin private XSD; //XSD stablecoin instanceaddresspublic smartcontract_owner;
/* ========== MODIFIERS ========== */modifieronlyPools() {
require(XSD.xsd_pools(msg.sender) ==true, "Only xsd pools can mint new BankX");
_;
}
modifieronlyByOwner() {
require(msg.sender== smartcontract_owner, "You are not an owner");
_;
}
/* ========== CONSTRUCTOR ========== */constructor(stringmemory _name,
stringmemory _symbol,
uint256 _pool_amount,
uint256 _genesis_supply,
address _treasury,
address _smartcontract_owner
) {
require((_treasury !=address(0)), "Zero address detected");
name = _name;
symbol = _symbol;
genesis_supply = _genesis_supply + _pool_amount;
treasury = _treasury;
_mint(_msgSender(), _pool_amount);
_mint(treasury, _genesis_supply);
smartcontract_owner = _smartcontract_owner;
}
/* ========== RESTRICTED FUNCTIONS ========== */functionsetPool(address new_pool) externalonlyByOwner{
require(new_pool !=address(0), "Zero address detected");
pool_address = new_pool;
}
functionsetTreasury(address new_treasury) externalonlyByOwner{
require(new_treasury !=address(0), "Treasury address cannot be 0");
treasury = new_treasury;
}
functionsetRouterAddress(address _router) externalonlyByOwner{
require(_router !=address(0), "Zero address detected");
router = _router;
}
functionsetXSDAddress(address xsd_contract_address) externalonlyByOwner{
require(xsd_contract_address !=address(0), "Zero address detected");
XSD = XSDStablecoin(xsd_contract_address);
emit XSDAddressSet(xsd_contract_address);
}
functionmint(address to, uint256 amount) publiconlyPools{
_mint(to, amount);
emit BankXMinted(address(this), to, amount);
}
functiongenesisSupply() publicviewreturns(uint256){
return genesis_supply;
}
// This function is what other xsd pools will call to mint new BankX (similar to the XSD mint) functionpool_mint(address m_address, uint256 m_amount) externalonlyPools{
super._mint(m_address, m_amount);
emit BankXMinted(address(this), m_address, m_amount);
}
// This function is what other xsd pools will call to burn BankX functionpool_burn_from(address b_address, uint256 b_amount) externalonlyPools{
super._burnFrom(b_address, b_amount);
emit BankXBurned(b_address, address(this), b_amount);
}
//burn bankx from the pool when bankx is inflationaryfunctionburnpoolBankX(uint _bankx_amount) public{
require(msg.sender== router, "Only Router can access this function");
require(totalSupply()>genesis_supply,"BankX must be deflationary");
super._burn(pool_address, _bankx_amount);
IBankXWETHpool(pool_address).sync();
emit BankXBurned(msg.sender, address(this), _bankx_amount);
}
functionsetSmartContractOwner(address _smartcontract_owner) external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
require(msg.sender!=address(0), "Zero address detected");
smartcontract_owner = _smartcontract_owner;
}
functionrenounceOwnership() external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
smartcontract_owner =address(0);
}
/* ========== EVENTS ========== */// Track BankX burnedeventBankXBurned(addressindexedfrom, addressindexed to, uint256 amount);
// Track BankX mintedeventBankXMinted(addressindexedfrom, addressindexed to, uint256 amount);
eventXSDAddressSet(address addr);
}
// SPDX-License-Identifier: MIT/*
BBBBBBBBBBBBBBBBB kkkkkkkk XXXXXXX XXXXXXX
B::::::::::::::::B k::::::k X:::::X X:::::X
B::::::BBBBBB:::::B k::::::k X:::::X X:::::X
BB:::::B B:::::B k::::::k X::::::X X::::::X
B::::B B:::::B aaaaaaaaaaaaa nnnn nnnnnnnn k:::::k kkkkkkk XXX:::::X X:::::XXX
B::::B B:::::B a::::::::::::a n:::nn::::::::nn k:::::k k:::::k X:::::X X:::::X
B::::BBBBBB:::::B aaaaaaaaa:::::a n::::::::::::::nn k:::::k k:::::k X:::::X:::::X
B:::::::::::::BB a::::a nn:::::::::::::::n k:::::k k:::::k X:::::::::X
B::::BBBBBB:::::B aaaaaaa:::::a n:::::nnnn:::::n k::::::k:::::k X:::::::::X
B::::B B:::::B aa::::::::::::a n::::n n::::n k:::::::::::k X:::::X:::::X
B::::B B:::::B a::::aaaa::::::a n::::n n::::n k:::::::::::k X:::::X X:::::X
B::::B B:::::B a::::a a:::::a n::::n n::::n k::::::k:::::k XXX:::::X X:::::XXX
BB:::::BBBBBB::::::B a::::a a:::::a n::::n n::::n k::::::k k:::::k X::::::X X::::::X
B:::::::::::::::::B a:::::aaaa::::::a n::::n n::::n k::::::k k:::::k X:::::X X:::::X
B::::::::::::::::B a::::::::::aa:::a n::::n n::::n k::::::k k:::::k X:::::X X:::::X
BBBBBBBBBBBBBBBBB aaaaaaaaaa aaaa nnnnnn nnnnnn kkkkkkkk kkkkkkk XXXXXXX XXXXXXX
Currency Creators Manifesto
Our world faces an urgent crisis of currency manipulation, theft and inflation. Under the current system, currency is controlled by and benefits elite families, governments and large banking institutions. We believe currencies should be minted by and benefit the individual, not the establishment. It is time to take back the control of and the freedom that money can provide.
BankX is rebuilding the legacy banking system from the ground up by providing you with the capability to create currency and be in complete control of wealth creation with a concept we call ‘Individual Created Digital Currency’ (ICDC). You own the collateral. You mint currency. You earn interest. You leverage without the risk of liquidation. You stake to earn even more returns. All of this is done with complete autonomy and decentralization. BankX has built a stablecoin for Individual Freedom.
BankX is the antidote for the malevolent financial system bringing in a new future of freedom where you are in complete control with no middlemen, bank or central bank between you and your finances. This capability to create currency and be in complete control of wealth creation will be in the hands of every individual that uses BankX.
By 2030, we will rid the world of the corrupt, tyrannical and incompetent banking system replacing it with a system where billions of people will be in complete control of their financial future. Everyone will be given ultimate freedom to use their assets to create currency, earn interest and multiply returns to accomplish their individual goals. The mission of BankX is to be the first to mint $1 trillion in stablecoin.
We will bring about this transformation by attracting people that believe what we believe. We will partner with other blockchain protocols and build decentralized applications that drive even more usage. Finally, we will deploy a private network that is never connected to the Internet to communicate between counterparties, that allows for blockchain-to-blockchain interoperability and stores private keys and cryptocurrency wallets. Our ecosystem, network and platform has never been seen in the market and provides us with a long term sustainable competitive advantage.
We value individual freedom.
We believe in financial autonomy.
We are anti-establishment.
We envision a future of self-empowerment.
*/pragmasolidity ^0.8.0;import'@uniswap/lib/contracts/libraries/TransferHelper.sol';
import'@openzeppelin/contracts/security/ReentrancyGuard.sol';
import"../../BankX/BankXToken.sol";
import"../XSDStablecoin.sol";
import"./Interfaces/IBankXWETHpool.sol";
import"./Interfaces/IXSDWETHpool.sol";
import'../../Oracle/Interfaces/IPIDController.sol';
import"../../ERC20/IWETH.sol";
import"./CollateralPoolLibrary.sol";
contractCollateralPoolisReentrancyGuard{
// accumulated interest rate: 0.000002739726639247/* ========== STATE VARIABLES ========== */addresspublic WETH;
addresspublic smartcontract_owner;
addresspublic xsd_contract_address;
addresspublic bankx_contract_address;
addresspublic xsdweth_pool;
addresspublic bankxweth_pool;
addresspublic pid_address;
BankXToken private BankX;
XSDStablecoin private XSD;
IPIDController private pid_controller;
uint256public collat_XSD;
boolpublic mint_paused;
boolpublic redeem_paused;
boolpublic buyback_paused;
structMintInfo {
uint256 accum_interest; //accumulated interest from previous mintsuint256 interest_rate; //interest rate at that particular timestampuint256 time; //last timestampuint256 amount; //XSD amount minted
}
mapping(address=>MintInfo) public mintMapping;
mapping (address=>uint256) public redeemBankXBalances;
mapping (address=>uint256) public redeemCollateralBalances;
mapping (address=>uint256) public vestingtimestamp;
uint256public unclaimedPoolCollateral;
uint256public unclaimedPoolBankX;
uint256public collateral_equivalent_d18;
uint256public bankx_minted_count;
mapping (address=>uint256) public lastRedeemed;
// Number of blocks to wait before being able to collectRedemption()uint256public redemption_delay =2;
/* ========== MODIFIERS ========== */modifieronlyByOwner() {
require(msg.sender== smartcontract_owner, "Not owner");
_;
}
/* ========== CONSTRUCTOR ========== */constructor(address _xsd_contract_address,
address _bankx_contract_address,
address _bankxweth_pool,
address _xsdweth_pool,
address _WETH,
address _smartcontract_owner
) {
require(
(_xsd_contract_address !=address(0))
&& (_bankx_contract_address !=address(0))
&& (_WETH !=address(0))
&& (_bankxweth_pool !=address(0))
&& (_xsdweth_pool !=address(0))
, "Zero address detected");
XSD = XSDStablecoin(_xsd_contract_address);
BankX = BankXToken(_bankx_contract_address);
xsd_contract_address = _xsd_contract_address;
bankx_contract_address = _bankx_contract_address;
xsdweth_pool = _xsdweth_pool;
bankxweth_pool = _bankxweth_pool;
WETH = _WETH;
smartcontract_owner = _smartcontract_owner;
}
/* ========== VIEWS ========== *///only accept ETH via fallback function from the WETH contractreceive() externalpayable{
assert(msg.sender== WETH);
}
// Returns dollar value of collateral held in this XSD poolfunctioncollatDollarBalance() publicviewreturns (uint256) {
return ((IWETH(WETH).balanceOf(address(this))*XSD.eth_usd_price())/(1e6));
}
// Returns the value of excess collateral held in this XSD pool, compared to what is needed to maintain the global collateral ratiofunctionavailableExcessCollatDV() publicviewreturns (uint256) {
uint256 global_collateral_ratio = XSD.global_collateral_ratio();
uint256 global_collat_value = XSD.globalCollateralValue();
if (global_collateral_ratio > (1e6)) global_collateral_ratio = (1e6); // Handles an overcollateralized contract with CR > 1uint256 required_collat_dollar_value_d18 = ((collat_XSD)*global_collateral_ratio*(XSD.xag_usd_price()*(1e4))/(311035))/(1e12); // Calculates collateral needed to back each 1 XSD with $1 of collateral at current collat ratioif ((global_collat_value-unclaimedPoolCollateral)>required_collat_dollar_value_d18) return (global_collat_value-unclaimedPoolCollateral-required_collat_dollar_value_d18);
elsereturn0;
}
/* ========== INTERNAL FUNCTIONS ======== */functionmintInterestCalc(uint xsd_amount,address sender) internal{
(mintMapping[sender].accum_interest, mintMapping[sender].interest_rate, mintMapping[sender].time, mintMapping[sender].amount) = CollateralPoolLibrary.calcMintInterest(xsd_amount,XSD.xag_usd_price(), XSD.interest_rate(), mintMapping[sender].accum_interest, mintMapping[sender].interest_rate, mintMapping[sender].time, mintMapping[sender].amount);
}
functionredeemInterestCalc(uint xsd_amount,address sender) internal{
(mintMapping[sender].accum_interest, mintMapping[sender].interest_rate, mintMapping[sender].time, mintMapping[sender].amount)=CollateralPoolLibrary.calcRedemptionInterest(xsd_amount,XSD.xag_usd_price(), mintMapping[sender].accum_interest, mintMapping[sender].interest_rate, mintMapping[sender].time, mintMapping[sender].amount);
}
/* ========== PUBLIC FUNCTIONS ========== */// We separate out the 1t1, fractional and algorithmic minting functions for gas efficiency functionmint1t1XSD(uint256 XSD_out_min) externalpayablenonReentrant{
require(!mint_paused, "Mint Paused");
require(msg.value>0, "Invalid collateral amount");
require(XSD.global_collateral_ratio() >= (1e6), "Collateral ratio must be >= 1");
(uint256 xsd_amount_d18) = CollateralPoolLibrary.calcMint1t1XSD(
XSD.eth_usd_price(),
XSD.xag_usd_price(),
msg.value
); //1 XSD for each $1 worth of collateralrequire(XSD_out_min <= xsd_amount_d18, "Slippage limit reached");
mintInterestCalc(xsd_amount_d18,msg.sender);
IWETH(WETH).deposit{value: msg.value}();
assert(IWETH(WETH).transfer(address(this), msg.value));
collat_XSD = collat_XSD + xsd_amount_d18;
XSD.pool_mint(msg.sender, xsd_amount_d18);
}
// 0% collateral-backedfunctionmintAlgorithmicXSD(uint256 bankx_amount_d18, uint256 XSD_out_min) externalnonReentrant{
require(!mint_paused, "Mint Paused");
uint256 bankx_price = XSD.bankx_price();
uint256 xag_usd_price = XSD.xag_usd_price();
//for testing//require(XSD.global_collateral_ratio() == 0, "Collateral ratio must be 0");
(uint256 xsd_amount_d18) = CollateralPoolLibrary.calcMintAlgorithmicXSD(
bankx_price, // X BankX / 1 USD
xag_usd_price,
bankx_amount_d18
);
require(XSD_out_min <= xsd_amount_d18, "Slippage limit reached");
mintInterestCalc(xsd_amount_d18,msg.sender);
collat_XSD = collat_XSD + xsd_amount_d18;
bankx_minted_count = bankx_minted_count + bankx_amount_d18;
BankX.pool_burn_from(msg.sender, bankx_amount_d18);
XSD.pool_mint(msg.sender, xsd_amount_d18);
}
// Will fail if fully collateralized or fully algorithmic// > 0% and < 100% collateral-backedfunctionmintFractionalXSD(uint256 bankx_amount, uint256 XSD_out_min) externalpayablenonReentrant{
require(!mint_paused, "Mint Paused");
uint256 xag_usd_price = XSD.xag_usd_price();
uint256 global_collateral_ratio = XSD.global_collateral_ratio();
require(global_collateral_ratio < (1e6) && global_collateral_ratio >0, "Collateral ratio needs to be between .000001 and .999999");
CollateralPoolLibrary.MintFF_Params memory input_params = CollateralPoolLibrary.MintFF_Params(
XSD.bankx_price(),
XSD.eth_usd_price(),
bankx_amount,
msg.value,
global_collateral_ratio
);
(uint256 mint_amount, uint256 bankx_needed) = CollateralPoolLibrary.calcMintFractionalXSD(input_params);
mint_amount = (mint_amount*31103477)/((xag_usd_price)); //grams of silver in calculated mint amountrequire(XSD_out_min <= mint_amount, "Slippage limit reached");
require(bankx_needed <= bankx_amount, "Not enough BankX inputted");
mintInterestCalc(mint_amount,msg.sender);
bankx_minted_count = bankx_minted_count + bankx_needed;
BankX.pool_burn_from(msg.sender, bankx_needed);
IWETH(WETH).deposit{value: msg.value}();
assert(IWETH(WETH).transfer(address(this), msg.value));
collat_XSD = collat_XSD + mint_amount;
XSD.pool_mint(msg.sender, mint_amount);
}
// Redeem collateral. 100% collateral-backedfunctionredeem1t1XSD(uint256 XSD_amount, uint256 COLLATERAL_out_min) externalnonReentrant{
require(!pid_controller.bucket3(), "Cannot withdraw in times of deficit");
require(!redeem_paused, "Redeem Paused");
require(XSD.global_collateral_ratio() == (1e6), "Collateral ratio must be == 1");
require(XSD_amount<=mintMapping[msg.sender].amount, "OVERREDEMPTION ERROR");
// convert xsd to $ and then to collateral value
(uint256 XSD_dollar,uint256 collateral_needed) = CollateralPoolLibrary.calcRedeem1t1XSD(
XSD.eth_usd_price(),
XSD.xag_usd_price(),
XSD_amount
);
uint total_xsd_amount = mintMapping[msg.sender].amount;
require(collateral_needed <= (IWETH(WETH).balanceOf(address(this))-unclaimedPoolCollateral), "Not enough collateral in pool");
require(COLLATERAL_out_min <= collateral_needed, "Slippage limit reached");
redeemInterestCalc(XSD_amount, msg.sender);
uint current_accum_interest = (XSD_amount*mintMapping[msg.sender].accum_interest)/total_xsd_amount;
redeemBankXBalances[msg.sender] = (redeemBankXBalances[msg.sender]+current_accum_interest);
redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender]+XSD_dollar;
unclaimedPoolCollateral = unclaimedPoolCollateral+XSD_dollar;
lastRedeemed[msg.sender] =block.number;
unclaimedPoolBankX = (unclaimedPoolBankX+current_accum_interest);
uint256 bankx_amount = (current_accum_interest*1e6)/XSD.bankx_price();
// Move all external functions to the end
collat_XSD -= XSD_amount;
mintMapping[msg.sender].accum_interest = (mintMapping[msg.sender].accum_interest - current_accum_interest);
XSD.pool_burn_from(msg.sender, XSD_amount);
BankX.pool_mint(address(this), bankx_amount);
}
// Will fail if fully collateralized or algorithmic// Redeem XSD for collateral and BankX. > 0% and < 100% collateral-backedfunctionredeemFractionalXSD(uint256 XSD_amount, uint256 BankX_out_min, uint256 COLLATERAL_out_min) externalnonReentrant{
require(!pid_controller.bucket3(), "Cannot withdraw in times of deficit");
require(!redeem_paused, "Redeem Paused");
require(XSD_amount<=mintMapping[msg.sender].amount, "OVERREDEMPTION ERROR");
uint256 xag_usd_price = XSD.xag_usd_price();
uint256 global_collateral_ratio = XSD.global_collateral_ratio();
require(global_collateral_ratio < (1e6) && global_collateral_ratio >0, "Collateral ratio needs to be between .000001 and .999999");
uint256 bankx_dollar_value_d18 = XSD_amount - ((XSD_amount*global_collateral_ratio)/(1e6));
bankx_dollar_value_d18 = (bankx_dollar_value_d18*xag_usd_price)/(31103477);
uint256 bankx_amount = (bankx_dollar_value_d18*1e6)/XSD.bankx_price();
// Get dollar value required and then convert to collateral value(WETH)uint256 collateral_dollar_value = (XSD_amount*global_collateral_ratio)/(1e6);
collateral_dollar_value = (collateral_dollar_value*xag_usd_price)/31103477;
uint256 collateral_amount = (collateral_dollar_value*1e6)/XSD.eth_usd_price();
require(collateral_amount <= (IWETH(WETH).balanceOf(address(this))-unclaimedPoolCollateral), "Not enough collateral in pool");
require(COLLATERAL_out_min <= collateral_amount, "Slippage limit reached [collateral]");
require(BankX_out_min <= bankx_amount, "Slippage limit reached [BankX]");
redeemCollateralBalances[msg.sender] = redeemCollateralBalances[msg.sender]+collateral_dollar_value;
unclaimedPoolCollateral = unclaimedPoolCollateral+collateral_dollar_value;
lastRedeemed[msg.sender] =block.number;
uint total_xsd_amount = mintMapping[msg.sender].amount;
redeemInterestCalc(XSD_amount, msg.sender);
uint current_accum_interest = (XSD_amount*mintMapping[msg.sender].accum_interest)/total_xsd_amount;
redeemBankXBalances[msg.sender] = redeemBankXBalances[msg.sender]+current_accum_interest;
bankx_amount = bankx_amount + ((current_accum_interest*1e6)/XSD.bankx_price());
mintMapping[msg.sender].accum_interest = mintMapping[msg.sender].accum_interest - current_accum_interest;
redeemBankXBalances[msg.sender] = redeemBankXBalances[msg.sender]+bankx_dollar_value_d18;
unclaimedPoolBankX = unclaimedPoolBankX+bankx_dollar_value_d18+current_accum_interest;
collat_XSD -= XSD_amount;
// Move all external functions to the end
XSD.pool_burn_from(msg.sender, XSD_amount);
BankX.pool_mint(address(this), bankx_amount);
}
// Redeem XSD for BankX. 0% collateral-backedfunctionredeemAlgorithmicXSD(uint256 XSD_amount, uint256 BankX_out_min) externalnonReentrant{
require(!pid_controller.bucket3(), "Cannot withdraw in times of deficit");
require(!redeem_paused, "Redeem Paused");
require(XSD_amount<=mintMapping[msg.sender].amount, "OVERREDEMPTION ERROR");
uint256 bankx_price = XSD.bankx_price();
//for testing//require(XSD.global_collateral_ratio() == 0, "Collateral ratio must be 0"); uint256 bankx_dollar_value_d18 = (XSD_amount*XSD.xag_usd_price())/(31103477);
uint256 bankx_amount = (bankx_dollar_value_d18*1e6)/bankx_price;
lastRedeemed[msg.sender] =block.number;
uint total_xsd_amount = mintMapping[msg.sender].amount;
require(BankX_out_min <= bankx_amount, "Slippage limit reached");
redeemInterestCalc(XSD_amount, msg.sender);
uint current_accum_interest = XSD_amount*mintMapping[msg.sender].accum_interest/total_xsd_amount; //precision of 6
redeemBankXBalances[msg.sender] = (redeemBankXBalances[msg.sender]+current_accum_interest);
bankx_amount = bankx_amount + ((current_accum_interest*1e6)/bankx_price);
mintMapping[msg.sender].accum_interest = (mintMapping[msg.sender].accum_interest - current_accum_interest);
redeemBankXBalances[msg.sender] = redeemBankXBalances[msg.sender]+bankx_dollar_value_d18;
unclaimedPoolBankX = unclaimedPoolBankX+bankx_dollar_value_d18+current_accum_interest;
collat_XSD -= XSD_amount;
// Move all external functions to the end
XSD.pool_burn_from(msg.sender, XSD_amount);
BankX.pool_mint(address(this), bankx_amount);
}
// After a redemption happens, transfer the newly minted BankX and owed collateral from this pool// contract to the user. Redemption is split into two functions to prevent flash loans from being able// to take out XSD/collateral from the system, use an AMM to trade the new price, and then mint back into the system.functioncollectRedemption() externalnonReentrant{
require(!pid_controller.bucket3(), "Cannot withdraw in times of deficit");
require(!redeem_paused, "Redeem Paused");
require((lastRedeemed[msg.sender]+(redemption_delay)) <=block.number, "Must wait for redemption_delay blocks before collecting redemption");
//require(redeemCollateralBalances[msg.sender]< , "Not enough collateral in the pool");//check for bucket and revert if there is a deficit.// add address parameteruint BankXDollarAmount;
uint CollateralDollarAmount;
uint BankXAmount;
uint CollateralAmount;
// Use Checks-Effects-Interactions patternif(redeemBankXBalances[msg.sender] >0){
BankXDollarAmount = redeemBankXBalances[msg.sender];
BankXAmount = (BankXDollarAmount*1e6)/XSD.bankx_price();
redeemBankXBalances[msg.sender] =0;
unclaimedPoolBankX = unclaimedPoolBankX-BankXDollarAmount;
TransferHelper.safeTransfer(address(BankX), msg.sender, BankXAmount);
}
if(redeemCollateralBalances[msg.sender] >0){
CollateralDollarAmount = redeemCollateralBalances[msg.sender];
CollateralAmount = (CollateralDollarAmount*1e6)/XSD.eth_usd_price();
redeemCollateralBalances[msg.sender] =0;
unclaimedPoolCollateral = unclaimedPoolCollateral-CollateralDollarAmount;
IWETH(WETH).withdraw(CollateralAmount); //try to unwrap eth in the redeem
TransferHelper.safeTransferETH(msg.sender, CollateralAmount);
}
}
// Function can be called by an BankX holder to have the protocol buy back BankX with excess collateral value from a desired collateral pool// This can also happen if the collateral ratio > 1// add XSD as a burn option while uXSD value is positive// need two seperate functions: one for bankx and one for XSDfunctionbuyBackBankX(uint256 BankX_amount,uint256 COLLATERAL_out_min) external{
require(!buyback_paused, "Buyback Paused");
CollateralPoolLibrary.BuybackBankX_Params memory input_params = CollateralPoolLibrary.BuybackBankX_Params(
availableExcessCollatDV(),
XSD.bankx_price(),
XSD.eth_usd_price(),
BankX_amount
);
(collateral_equivalent_d18) = (CollateralPoolLibrary.calcBuyBackBankX(input_params));
require(COLLATERAL_out_min <= collateral_equivalent_d18, "Slippage limit reached");
// Give the sender their desired collateral and burn the BankX
BankX.pool_burn_from(msg.sender, BankX_amount);
TransferHelper.safeTransfer(address(WETH), address(this), collateral_equivalent_d18);
IWETH(WETH).withdraw(collateral_equivalent_d18);
TransferHelper.safeTransferETH(msg.sender, collateral_equivalent_d18);
}
//buyback with XSD instead of bankxfunctionbuyBackXSD(uint256 XSD_amount, uint256 collateral_out_min) external{
require(!buyback_paused, "Buyback Paused");
if(XSD_amount !=0) require((XSD.totalSupply()+XSD_amount)>collat_XSD, "uXSD MUST BE POSITIVE");
CollateralPoolLibrary.BuybackXSD_Params memory input_params = CollateralPoolLibrary.BuybackXSD_Params(
availableExcessCollatDV(),
XSD.xsd_price(),
XSD.eth_usd_price(),
XSD_amount
);
(collateral_equivalent_d18) = (CollateralPoolLibrary.calcBuyBackXSD(input_params));
require(collateral_out_min <= collateral_equivalent_d18, "Slippage limit reached");
XSD.pool_burn_from(msg.sender, XSD_amount);
TransferHelper.safeTransfer(address(WETH), address(this), collateral_equivalent_d18);
IWETH(WETH).withdraw(collateral_equivalent_d18);
TransferHelper.safeTransferETH(msg.sender, collateral_equivalent_d18);
}
// Combined into one function due to 24KiB contract memory limitfunctionsetPoolParameters(uint256 new_redemption_delay, bool _mint_paused, bool _redeem_paused, bool _buyback_paused) externalonlyByOwner{
redemption_delay = new_redemption_delay;
mint_paused = _mint_paused;
redeem_paused = _redeem_paused;
buyback_paused = _buyback_paused;
emit PoolParametersSet(new_redemption_delay);
}
functionsetPIDController(address new_pid_address) externalonlyByOwner{
pid_controller = IPIDController(new_pid_address);
pid_address = new_pid_address;
}
functionsetSmartContractOwner(address _smartcontract_owner) external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
require(msg.sender!=address(0), "Zero address detected");
smartcontract_owner = _smartcontract_owner;
}
functionrenounceOwnership() external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
smartcontract_owner =address(0);
}
functionresetAddresses(address _xsd_contract_address,
address _bankx_contract_address,
address _bankxweth_pool,
address _xsdweth_pool,
address _WETH) external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
require(
(_xsd_contract_address !=address(0))
&& (_bankx_contract_address !=address(0))
&& (_WETH !=address(0))
&& (_bankxweth_pool !=address(0))
&& (_xsdweth_pool !=address(0))
, "Zero address detected");
XSD = XSDStablecoin(_xsd_contract_address);
BankX = BankXToken(_bankx_contract_address);
xsd_contract_address = _xsd_contract_address;
bankx_contract_address = _bankx_contract_address;
xsdweth_pool = _xsdweth_pool;
bankxweth_pool = _bankxweth_pool;
WETH = _WETH;
}
/* ========== EVENTS ========== */eventPoolParametersSet(uint256 new_redemption_delay);
}
Contract Source Code
File 6 of 19: CollateralPoolLibrary.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;libraryCollateralPoolLibrary{
// ================ Structs ================// Needed to lower stack sizestructMintFF_Params {
uint256 bankx_price_usd;
uint256 col_price_usd;
uint256 bankx_amount;
uint256 collateral_amount;
uint256 col_ratio;
}
structBuybackBankX_Params {
uint256 excess_collateral_dollar_value_d18;
uint256 bankx_price_usd;
uint256 col_price_usd;
uint256 BankX_amount;
}
structBuybackXSD_Params {
uint256 excess_collateral_dollar_value_d18;
uint256 xsd_price_usd;
uint256 col_price_usd;
uint256 XSD_amount;
}
// ================ Functions ================// xsd is at the price of one gram of silver.functioncalcMint1t1XSD(uint256 col_price, uint256 silver_price, uint256 collateral_amount_d18) publicpurereturns (uint256) {
uint256 gram_price = (silver_price*(1e4))/(311035);
return (collateral_amount_d18*(col_price))/(gram_price);
}
// xsd is at the price of one gram of silverfunctioncalcMintAlgorithmicXSD(uint256 bankx_price_usd, uint256 silver_price, uint256 bankx_amount_d18) publicpurereturns (uint256) {
uint256 gram_price = (silver_price*(1e4))/(311035);
return (bankx_amount_d18*bankx_price_usd)/(gram_price);
}
//new-code-startfunctioncalcMintInterest(uint256 XSD_amount,uint256 silver_price,uint256 rate, uint256 accum_interest, uint256 interest_rate, uint256 time, uint256 amount) internalviewreturns(uint256, uint256, uint256, uint256) {
uint256 gram_price = (silver_price*(1e4))/(311035);
if(time ==0){
interest_rate = rate;
amount = XSD_amount;
time =block.timestamp;
}
else{
uint delta_t =block.timestamp- time;
delta_t = delta_t/(86400); //1 day = 86400
accum_interest = accum_interest+((amount*gram_price*interest_rate*delta_t)/(365*(1e12)));
//check calculation for interest rate - done
interest_rate = (amount*interest_rate) + (XSD_amount*rate);//weighted average interest calculation is in two parts
amount = amount+XSD_amount;
interest_rate = interest_rate/amount;//second part
time =block.timestamp;
}
return (
accum_interest,
interest_rate,
time,
amount
);
}
functioncalcRedemptionInterest(uint256 XSD_amount,uint256 silver_price, uint256 accum_interest, uint256 interest_rate, uint256 time, uint256 amount) internalviewreturns(uint256, uint256, uint256, uint256){
uint256 gram_price = (silver_price*(1e4))/(311035);
uint delta_t =block.timestamp- time;
delta_t = delta_t/(86400);
accum_interest = accum_interest+((amount*gram_price*interest_rate*delta_t)/(365*(1e12)));
amount = amount - XSD_amount;
time =block.timestamp;
return (
accum_interest,
interest_rate,
time,
amount
);
}
//new-code-end// Must be internal because of the struct// xsd must be the dollar value of one price of silverfunctioncalcMintFractionalXSD(MintFF_Params memory params) internalpurereturns (uint256, uint256) {
// Since solidity truncates division, every division operation must be the last operation in the equation to ensure minimum error// The contract must check the proper ratio was sent to mint XSD. We do this by seeing the minimum mintable XSD based on each amount uint256 bankx_dollar_value_d18;
uint256 c_dollar_value_d18;
// Scoping for stack concerns
{
// USD amounts of the collateral and the BankX
bankx_dollar_value_d18 = params.bankx_amount*(params.bankx_price_usd)/(1e6);
c_dollar_value_d18 = params.collateral_amount*(params.col_price_usd)/(1e6);
}
uint calculated_bankx_dollar_value_d18 =
(c_dollar_value_d18*(1e6)/(params.col_ratio))
-(c_dollar_value_d18);
uint calculated_bankx_needed = calculated_bankx_dollar_value_d18*(1e6)/(params.bankx_price_usd);
return (
(c_dollar_value_d18+calculated_bankx_dollar_value_d18),
calculated_bankx_needed
);
}
functioncalcRedeem1t1XSD(uint256 col_price_usd,uint256 silver_price, uint256 XSD_amount) publicpurereturns (uint256,uint256) {
uint256 gram_price = (silver_price*(1e4))/(311035);
return ((XSD_amount*gram_price/1e6),((XSD_amount*gram_price)/col_price_usd));
}
// Must be internal because of the structfunctioncalcBuyBackBankX(BuybackBankX_Params memory params) internalpurereturns (uint256) {
// If the total collateral value is higher than the amount required at the current collateral ratio then buy back up to the possible BankX with the desired collateralrequire(params.excess_collateral_dollar_value_d18 >0, "No excess collateral to buy back!");
// Make sure not to take more than is availableuint256 bankx_dollar_value_d18 = (params.BankX_amount*params.bankx_price_usd);
require((bankx_dollar_value_d18/1e6) <= params.excess_collateral_dollar_value_d18, "You are trying to buy back more than the excess!");
// Get the equivalent amount of collateral based on the market value of BankX provided uint256 collateral_equivalent_d18 = (bankx_dollar_value_d18)/(params.col_price_usd);
//collateral_equivalent_d18 = collateral_equivalent_d18-((collateral_equivalent_d18*(params.buyback_fee))/(1e6));return (
collateral_equivalent_d18
);
}
functioncalcBuyBackXSD(BuybackXSD_Params memory params) internalpurereturns (uint256) {
require(params.excess_collateral_dollar_value_d18 >0, "No excess collateral to buy back!");
uint256 xsd_dollar_value_d18 = params.XSD_amount*(params.xsd_price_usd);
require((xsd_dollar_value_d18/1e6) <= params.excess_collateral_dollar_value_d18, "You are trying to buy more than the excess!");
uint256 collateral_equivalent_d18 = (xsd_dollar_value_d18)/(params.col_price_usd);
return (
collateral_equivalent_d18
);
}
}
Contract Source Code
File 7 of 19: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 8 of 19: ERC20.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/ERC20.sol)pragmasolidity ^0.8.0;import"./IERC20.sol";
import"./extensions/IERC20Metadata.sol";
import"../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/contractERC20isContext, IERC20, IERC20Metadata{
mapping(address=>uint256) private _balances;
mapping(address=>mapping(address=>uint256)) private _allowances;
uint256private _totalSupply;
stringprivate _name;
stringprivate _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/constructor(stringmemory name_, stringmemory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/functionname() publicviewvirtualoverridereturns (stringmemory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/functionsymbol() publicviewvirtualoverridereturns (stringmemory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/functiondecimals() publicviewvirtualoverridereturns (uint8) {
return18;
}
/**
* @dev See {IERC20-totalSupply}.
*/functiontotalSupply() publicviewvirtualoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/functionbalanceOf(address account) publicviewvirtualoverridereturns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/functiontransfer(address to, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
returntrue;
}
/**
* @dev See {IERC20-allowance}.
*/functionallowance(address owner, address spender) publicviewvirtualoverridereturns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionapprove(address spender, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
returntrue;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualoverridereturns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
returntrue;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionincreaseAllowance(address spender, uint256 addedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
returntrue;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/functiondecreaseAllowance(address spender, uint256 subtractedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
returntrue;
}
/**
* @dev Moves `amount` of tokens from `sender` to `recipient`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/function_transfer(addressfrom,
address to,
uint256 amount
) internalvirtual{
require(from!=address(0), "ERC20: transfer from the zero address");
require(to !=address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/function_mint(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/function_burn(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/function_approve(address owner,
address spender,
uint256 amount
) internalvirtual{
require(owner !=address(0), "ERC20: approve from the zero address");
require(spender !=address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/function_spendAllowance(address owner,
address spender,
uint256 amount
) internalvirtual{
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance !=type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_beforeTokenTransfer(addressfrom,
address to,
uint256 amount
) internalvirtual{}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_afterTokenTransfer(addressfrom,
address to,
uint256 amount
) internalvirtual{}
}
Contract Source Code
File 9 of 19: ERC20Custom.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"@openzeppelin/contracts/utils/Context.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"@openzeppelin/contracts/utils/math/SafeMath.sol";
// Due to compiling issues, _name, _symbol, and _decimals were removed/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20Mintable}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/contractERC20CustomisContext, IERC20{
usingSafeMathforuint256;
mapping (address=>uint256) internal _balances;
mapping (address=>mapping (address=>uint256)) internal _allowances;
uint256private _totalSupply;
/**
* @dev See {IERC20-totalSupply}.
*/functiontotalSupply() publicviewoverridereturns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/functionbalanceOf(address account) publicviewoverridereturns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/functiontransfer(address to, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
returntrue;
}
/**
* @dev See {IERC20-allowance}.
*/functionallowance(address owner, address spender) publicviewvirtualoverridereturns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.approve(address spender, uint256 amount)
*/functionapprove(address spender, uint256 amount) publicvirtualoverridereturns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
returntrue;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualoverridereturns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
returntrue;
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/function_spendAllowance(address owner,
address spender,
uint256 amount
) internalvirtual{
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance !=type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/functionincreaseAllowance(address spender, uint256 addedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
returntrue;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/functiondecreaseAllowance(address spender, uint256 subtractedValue) publicvirtualreturns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
returntrue;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/function_transfer(addressfrom, address to, uint256 amount) internalvirtual{
require(from!=address(0), "ERC20: transfer from the zero address");
require(to !=address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/function_mint(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from the caller.
*
* See {ERC20-_burn}.
*/functionburn(uint256 amount) publicvirtual{
_burn(_msgSender(), amount);
}
/**
* @dev Destroys `amount` tokens from `account`, deducting from the caller's
* allowance.
*
* See {ERC20-_burn} and {ERC20-allowance}.
*
* Requirements:
*
* - the caller must have allowance for `accounts`'s tokens of at least
* `amount`.
*/functionburnFrom(address account, uint256 amount) publicvirtual{
uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance");
_approve(account, _msgSender(), decreasedAllowance);
_burn(account, amount);
}
/**
* @dev Transfers 'tokens' from 'account' to origin address, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/function_burn(address account, uint256 amount) internalvirtual{
require(account !=address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/function_approve(address owner,
address spender,
uint256 amount
) internalvirtual{
require(owner !=address(0), "ERC20: approve from the zero address");
require(spender !=address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Destroys `amount` tokens from `account`.`amount` is then deducted
* from the caller's allowance.
*
* See {_burn} and {_approve}.
*/function_burnFrom(address account, uint256 amount) internalvirtual{
_burn(account, amount);
_approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance"));
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of `from`'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of `from`'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:using-hooks.adoc[Using Hooks].
*/function_beforeTokenTransfer(addressfrom, address to, uint256 amount) internalvirtual{ }
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/function_afterTokenTransfer(addressfrom,
address to,
uint256 amount
) internalvirtual{}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)pragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address to, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(addressfrom,
address to,
uint256 amount
) externalreturns (bool);
}
Contract Source Code
File 12 of 19: IERC20Metadata.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)pragmasolidity ^0.8.0;import"../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/interfaceIERC20MetadataisIERC20{
/**
* @dev Returns the name of the token.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the symbol of the token.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the decimals places of the token.
*/functiondecimals() externalviewreturns (uint8);
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)pragmasolidity ^0.8.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
// On the first call to nonReentrant, _notEntered will be truerequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Contract Source Code
File 17 of 19: SafeMath.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)pragmasolidity ^0.8.0;// CAUTION// This version of SafeMath should only be used with Solidity 0.8 or later,// because it relies on the compiler's built in overflow checks./**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/librarySafeMath{
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the// benefit is lost if 'b' is also tested.// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
unchecked {
if (b ==0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(uint256 a, uint256 b) internalpurereturns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functiondiv(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
unchecked {
require(b >0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/functionmod(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
unchecked {
require(b >0, errorMessage);
return a % b;
}
}
}
Contract Source Code
File 18 of 19: TransferHelper.sol
// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity >=0.6.0;// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/falselibraryTransferHelper{
functionsafeApprove(address token,
address to,
uint256 value
) internal{
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length==0||abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
functionsafeTransfer(address token,
address to,
uint256 value
) internal{
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length==0||abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
functionsafeTransferFrom(address token,
addressfrom,
address to,
uint256 value
) internal{
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytesmemory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length==0||abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
functionsafeTransferETH(address to, uint256 value) internal{
(bool success, ) = to.call{value: value}(newbytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}
Contract Source Code
File 19 of 19: XSDStablecoin.sol
// SPDX-License-Identifier: GPL-2.0-or-laterpragmasolidity ^0.8.0;import"@openzeppelin/contracts/utils/Context.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"../ERC20/ERC20Custom.sol";
import"@openzeppelin/contracts/token/ERC20/ERC20.sol";
import"../BankX/BankXToken.sol";
import"./Pools/CollateralPool.sol";
import"./Pools/Interfaces/IBankXWETHpool.sol";
import"./Pools/Interfaces/IXSDWETHpool.sol";
import"../Oracle/ChainlinkETHUSDPriceConsumer.sol";
import"../Oracle/ChainlinkXAGUSDPriceConsumer.sol";
contractXSDStablecoinisERC20Custom{
/* ========== STATE VARIABLES ========== *///change genesis mint numbersenumPriceChoice { XSD, BankX }
ChainlinkETHUSDPriceConsumer private eth_usd_pricer;
ChainlinkXAGUSDPriceConsumer private xag_usd_pricer;
uint8private eth_usd_pricer_decimals;
uint8private xag_usd_pricer_decimals;
stringpublic symbol;
stringpublic name;
uint8publicconstant decimals =18;
addresspublic pid_address; // pid controller addressaddresspublic treasury; //stores the genesis supplyaddresspublic collateral_pool_address;
addresspublic router;
addresspublic eth_usd_oracle_address;
addresspublic xag_usd_oracle_address;
addresspublic smartcontract_owner;
// interest rate related variablesuint256public interest_rate;
IBankXWETHpool private bankxEthPool;
IXSDWETHpool private xsdEthPool;
uint256public cap_rate;
// genesis mint: 17M//mint cap at 40Muint256public genesis_supply;
// The addresses in this array are added by the oracle and these contracts are able to mint xsdaddress[] public xsd_pools_array;
// Mapping is also used for faster verificationmapping(address=>bool) public xsd_pools;
// Constants for various precisionsuint256privateconstant PRICE_PRECISION =1e6;
uint256public global_collateral_ratio; // 6 decimals of precision, e.g. 924102 = 0.924102uint256public xsd_step; // Amount to change the collateralization ratio by upon refreshCollateralRatio()uint256public refresh_cooldown; // Seconds to wait before being able to run refreshCollateralRatio() againuint256public price_target; // The price of XSD at which the collateral ratio will respond to; this value is only used for the collateral ratio mechanism and not for minting and redeeming which are hardcoded at $1uint256public price_band; // The bound above and below the price target at which the refreshCollateralRatio() will not change the collateral ratioboolpublic collateral_ratio_paused =false;
/* ========== MODIFIERS ========== */modifieronlyPools() {
require(xsd_pools[msg.sender] ==true, "Only xsd pools can call this function");
_;//check happens before the function is executed
}
modifieronlyByOwner(){
require(msg.sender== smartcontract_owner, "You are not the owner");
_;
}
modifieronlyByOwnerPID() {
require(msg.sender== smartcontract_owner ||msg.sender== pid_address, "You are not the owner or the pid controller");
_;
}
modifieronlyByOwnerOrPool() {
require(
msg.sender== smartcontract_owner
|| xsd_pools[msg.sender] ==true,
"You are not the owner or a pool");
_;
}
/* ========== CONSTRUCTOR ========== */constructor(stringmemory _name,
stringmemory _symbol,
uint256 _pool_amount,
uint256 _genesis_supply,
address _smartcontract_owner,
address _treasury,
uint256 _cap_rate
) {
require((_smartcontract_owner !=address(0))
&& (_treasury !=address(0)), "Zero address detected");
name = _name;
symbol = _symbol;
genesis_supply = _genesis_supply + _pool_amount;
treasury = _treasury;
_mint(_smartcontract_owner, _pool_amount);
_mint(treasury, _genesis_supply);
smartcontract_owner = _smartcontract_owner;
xsd_step =2500; // 6 decimals of precision, equal to 0.25%
global_collateral_ratio =1000000; // XSD system starts off fully collateralized (6 decimals of precision)
interest_rate =52800; //interest rate starts off at 5%
refresh_cooldown =3600; // Refresh cooldown period is set to 1 hour (3600 seconds) at genesis
price_target =800000; // Change price target to 1 gram of silver
price_band =5000; // Collateral ratio will not adjust if 0.005 off target at genesis
cap_rate = _cap_rate;// Maximum mint amount
}
//change price target to reflect true value of silver/* ========== VIEWS ========== */// Choice = 'XSD' or 'BankX' for nowfunctionpool_price(PriceChoice choice) internalviewreturns (uint256) {
// Get the ETH / USD price first, and cut it down to 1e6 precisionuint256 _eth_usd_price = (uint256(eth_usd_pricer.getLatestPrice())*PRICE_PRECISION)/(uint256(10) ** eth_usd_pricer_decimals);
uint256 price_vs_eth =0;
uint256 reserve0;
uint256 reserve1;
if (choice == PriceChoice.XSD) {
(reserve0, reserve1, ) = xsdEthPool.getReserves();
if(reserve0 ==0|| reserve1 ==0){
return1;
}
price_vs_eth = reserve0/(reserve1); // How much XSD if you put in 1 WETH
}
elseif (choice == PriceChoice.BankX) {
(reserve0, reserve1, ) = bankxEthPool.getReserves();
if(reserve0 ==0|| reserve1 ==0){
return1;
}
price_vs_eth = reserve0/(reserve1); // How much BankX if you put in 1 WETH
}
elserevert("INVALID PRICE CHOICE. Needs to be either 0 (XSD) or 1 (BankX)");
// Will be in 1e6 formatreturn _eth_usd_price/price_vs_eth;
}
// Returns X XSD = 1 USD//XSD pricefunctionxsd_price() publicviewreturns (uint256) {
return pool_price(PriceChoice.XSD);
}
// Returns X BankX = 1 USDfunctionbankx_price() publicviewreturns (uint256) {
return pool_price(PriceChoice.BankX);
}
functioneth_usd_price() publicviewreturns (uint256) {
return (uint256(eth_usd_pricer.getLatestPrice())*PRICE_PRECISION)/(uint256(10) ** eth_usd_pricer_decimals);
}
//silver price//hard coded value for testing on goerlifunctionxag_usd_price() publicviewreturns (uint256) {
return (uint256(xag_usd_pricer.getLatestPrice())*PRICE_PRECISION)/(uint256(10) ** xag_usd_pricer_decimals);
}
// This is needed to avoid costly repeat calls to different getter functions// It is cheaper gas-wise to just dump everything and only use some of the infofunctionxsd_info() publicviewreturns (uint256, uint256, uint256, uint256, uint256, uint256) {
return (
pool_price(PriceChoice.XSD), // xsd_price()
pool_price(PriceChoice.BankX), // bankx_price()
totalSupply(), // totalSupply()
global_collateral_ratio, // global_collateral_ratio()
globalCollateralValue(), // globalCollateralValue
(uint256(eth_usd_pricer.getLatestPrice())*PRICE_PRECISION)/(uint256(10) ** eth_usd_pricer_decimals) //eth_usd_price
);
}
// Iterate through all xsd pools and calculate all value of collateral in all pools globally functionglobalCollateralValue() publicviewreturns (uint256) {
uint256 collateral_amount =0;
collateral_amount = CollateralPool(payable(collateral_pool_address)).collatDollarBalance();
return collateral_amount;
}
/* ========== PUBLIC FUNCTIONS ========== */// There needs to be a time interval that this can be called. Otherwise it can be called multiple times per expansion.// To simulate global collateral ratio set xsd price higher than silver price and hit refresh collateral ratio.uint256public last_call_time; // Last time the refreshCollateralRatio function was calledfunctionrefreshCollateralRatio() public{
require(collateral_ratio_paused ==false, "Collateral Ratio has been paused");
uint256 xsd_price_cur = xsd_price();
require(block.timestamp- last_call_time >= refresh_cooldown, "Must wait for the refresh cooldown since last refresh");
// Step increments are 0.25% (upon genesis, changable by setXSDStep()) if (xsd_price_cur > (price_target+price_band)) { //decrease collateral ratioif(global_collateral_ratio <= xsd_step){ //if within a step of 0, go to 0
global_collateral_ratio =0;
} else {
global_collateral_ratio = global_collateral_ratio-xsd_step;
}
} elseif (xsd_price_cur < price_target-price_band) { //increase collateral ratioif(global_collateral_ratio+xsd_step >=1000000){
global_collateral_ratio =1000000; // cap collateral ratio at 1.000000
} else {
global_collateral_ratio = global_collateral_ratio+xsd_step;
}
}
else
last_call_time =block.timestamp; // Set the time of the last expansionuint256 _interest_rate = (1000000-global_collateral_ratio)/(2);
//update interest rateif(_interest_rate>52800){
interest_rate = _interest_rate;
}
else{
interest_rate =52800;
}
emit CollateralRatioRefreshed(global_collateral_ratio);
}
functioncreatorMint(uint256 amount) publiconlyByOwner{
require(genesis_supply+amount<cap_rate,"cap limit reached");
super._mint(treasury,amount);
}
/* ========== RESTRICTED FUNCTIONS ========== */// Used by pools when user redeemsfunctionpool_burn_from(address b_address, uint256 b_amount) publiconlyPools{
super._burnFrom(b_address, b_amount);
emit XSDBurned(b_address, msg.sender, b_amount);
}
// This function is what other xsd pools will call to mint new XSD functionpool_mint(address m_address, uint256 m_amount) publiconlyPools{
super._mint(m_address, m_amount);
emit XSDMinted(msg.sender, m_address, m_amount);
}
// Adds collateral addresses supported, such as tether and busd, must be ERC20 functionaddPool(address pool_address) publiconlyByOwner{
require(pool_address !=address(0), "Zero address detected");
require(xsd_pools[pool_address] ==false, "Address already exists");
xsd_pools[pool_address] =true;
xsd_pools_array.push(pool_address);
emit PoolAdded(pool_address);
}
// Remove a pool functionremovePool(address pool_address) publiconlyByOwner{
require(pool_address !=address(0), "Zero address detected");
require(xsd_pools[pool_address] ==true, "Address nonexistant");
// Delete from the mappingdelete xsd_pools[pool_address];
// 'Delete' from the array by setting the address to 0x0for (uint i =0; i < xsd_pools_array.length; i++){
if (xsd_pools_array[i] == pool_address) {
xsd_pools_array[i] =address(0); // This will leave a null in the array and keep the indices the samebreak;
}
}
emit PoolRemoved(pool_address);
}
// create a seperate function for users and the poolfunctionburnpoolXSD(uint _xsdamount) public{
//uXSD = totalSupply - collat_XSDrequire(msg.sender== router, "Only the router can access this function");
require(totalSupply()-CollateralPool(payable(collateral_pool_address)).collat_XSD()>_xsdamount, "uXSD has to be positive");
super._burn(address(xsdEthPool),_xsdamount);
xsdEthPool.sync();
emit XSDBurned(msg.sender, address(this), _xsdamount);
}
// add burn function for usersfunctionburnUserXSD(uint _xsdamount) public{
require(totalSupply()-CollateralPool(payable(collateral_pool_address)).collat_XSD()>_xsdamount, "uXSD has to be positive");
super._burn(msg.sender, _xsdamount);
emit XSDBurned(msg.sender, address(this), _xsdamount);
}
functionsetXSDStep(uint256 _new_step) publiconlyByOwnerPID{
xsd_step = _new_step;
emit XSDStepSet(_new_step);
}
functionsetPriceTarget (uint256 _new_price_target) publiconlyByOwnerPID{
price_target = _new_price_target;
emit PriceTargetSet(_new_price_target);
}
functionsetRefreshCooldown(uint256 _new_cooldown) publiconlyByOwnerPID{
refresh_cooldown = _new_cooldown;
emit RefreshCooldownSet(_new_cooldown);
}
functionsetTreasury(address _new_treasury) publiconlyByOwner{
require(_new_treasury !=address(0), "Zero address detected");
treasury = _new_treasury;
}
functionsetETHUSDOracle(address _eth_usd_oracle_address) publiconlyByOwner{
require(_eth_usd_oracle_address !=address(0), "Zero address detected");
eth_usd_oracle_address = _eth_usd_oracle_address;
eth_usd_pricer = ChainlinkETHUSDPriceConsumer(eth_usd_oracle_address);
eth_usd_pricer_decimals = eth_usd_pricer.getDecimals();
emit ETHUSDOracleSet(_eth_usd_oracle_address);
}
functionsetXAGUSDOracle(address _xag_usd_oracle_address) publiconlyByOwner{
require(_xag_usd_oracle_address !=address(0), "Zero address detected");
xag_usd_oracle_address = _xag_usd_oracle_address;
xag_usd_pricer = ChainlinkXAGUSDPriceConsumer(xag_usd_oracle_address);
xag_usd_pricer_decimals = xag_usd_pricer.getDecimals();
emit XAGUSDOracleSet(_xag_usd_oracle_address);
}
functionsetPIDController(address _pid_address) externalonlyByOwner{
require(_pid_address !=address(0), "Zero address detected");
pid_address = _pid_address;
emit PIDControllerSet(_pid_address);
}
functionsetRouterAddress(address _router) externalonlyByOwner{
require(_router !=address(0), "Zero address detected");
router = _router;
}
functionsetPriceBand(uint256 _price_band) externalonlyByOwner{
price_band = _price_band;
emit PriceBandSet(_price_band);
}
// Sets the XSD_ETH Uniswap oracle address functionsetXSDEthPool(address _xsd_pool_addr) publiconlyByOwner{
require(_xsd_pool_addr !=address(0), "Zero address detected");
xsdEthPool = IXSDWETHpool(_xsd_pool_addr);
emit XSDETHPoolSet(_xsd_pool_addr);
}
// Sets the BankX_ETH Uniswap oracle address functionsetBankXEthPool(address _bankx_pool_addr) publiconlyByOwner{
require(_bankx_pool_addr !=address(0), "Zero address detected");
bankxEthPool = IBankXWETHpool(_bankx_pool_addr);
emit BankXEthPoolSet(_bankx_pool_addr);
}
//sets the collateral pool addressfunctionsetCollateralEthPool(address _collateral_pool_address) publiconlyByOwner{
require(_collateral_pool_address !=address(0), "Zero address detected");
collateral_pool_address =payable(_collateral_pool_address);
}
functionsetSmartContractOwner(address _smartcontract_owner) external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
require(msg.sender!=address(0), "Zero address detected");
smartcontract_owner = _smartcontract_owner;
}
functionrenounceOwnership() external{
require(msg.sender== smartcontract_owner, "Only the smart contract owner can access this function");
smartcontract_owner =address(0);
}
/* ========== EVENTS ========== */// Track XSD burnedeventXSDBurned(addressindexedfrom, addressindexed to, uint256 amount);
// Track XSD mintedeventXSDMinted(addressindexedfrom, addressindexed to, uint256 amount);
eventCollateralRatioRefreshed(uint256 global_collateral_ratio);
eventPoolAdded(address pool_address);
eventPoolRemoved(address pool_address);
eventRedemptionFeeSet(uint256 red_fee);
eventMintingFeeSet(uint256 min_fee);
eventXSDStepSet(uint256 new_step);
eventPriceTargetSet(uint256 new_price_target);
eventRefreshCooldownSet(uint256 new_cooldown);
eventETHUSDOracleSet(address eth_usd_oracle_address);
eventXAGUSDOracleSet(address xag_usd_oracle_address);
eventPIDControllerSet(address _pid_controller);
eventPriceBandSet(uint256 price_band);
eventXSDETHPoolSet(address xsd_pool_addr);
eventBankXEthPoolSet(address bankx_pool_addr);
eventCollateralRatioToggled(bool collateral_ratio_paused);
}