Transaction Hash:
Block:
11394199 at Dec-05-2020 06:15:07 PM +UTC
Transaction Fee:
0.001586897164426943 ETH
$3.13
Gas Used:
122,069 Gas / 13.000001347 Gwei
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x00000000...0A3f67E9a | (MEV Bot: 0x000...E9a) | ||||
| 0x0000002C...8481A9dE0 |
0.575686084890956348 Eth
Nonce: 44055
|
0.574099187726529405 Eth
Nonce: 44056
| 0.001586897164426943 | ||
|
0xB3b7874F...A3505D8d4
Miner
| (Babel Pool) | 2,938.49686084542175286 Eth | 2,938.498447742586179803 Eth | 0.001586897164426943 |
Execution Trace
MEV Bot: 0x000...E9a.00009808( )
KyberReserve.getConversionRate( src=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, dest=0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e, srcQty=2497279488899168601, blockNumber=11394199 ) => ( 962934925141120154574 )-
LiquidityConversionRates.getRate( conversionToken=0x8400D94A5cb0fa0D041a3788e395285d61c9ee5e, currentBlockNumber=11394199, buy=True, qtyInSrcWei=2497279488899168601 ) => ( 962934925141120154574 )
UniBrightToken.allowance( _from=0x3c100e43cc0715B659d5e7F8C67a9aac7dD170C0, _spender=0xfe06bc8BC12595C1c871fF7c2ea9CadC42735d7D ) => ( 9999997375827085851557 )
-
EToken2.allowance( _from=0x3c100e43cc0715B659d5e7F8C67a9aac7dD170C0, _spender=0xfe06bc8BC12595C1c871fF7c2ea9CadC42735d7D, _symbol=554E420000000000000000000000000000000000000000000000000000000000 ) => ( 9999997375827085851557 )
-
-
-
UniswapV2Pair.STATICCALL( )
File 1 of 5: KyberReserve
File 2 of 5: LiquidityConversionRates
File 3 of 5: UniBrightToken
File 4 of 5: EToken2
File 5 of 5: UniswapV2Pair
// File: contracts/ERC20Interface.sol
pragma solidity 0.4.18;
// https://github.com/ethereum/EIPs/issues/20
interface ERC20 {
function totalSupply() public view returns (uint supply);
function balanceOf(address _owner) public view returns (uint balance);
function transfer(address _to, uint _value) public returns (bool success);
function transferFrom(address _from, address _to, uint _value) public returns (bool success);
function approve(address _spender, uint _value) public returns (bool success);
function allowance(address _owner, address _spender) public view returns (uint remaining);
function decimals() public view returns(uint digits);
event Approval(address indexed _owner, address indexed _spender, uint _value);
}
// File: contracts/Utils.sol
pragma solidity 0.4.18;
/// @title Kyber constants contract
contract Utils {
ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);
uint constant internal PRECISION = (10**18);
uint constant internal MAX_QTY = (10**28); // 10B tokens
uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH
uint constant internal MAX_DECIMALS = 18;
uint constant internal ETH_DECIMALS = 18;
mapping(address=>uint) internal decimals;
function setDecimals(ERC20 token) internal {
if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;
else decimals[token] = token.decimals();
}
function getDecimals(ERC20 token) internal view returns(uint) {
if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
uint tokenDecimals = decimals[token];
// technically, there might be token with decimals 0
// moreover, very possible that old tokens have decimals 0
// these tokens will just have higher gas fees.
if(tokenDecimals == 0) return token.decimals();
return tokenDecimals;
}
function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(srcQty <= MAX_QTY);
require(rate <= MAX_RATE);
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
}
}
function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {
require(dstQty <= MAX_QTY);
require(rate <= MAX_RATE);
//source quantity is rounded up. to avoid dest quantity being too low.
uint numerator;
uint denominator;
if (srcDecimals >= dstDecimals) {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
denominator = rate;
} else {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS);
numerator = (PRECISION * dstQty);
denominator = (rate * (10**(dstDecimals - srcDecimals)));
}
return (numerator + denominator - 1) / denominator; //avoid rounding down errors
}
}
// File: contracts/PermissionGroups.sol
pragma solidity 0.4.18;
contract PermissionGroups {
address public admin;
address public pendingAdmin;
mapping(address=>bool) internal operators;
mapping(address=>bool) internal alerters;
address[] internal operatorsGroup;
address[] internal alertersGroup;
uint constant internal MAX_GROUP_SIZE = 50;
function PermissionGroups() public {
admin = msg.sender;
}
modifier onlyAdmin() {
require(msg.sender == admin);
_;
}
modifier onlyOperator() {
require(operators[msg.sender]);
_;
}
modifier onlyAlerter() {
require(alerters[msg.sender]);
_;
}
function getOperators () external view returns(address[]) {
return operatorsGroup;
}
function getAlerters () external view returns(address[]) {
return alertersGroup;
}
event TransferAdminPending(address pendingAdmin);
/**
* @dev Allows the current admin to set the pendingAdmin address.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdmin(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(pendingAdmin);
pendingAdmin = newAdmin;
}
/**
* @dev Allows the current admin to set the admin in one tx. Useful initial deployment.
* @param newAdmin The address to transfer ownership to.
*/
function transferAdminQuickly(address newAdmin) public onlyAdmin {
require(newAdmin != address(0));
TransferAdminPending(newAdmin);
AdminClaimed(newAdmin, admin);
admin = newAdmin;
}
event AdminClaimed( address newAdmin, address previousAdmin);
/**
* @dev Allows the pendingAdmin address to finalize the change admin process.
*/
function claimAdmin() public {
require(pendingAdmin == msg.sender);
AdminClaimed(pendingAdmin, admin);
admin = pendingAdmin;
pendingAdmin = address(0);
}
event AlerterAdded (address newAlerter, bool isAdd);
function addAlerter(address newAlerter) public onlyAdmin {
require(!alerters[newAlerter]); // prevent duplicates.
require(alertersGroup.length < MAX_GROUP_SIZE);
AlerterAdded(newAlerter, true);
alerters[newAlerter] = true;
alertersGroup.push(newAlerter);
}
function removeAlerter (address alerter) public onlyAdmin {
require(alerters[alerter]);
alerters[alerter] = false;
for (uint i = 0; i < alertersGroup.length; ++i) {
if (alertersGroup[i] == alerter) {
alertersGroup[i] = alertersGroup[alertersGroup.length - 1];
alertersGroup.length--;
AlerterAdded(alerter, false);
break;
}
}
}
event OperatorAdded(address newOperator, bool isAdd);
function addOperator(address newOperator) public onlyAdmin {
require(!operators[newOperator]); // prevent duplicates.
require(operatorsGroup.length < MAX_GROUP_SIZE);
OperatorAdded(newOperator, true);
operators[newOperator] = true;
operatorsGroup.push(newOperator);
}
function removeOperator (address operator) public onlyAdmin {
require(operators[operator]);
operators[operator] = false;
for (uint i = 0; i < operatorsGroup.length; ++i) {
if (operatorsGroup[i] == operator) {
operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];
operatorsGroup.length -= 1;
OperatorAdded(operator, false);
break;
}
}
}
}
// File: contracts/Withdrawable.sol
pragma solidity 0.4.18;
/**
* @title Contracts that should be able to recover tokens or ethers
* @author Ilan Doron
* @dev This allows to recover any tokens or Ethers received in a contract.
* This will prevent any accidental loss of tokens.
*/
contract Withdrawable is PermissionGroups {
event TokenWithdraw(ERC20 token, uint amount, address sendTo);
/**
* @dev Withdraw all ERC20 compatible tokens
* @param token ERC20 The address of the token contract
*/
function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {
require(token.transfer(sendTo, amount));
TokenWithdraw(token, amount, sendTo);
}
event EtherWithdraw(uint amount, address sendTo);
/**
* @dev Withdraw Ethers
*/
function withdrawEther(uint amount, address sendTo) external onlyAdmin {
sendTo.transfer(amount);
EtherWithdraw(amount, sendTo);
}
}
// File: contracts/ConversionRatesInterface.sol
pragma solidity 0.4.18;
interface ConversionRatesInterface {
function recordImbalance(
ERC20 token,
int buyAmount,
uint rateUpdateBlock,
uint currentBlock
)
public;
function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);
}
// File: contracts/SanityRatesInterface.sol
pragma solidity 0.4.18;
interface SanityRatesInterface {
function getSanityRate(ERC20 src, ERC20 dest) public view returns(uint);
}
// File: contracts/KyberReserveInterface.sol
pragma solidity 0.4.18;
/// @title Kyber Reserve contract
interface KyberReserveInterface {
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool);
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint);
}
// File: contracts/reserves/KyberReserve.sol
pragma solidity 0.4.18;
/// @title Kyber Reserve contract
contract KyberReserve is KyberReserveInterface, Withdrawable, Utils {
address public kyberNetwork;
bool public tradeEnabled;
ConversionRatesInterface public conversionRatesContract;
SanityRatesInterface public sanityRatesContract;
mapping(bytes32=>bool) public approvedWithdrawAddresses; // sha3(token,address)=>bool
mapping(address=>address) public tokenWallet;
function KyberReserve(address _kyberNetwork, ConversionRatesInterface _ratesContract, address _admin) public {
require(_admin != address(0));
require(_ratesContract != address(0));
require(_kyberNetwork != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _ratesContract;
admin = _admin;
tradeEnabled = true;
}
event DepositToken(ERC20 token, uint amount);
function() public payable {
DepositToken(ETH_TOKEN_ADDRESS, msg.value);
}
event TradeExecute(
address indexed origin,
address src,
uint srcAmount,
address destToken,
uint destAmount,
address destAddress
);
function trade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
public
payable
returns(bool)
{
require(tradeEnabled);
require(msg.sender == kyberNetwork);
require(doTrade(srcToken, srcAmount, destToken, destAddress, conversionRate, validate));
return true;
}
event TradeEnabled(bool enable);
function enableTrade() public onlyAdmin returns(bool) {
tradeEnabled = true;
TradeEnabled(true);
return true;
}
function disableTrade() public onlyAlerter returns(bool) {
tradeEnabled = false;
TradeEnabled(false);
return true;
}
event WithdrawAddressApproved(ERC20 token, address addr, bool approve);
function approveWithdrawAddress(ERC20 token, address addr, bool approve) public onlyAdmin {
approvedWithdrawAddresses[keccak256(token, addr)] = approve;
WithdrawAddressApproved(token, addr, approve);
setDecimals(token);
if ((tokenWallet[token] == address(0x0)) && (token != ETH_TOKEN_ADDRESS)) {
tokenWallet[token] = this; // by default
require(token.approve(this, 2 ** 255));
}
}
event NewTokenWallet(ERC20 token, address wallet);
function setTokenWallet(ERC20 token, address wallet) public onlyAdmin {
require(wallet != address(0x0));
tokenWallet[token] = wallet;
NewTokenWallet(token, wallet);
}
event WithdrawFunds(ERC20 token, uint amount, address destination);
function withdraw(ERC20 token, uint amount, address destination) public onlyOperator returns(bool) {
require(approvedWithdrawAddresses[keccak256(token, destination)]);
if (token == ETH_TOKEN_ADDRESS) {
destination.transfer(amount);
} else {
require(token.transferFrom(tokenWallet[token], destination, amount));
}
WithdrawFunds(token, amount, destination);
return true;
}
event SetContractAddresses(address network, address rate, address sanity);
function setContracts(
address _kyberNetwork,
ConversionRatesInterface _conversionRates,
SanityRatesInterface _sanityRates
)
public
onlyAdmin
{
require(_kyberNetwork != address(0));
require(_conversionRates != address(0));
kyberNetwork = _kyberNetwork;
conversionRatesContract = _conversionRates;
sanityRatesContract = _sanityRates;
SetContractAddresses(kyberNetwork, conversionRatesContract, sanityRatesContract);
}
////////////////////////////////////////////////////////////////////////////
/// status functions ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
function getBalance(ERC20 token) public view returns(uint) {
if (token == ETH_TOKEN_ADDRESS)
return this.balance;
else {
address wallet = tokenWallet[token];
uint balanceOfWallet = token.balanceOf(wallet);
uint allowanceOfWallet = token.allowance(wallet, this);
return (balanceOfWallet < allowanceOfWallet) ? balanceOfWallet : allowanceOfWallet;
}
}
function getDestQty(ERC20 src, ERC20 dest, uint srcQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcDstQty(srcQty, srcDecimals, dstDecimals, rate);
}
function getSrcQty(ERC20 src, ERC20 dest, uint dstQty, uint rate) public view returns(uint) {
uint dstDecimals = getDecimals(dest);
uint srcDecimals = getDecimals(src);
return calcSrcQty(dstQty, srcDecimals, dstDecimals, rate);
}
function getConversionRate(ERC20 src, ERC20 dest, uint srcQty, uint blockNumber) public view returns(uint) {
ERC20 token;
bool isBuy;
if (!tradeEnabled) return 0;
if (ETH_TOKEN_ADDRESS == src) {
isBuy = true;
token = dest;
} else if (ETH_TOKEN_ADDRESS == dest) {
isBuy = false;
token = src;
} else {
return 0; // pair is not listed
}
uint rate = conversionRatesContract.getRate(token, blockNumber, isBuy, srcQty);
uint destQty = getDestQty(src, dest, srcQty, rate);
if (getBalance(dest) < destQty) return 0;
if (sanityRatesContract != address(0)) {
uint sanityRate = sanityRatesContract.getSanityRate(src, dest);
if (rate > sanityRate) return 0;
}
return rate;
}
/// @dev do a trade
/// @param srcToken Src token
/// @param srcAmount Amount of src token
/// @param destToken Destination token
/// @param destAddress Destination address to send tokens to
/// @param validate If true, additional validations are applicable
/// @return true iff trade is successful
function doTrade(
ERC20 srcToken,
uint srcAmount,
ERC20 destToken,
address destAddress,
uint conversionRate,
bool validate
)
internal
returns(bool)
{
// can skip validation if done at kyber network level
if (validate) {
require(conversionRate > 0);
if (srcToken == ETH_TOKEN_ADDRESS)
require(msg.value == srcAmount);
else
require(msg.value == 0);
}
uint destAmount = getDestQty(srcToken, destToken, srcAmount, conversionRate);
// sanity check
require(destAmount > 0);
// add to imbalance
ERC20 token;
int tradeAmount;
if (srcToken == ETH_TOKEN_ADDRESS) {
tradeAmount = int(destAmount);
token = destToken;
} else {
tradeAmount = -1 * int(srcAmount);
token = srcToken;
}
conversionRatesContract.recordImbalance(
token,
tradeAmount,
0,
block.number
);
// collect src tokens
if (srcToken != ETH_TOKEN_ADDRESS) {
require(srcToken.transferFrom(msg.sender, tokenWallet[srcToken], srcAmount));
}
// send dest tokens
if (destToken == ETH_TOKEN_ADDRESS) {
destAddress.transfer(destAmount);
} else {
require(destToken.transferFrom(tokenWallet[destToken], destAddress, destAmount));
}
TradeExecute(msg.sender, srcToken, srcAmount, destToken, destAmount, destAddress);
return true;
}
}File 2 of 5: LiquidityConversionRates
{"ConversionRatesInterface.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\ninterface ConversionRatesInterface {\n\n function recordImbalance(\n ERC20 token,\n int buyAmount,\n uint rateUpdateBlock,\n uint currentBlock\n )\n public;\n\n function getRate(ERC20 token, uint currentBlockNumber, bool buy, uint qty) public view returns(uint);\n}\n"},"ERC20Interface.sol":{"content":"pragma solidity 0.4.18;\n\n\n// https://github.com/ethereum/EIPs/issues/20\ninterface ERC20 {\n function totalSupply() public view returns (uint supply);\n function balanceOf(address _owner) public view returns (uint balance);\n function transfer(address _to, uint _value) public returns (bool success);\n function transferFrom(address _from, address _to, uint _value) public returns (bool success);\n function approve(address _spender, uint _value) public returns (bool success);\n function allowance(address _owner, address _spender) public view returns (uint remaining);\n function decimals() public view returns(uint digits);\n event Approval(address indexed _owner, address indexed _spender, uint _value);\n}\n"},"LiquidityConversionRates.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"../../ConversionRatesInterface.sol\";\nimport \"../../Withdrawable.sol\";\nimport \"../../Utils.sol\";\nimport \"./LiquidityFormula.sol\";\n\n\ncontract LiquidityConversionRates is ConversionRatesInterface, LiquidityFormula, Withdrawable, Utils {\n\n uint constant FORMULA_PRECISION_BITS = 40;\n\n ERC20 public token;\n address public reserveContract;\n\n uint public numFpBits;\n uint public formulaPrecision;\n\n uint public rInFp;\n uint public pMinInFp;\n\n uint public maxEthCapBuyInFp;\n uint public maxEthCapSellInFp;\n uint public maxQtyInFp;\n\n uint public feeInBps;\n uint public collectedFeesInTwei = 0;\n\n uint public maxBuyRateInPrecision;\n uint public minBuyRateInPrecision;\n uint public maxSellRateInPrecision;\n uint public minSellRateInPrecision;\n\n function LiquidityConversionRates(address _admin, ERC20 _token) public {\n transferAdminQuickly(_admin);\n token = _token;\n setDecimals(token);\n require(getDecimals(token) \u003c= MAX_DECIMALS);\n }\n\n event ReserveAddressSet(address reserve);\n\n function setReserveAddress(address reserve) public onlyAdmin {\n reserveContract = reserve;\n ReserveAddressSet(reserve);\n }\n\n event LiquidityParamsSet(\n uint rInFp,\n uint pMinInFp,\n uint numFpBits,\n uint maxCapBuyInFp,\n uint maxEthCapSellInFp,\n uint feeInBps,\n uint formulaPrecision,\n uint maxQtyInFp,\n uint maxBuyRateInPrecision,\n uint minBuyRateInPrecision,\n uint maxSellRateInPrecision,\n uint minSellRateInPrecision\n );\n\n function setLiquidityParams(\n uint _rInFp,\n uint _pMinInFp,\n uint _numFpBits,\n uint _maxCapBuyInWei,\n uint _maxCapSellInWei,\n uint _feeInBps,\n uint _maxTokenToEthRateInPrecision,\n uint _minTokenToEthRateInPrecision\n ) public onlyAdmin {\n require(_numFpBits == FORMULA_PRECISION_BITS); // only used config, but keep in API\n formulaPrecision = uint(1)\u003c\u003c_numFpBits; // require(formulaPrecision \u003c= MAX_QTY)\n require(_feeInBps \u003c 10000);\n require(_minTokenToEthRateInPrecision \u003c _maxTokenToEthRateInPrecision);\n require(_minTokenToEthRateInPrecision \u003e 0);\n require(_rInFp \u003e 0);\n require(_pMinInFp \u003e 0);\n\n rInFp = _rInFp;\n pMinInFp = _pMinInFp;\n maxQtyInFp = fromWeiToFp(MAX_QTY);\n numFpBits = _numFpBits;\n maxEthCapBuyInFp = fromWeiToFp(_maxCapBuyInWei);\n maxEthCapSellInFp = fromWeiToFp(_maxCapSellInWei);\n feeInBps = _feeInBps;\n maxBuyRateInPrecision = PRECISION * PRECISION / _minTokenToEthRateInPrecision;\n minBuyRateInPrecision = PRECISION * PRECISION / _maxTokenToEthRateInPrecision;\n maxSellRateInPrecision = _maxTokenToEthRateInPrecision;\n minSellRateInPrecision = _minTokenToEthRateInPrecision;\n\n LiquidityParamsSet(\n rInFp,\n pMinInFp,\n numFpBits,\n maxEthCapBuyInFp,\n maxEthCapSellInFp,\n feeInBps,\n formulaPrecision,\n maxQtyInFp,\n maxBuyRateInPrecision,\n minBuyRateInPrecision,\n maxSellRateInPrecision,\n minSellRateInPrecision\n );\n }\n\n function recordImbalance(\n ERC20 conversionToken,\n int buyAmountInTwei,\n uint rateUpdateBlock,\n uint currentBlock\n )\n public\n {\n conversionToken;\n rateUpdateBlock;\n currentBlock;\n\n require(msg.sender == reserveContract);\n if (buyAmountInTwei \u003e 0) {\n // Buy case\n collectedFeesInTwei += calcCollectedFee(abs(buyAmountInTwei));\n } else {\n // Sell case\n collectedFeesInTwei += abs(buyAmountInTwei) * feeInBps / 10000;\n }\n }\n\n event CollectedFeesReset(uint resetFeesInTwei);\n\n function resetCollectedFees() public onlyAdmin {\n uint resetFeesInTwei = collectedFeesInTwei;\n collectedFeesInTwei = 0;\n\n CollectedFeesReset(resetFeesInTwei);\n }\n\n function getRate(\n ERC20 conversionToken,\n uint currentBlockNumber,\n bool buy,\n uint qtyInSrcWei\n ) public view returns(uint) {\n\n currentBlockNumber;\n\n require(qtyInSrcWei \u003c= MAX_QTY);\n uint eInFp = fromWeiToFp(reserveContract.balance);\n uint rateInPrecision = getRateWithE(conversionToken, buy, qtyInSrcWei, eInFp);\n require(rateInPrecision \u003c= MAX_RATE);\n return rateInPrecision;\n }\n\n function getRateWithE(ERC20 conversionToken, bool buy, uint qtyInSrcWei, uint eInFp) public view returns(uint) {\n uint deltaEInFp;\n uint sellInputTokenQtyInFp;\n uint deltaTInFp;\n uint rateInPrecision;\n\n require(qtyInSrcWei \u003c= MAX_QTY);\n require(eInFp \u003c= maxQtyInFp);\n if (conversionToken != token) return 0;\n\n if (buy) {\n // ETH goes in, token goes out\n deltaEInFp = fromWeiToFp(qtyInSrcWei);\n if (deltaEInFp \u003e maxEthCapBuyInFp) return 0;\n\n if (deltaEInFp == 0) {\n rateInPrecision = buyRateZeroQuantity(eInFp);\n } else {\n rateInPrecision = buyRate(eInFp, deltaEInFp);\n }\n } else {\n sellInputTokenQtyInFp = fromTweiToFp(qtyInSrcWei);\n deltaTInFp = valueAfterReducingFee(sellInputTokenQtyInFp);\n if (deltaTInFp == 0) {\n rateInPrecision = sellRateZeroQuantity(eInFp);\n deltaEInFp = 0;\n } else {\n (rateInPrecision, deltaEInFp) = sellRate(eInFp, sellInputTokenQtyInFp, deltaTInFp);\n }\n\n if (deltaEInFp \u003e maxEthCapSellInFp) return 0;\n }\n\n rateInPrecision = rateAfterValidation(rateInPrecision, buy);\n return rateInPrecision;\n }\n\n function rateAfterValidation(uint rateInPrecision, bool buy) public view returns(uint) {\n uint minAllowRateInPrecision;\n uint maxAllowedRateInPrecision;\n\n if (buy) {\n minAllowRateInPrecision = minBuyRateInPrecision;\n maxAllowedRateInPrecision = maxBuyRateInPrecision;\n } else {\n minAllowRateInPrecision = minSellRateInPrecision;\n maxAllowedRateInPrecision = maxSellRateInPrecision;\n }\n\n if ((rateInPrecision \u003e maxAllowedRateInPrecision) || (rateInPrecision \u003c minAllowRateInPrecision)) {\n return 0;\n } else if (rateInPrecision \u003e MAX_RATE) {\n return 0;\n } else {\n return rateInPrecision;\n }\n }\n\n function buyRate(uint eInFp, uint deltaEInFp) public view returns(uint) {\n uint deltaTInFp = deltaTFunc(rInFp, pMinInFp, eInFp, deltaEInFp, formulaPrecision);\n require(deltaTInFp \u003c= maxQtyInFp);\n deltaTInFp = valueAfterReducingFee(deltaTInFp);\n return deltaTInFp * PRECISION / deltaEInFp;\n }\n\n function buyRateZeroQuantity(uint eInFp) public view returns(uint) {\n uint ratePreReductionInPrecision = formulaPrecision * PRECISION / pE(rInFp, pMinInFp, eInFp, formulaPrecision);\n return valueAfterReducingFee(ratePreReductionInPrecision);\n }\n\n function sellRate(\n uint eInFp,\n uint sellInputTokenQtyInFp,\n uint deltaTInFp\n ) public view returns(uint rateInPrecision, uint deltaEInFp) {\n deltaEInFp = deltaEFunc(rInFp, pMinInFp, eInFp, deltaTInFp, formulaPrecision, numFpBits);\n require(deltaEInFp \u003c= maxQtyInFp);\n rateInPrecision = deltaEInFp * PRECISION / sellInputTokenQtyInFp;\n }\n\n function sellRateZeroQuantity(uint eInFp) public view returns(uint) {\n uint ratePreReductionInPrecision = pE(rInFp, pMinInFp, eInFp, formulaPrecision) * PRECISION / formulaPrecision;\n return valueAfterReducingFee(ratePreReductionInPrecision);\n }\n\n function fromTweiToFp(uint qtyInTwei) public view returns(uint) {\n require(qtyInTwei \u003c= MAX_QTY);\n return qtyInTwei * formulaPrecision / (10 ** getDecimals(token));\n }\n\n function fromWeiToFp(uint qtyInwei) public view returns(uint) {\n require(qtyInwei \u003c= MAX_QTY);\n return qtyInwei * formulaPrecision / (10 ** ETH_DECIMALS);\n }\n\n function valueAfterReducingFee(uint val) public view returns(uint) {\n require(val \u003c= BIG_NUMBER);\n return ((10000 - feeInBps) * val) / 10000;\n }\n\n function calcCollectedFee(uint val) public view returns(uint) {\n require(val \u003c= MAX_QTY);\n return val * feeInBps / (10000 - feeInBps);\n }\n\n function abs(int val) public pure returns(uint) {\n if (val \u003c 0) {\n return uint(val * (-1));\n } else {\n return uint(val);\n }\n }\n}\n"},"LiquidityFormula.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract UtilMath {\n uint public constant BIG_NUMBER = (uint(1)\u003c\u003cuint(200));\n\n function checkMultOverflow(uint x, uint y) public pure returns(bool) {\n if (y == 0) return false;\n return (((x*y) / y) != x);\n }\n\n function compactFraction(uint p, uint q, uint precision) public pure returns (uint, uint) {\n if (q \u003c precision * precision) return (p, q);\n return compactFraction(p/precision, q/precision, precision);\n }\n\n /* solhint-disable code-complexity */\n function exp(uint p, uint q, uint precision) public pure returns (uint) {\n uint n = 0;\n uint nFact = 1;\n uint currentP = 1;\n uint currentQ = 1;\n\n uint sum = 0;\n uint prevSum = 0;\n\n while (true) {\n if (checkMultOverflow(currentP, precision)) return sum;\n if (checkMultOverflow(currentQ, nFact)) return sum;\n\n sum += (currentP * precision) / (currentQ * nFact);\n\n if (sum == prevSum) return sum;\n prevSum = sum;\n\n n++;\n\n if (checkMultOverflow(currentP, p)) return sum;\n if (checkMultOverflow(currentQ, q)) return sum;\n if (checkMultOverflow(nFact, n)) return sum;\n\n currentP *= p;\n currentQ *= q;\n nFact *= n;\n\n (currentP, currentQ) = compactFraction(currentP, currentQ, precision);\n }\n }\n /* solhint-enable code-complexity */\n\n function countLeadingZeros(uint p, uint q) public pure returns (uint) {\n uint denomator = (uint(1)\u003c\u003c255);\n for (int i = 255; i \u003e= 0; i--) {\n if ((q*denomator)/denomator != q) {\n // overflow\n denomator = denomator/2;\n continue;\n }\n if (p/(q*denomator) \u003e 0) return uint(i);\n denomator = denomator/2;\n }\n\n return uint(-1);\n }\n\n // log2 for a number that it in [1,2)\n function log2ForSmallNumber(uint x, uint numPrecisionBits) public pure returns (uint) {\n uint res = 0;\n uint one = (uint(1)\u003c\u003cnumPrecisionBits);\n uint two = 2 * one;\n uint addition = one;\n\n require((x \u003e= one) \u0026\u0026 (x \u003c= two));\n require(numPrecisionBits \u003c 125);\n\n for (uint i = numPrecisionBits; i \u003e 0; i--) {\n x = (x*x) / one;\n addition = addition/2;\n if (x \u003e= two) {\n x = x/2;\n res += addition;\n }\n }\n\n return res;\n }\n\n function logBase2 (uint p, uint q, uint numPrecisionBits) public pure returns (uint) {\n uint n = 0;\n uint precision = (uint(1)\u003c\u003cnumPrecisionBits);\n\n if (p \u003e q) {\n n = countLeadingZeros(p, q);\n }\n\n require(!checkMultOverflow(p, precision));\n require(!checkMultOverflow(n, precision));\n require(!checkMultOverflow(uint(1)\u003c\u003cn, q));\n\n uint y = p * precision / (q * (uint(1)\u003c\u003cn));\n uint log2Small = log2ForSmallNumber(y, numPrecisionBits);\n\n require(n*precision \u003c= BIG_NUMBER);\n require(log2Small \u003c= BIG_NUMBER);\n\n return n * precision + log2Small;\n }\n\n function ln(uint p, uint q, uint numPrecisionBits) public pure returns (uint) {\n uint ln2Numerator = 6931471805599453094172;\n uint ln2Denomerator = 10000000000000000000000;\n\n uint log2x = logBase2(p, q, numPrecisionBits);\n\n require(!checkMultOverflow(ln2Numerator, log2x));\n\n return ln2Numerator * log2x / ln2Denomerator;\n }\n}\n\n\ncontract LiquidityFormula is UtilMath {\n function pE(uint r, uint pMIn, uint e, uint precision) public pure returns (uint) {\n require(!checkMultOverflow(r, e));\n uint expRE = exp(r*e, precision*precision, precision);\n require(!checkMultOverflow(expRE, pMIn));\n return pMIn*expRE / precision;\n }\n\n function deltaTFunc(uint r, uint pMIn, uint e, uint deltaE, uint precision) public pure returns (uint) {\n uint pe = pE(r, pMIn, e, precision);\n uint rpe = r * pe;\n\n require(!checkMultOverflow(r, deltaE));\n uint erdeltaE = exp(r*deltaE, precision*precision, precision);\n\n require(erdeltaE \u003e= precision);\n require(!checkMultOverflow(erdeltaE - precision, precision));\n require(!checkMultOverflow((erdeltaE - precision)*precision, precision));\n require(!checkMultOverflow((erdeltaE - precision)*precision*precision, precision));\n require(!checkMultOverflow(rpe, erdeltaE));\n require(!checkMultOverflow(r, pe));\n\n return (erdeltaE - precision) * precision * precision * precision / (rpe*erdeltaE);\n }\n\n function deltaEFunc(uint r, uint pMIn, uint e, uint deltaT, uint precision, uint numPrecisionBits)\n public pure\n returns (uint)\n {\n uint pe = pE(r, pMIn, e, precision);\n uint rpe = r * pe;\n\n require(!checkMultOverflow(rpe, deltaT));\n require(precision * precision + rpe * deltaT/precision \u003e precision * precision);\n uint lnPart = ln(precision*precision + rpe*deltaT/precision, precision*precision, numPrecisionBits);\n\n require(!checkMultOverflow(r, pe));\n require(!checkMultOverflow(precision, precision));\n require(!checkMultOverflow(rpe, deltaT));\n require(!checkMultOverflow(lnPart, precision));\n\n return lnPart * precision / r;\n }\n}\n"},"PermissionGroups.sol":{"content":"pragma solidity 0.4.18;\n\n\ncontract PermissionGroups {\n\n address public admin;\n address public pendingAdmin;\n mapping(address=\u003ebool) internal operators;\n mapping(address=\u003ebool) internal alerters;\n address[] internal operatorsGroup;\n address[] internal alertersGroup;\n uint constant internal MAX_GROUP_SIZE = 50;\n\n function PermissionGroups() public {\n admin = msg.sender;\n }\n\n modifier onlyAdmin() {\n require(msg.sender == admin);\n _;\n }\n\n modifier onlyOperator() {\n require(operators[msg.sender]);\n _;\n }\n\n modifier onlyAlerter() {\n require(alerters[msg.sender]);\n _;\n }\n\n function getOperators () external view returns(address[]) {\n return operatorsGroup;\n }\n\n function getAlerters () external view returns(address[]) {\n return alertersGroup;\n }\n\n event TransferAdminPending(address pendingAdmin);\n\n /**\n * @dev Allows the current admin to set the pendingAdmin address.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdmin(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(pendingAdmin);\n pendingAdmin = newAdmin;\n }\n\n /**\n * @dev Allows the current admin to set the admin in one tx. Useful initial deployment.\n * @param newAdmin The address to transfer ownership to.\n */\n function transferAdminQuickly(address newAdmin) public onlyAdmin {\n require(newAdmin != address(0));\n TransferAdminPending(newAdmin);\n AdminClaimed(newAdmin, admin);\n admin = newAdmin;\n }\n\n event AdminClaimed( address newAdmin, address previousAdmin);\n\n /**\n * @dev Allows the pendingAdmin address to finalize the change admin process.\n */\n function claimAdmin() public {\n require(pendingAdmin == msg.sender);\n AdminClaimed(pendingAdmin, admin);\n admin = pendingAdmin;\n pendingAdmin = address(0);\n }\n\n event AlerterAdded (address newAlerter, bool isAdd);\n\n function addAlerter(address newAlerter) public onlyAdmin {\n require(!alerters[newAlerter]); // prevent duplicates.\n require(alertersGroup.length \u003c MAX_GROUP_SIZE);\n\n AlerterAdded(newAlerter, true);\n alerters[newAlerter] = true;\n alertersGroup.push(newAlerter);\n }\n\n function removeAlerter (address alerter) public onlyAdmin {\n require(alerters[alerter]);\n alerters[alerter] = false;\n\n for (uint i = 0; i \u003c alertersGroup.length; ++i) {\n if (alertersGroup[i] == alerter) {\n alertersGroup[i] = alertersGroup[alertersGroup.length - 1];\n alertersGroup.length--;\n AlerterAdded(alerter, false);\n break;\n }\n }\n }\n\n event OperatorAdded(address newOperator, bool isAdd);\n\n function addOperator(address newOperator) public onlyAdmin {\n require(!operators[newOperator]); // prevent duplicates.\n require(operatorsGroup.length \u003c MAX_GROUP_SIZE);\n\n OperatorAdded(newOperator, true);\n operators[newOperator] = true;\n operatorsGroup.push(newOperator);\n }\n\n function removeOperator (address operator) public onlyAdmin {\n require(operators[operator]);\n operators[operator] = false;\n\n for (uint i = 0; i \u003c operatorsGroup.length; ++i) {\n if (operatorsGroup[i] == operator) {\n operatorsGroup[i] = operatorsGroup[operatorsGroup.length - 1];\n operatorsGroup.length -= 1;\n OperatorAdded(operator, false);\n break;\n }\n }\n }\n}\n"},"Utils.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\n\n\n/// @title Kyber constants contract\ncontract Utils {\n\n ERC20 constant internal ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);\n uint constant internal PRECISION = (10**18);\n uint constant internal MAX_QTY = (10**28); // 10B tokens\n uint constant internal MAX_RATE = (PRECISION * 10**6); // up to 1M tokens per ETH\n uint constant internal MAX_DECIMALS = 18;\n uint constant internal ETH_DECIMALS = 18;\n mapping(address=\u003euint) internal decimals;\n\n function setDecimals(ERC20 token) internal {\n if (token == ETH_TOKEN_ADDRESS) decimals[token] = ETH_DECIMALS;\n else decimals[token] = token.decimals();\n }\n\n function getDecimals(ERC20 token) internal view returns(uint) {\n if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access\n uint tokenDecimals = decimals[token];\n // technically, there might be token with decimals 0\n // moreover, very possible that old tokens have decimals 0\n // these tokens will just have higher gas fees.\n if(tokenDecimals == 0) return token.decimals();\n\n return tokenDecimals;\n }\n\n function calcDstQty(uint srcQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(srcQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n\n if (dstDecimals \u003e= srcDecimals) {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;\n } else {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));\n }\n }\n\n function calcSrcQty(uint dstQty, uint srcDecimals, uint dstDecimals, uint rate) internal pure returns(uint) {\n require(dstQty \u003c= MAX_QTY);\n require(rate \u003c= MAX_RATE);\n \n //source quantity is rounded up. to avoid dest quantity being too low.\n uint numerator;\n uint denominator;\n if (srcDecimals \u003e= dstDecimals) {\n require((srcDecimals - dstDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));\n denominator = rate;\n } else {\n require((dstDecimals - srcDecimals) \u003c= MAX_DECIMALS);\n numerator = (PRECISION * dstQty);\n denominator = (rate * (10**(dstDecimals - srcDecimals)));\n }\n return (numerator + denominator - 1) / denominator; //avoid rounding down errors\n }\n}\n"},"Withdrawable.sol":{"content":"pragma solidity 0.4.18;\n\n\nimport \"./ERC20Interface.sol\";\nimport \"./PermissionGroups.sol\";\n\n\n/**\n * @title Contracts that should be able to recover tokens or ethers\n * @author Ilan Doron\n * @dev This allows to recover any tokens or Ethers received in a contract.\n * This will prevent any accidental loss of tokens.\n */\ncontract Withdrawable is PermissionGroups {\n\n event TokenWithdraw(ERC20 token, uint amount, address sendTo);\n\n /**\n * @dev Withdraw all ERC20 compatible tokens\n * @param token ERC20 The address of the token contract\n */\n function withdrawToken(ERC20 token, uint amount, address sendTo) external onlyAdmin {\n require(token.transfer(sendTo, amount));\n TokenWithdraw(token, amount, sendTo);\n }\n\n event EtherWithdraw(uint amount, address sendTo);\n\n /**\n * @dev Withdraw Ethers\n */\n function withdrawEther(uint amount, address sendTo) external onlyAdmin {\n sendTo.transfer(amount);\n EtherWithdraw(amount, sendTo);\n }\n}\n"}}File 3 of 5: UniBrightToken
pragma solidity 0.4.15;
contract RegistryICAPInterface {
function parse(bytes32 _icap) constant returns(address, bytes32, bool);
function institutions(bytes32 _institution) constant returns(address);
}
contract EToken2Interface {
function registryICAP() constant returns(RegistryICAPInterface);
function baseUnit(bytes32 _symbol) constant returns(uint8);
function description(bytes32 _symbol) constant returns(string);
function owner(bytes32 _symbol) constant returns(address);
function isOwner(address _owner, bytes32 _symbol) constant returns(bool);
function totalSupply(bytes32 _symbol) constant returns(uint);
function balanceOf(address _holder, bytes32 _symbol) constant returns(uint);
function isLocked(bytes32 _symbol) constant returns(bool);
function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) returns(bool);
function reissueAsset(bytes32 _symbol, uint _value) returns(bool);
function revokeAsset(bytes32 _symbol, uint _value) returns(bool);
function setProxy(address _address, bytes32 _symbol) returns(bool);
function lockAsset(bytes32 _symbol) returns(bool);
function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) returns(bool);
function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint);
function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) returns(bool);
}
contract AssetInterface {
function _performTransferWithReference(address _to, uint _value, string _reference, address _sender) returns(bool);
function _performTransferToICAPWithReference(bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function _performApprove(address _spender, uint _value, address _sender) returns(bool);
function _performTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
function _performTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function _performGeneric(bytes, address) payable {
revert();
}
}
contract ERC20Interface {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed from, address indexed spender, uint256 value);
function totalSupply() constant returns(uint256 supply);
function balanceOf(address _owner) constant returns(uint256 balance);
function transfer(address _to, uint256 _value) returns(bool success);
function transferFrom(address _from, address _to, uint256 _value) returns(bool success);
function approve(address _spender, uint256 _value) returns(bool success);
function allowance(address _owner, address _spender) constant returns(uint256 remaining);
// function symbol() constant returns(string);
function decimals() constant returns(uint8);
// function name() constant returns(string);
}
contract AssetProxyInterface {
function _forwardApprove(address _spender, uint _value, address _sender) returns(bool);
function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) returns(bool);
function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool);
function balanceOf(address _owner) constant returns(uint);
}
contract Bytes32 {
function _bytes32(string _input) internal constant returns(bytes32 result) {
assembly {
result := mload(add(_input, 32))
}
}
}
contract ReturnData {
function _returnReturnData(bool _success) internal {
assembly {
let returndatastart := msize()
mstore(0x40, add(returndatastart, returndatasize))
returndatacopy(returndatastart, 0, returndatasize)
switch _success case 0 { revert(returndatastart, returndatasize) } default { return(returndatastart, returndatasize) }
}
}
function _assemblyCall(address _destination, uint _value, bytes _data) internal returns(bool success) {
assembly {
success := call(div(mul(gas, 63), 64), _destination, _value, add(_data, 32), mload(_data), 0, 0)
}
}
}
/**
* @title EToken2 Asset Proxy.
*
* Proxy implements ERC20 interface and acts as a gateway to a single EToken2 asset.
* Proxy adds etoken2Symbol and caller(sender) when forwarding requests to EToken2.
* Every request that is made by caller first sent to the specific asset implementation
* contract, which then calls back to be forwarded onto EToken2.
*
* Calls flow: Caller ->
* Proxy.func(...) ->
* Asset._performFunc(..., Caller.address) ->
* Proxy._forwardFunc(..., Caller.address) ->
* Platform.proxyFunc(..., symbol, Caller.address)
*
* Generic call flow: Caller ->
* Proxy.unknownFunc(...) ->
* Asset._performGeneric(..., Caller.address) ->
* Asset.unknownFunc(...)
*
* Asset implementation contract is mutable, but each user have an option to stick with
* old implementation, through explicit decision made in timely manner, if he doesn't agree
* with new rules.
* Each user have a possibility to upgrade to latest asset contract implementation, without the
* possibility to rollback.
*
* Note: all the non constant functions return false instead of throwing in case if state change
* didn't happen yet.
*/
contract AssetProxy is ERC20Interface, AssetProxyInterface, Bytes32, ReturnData {
// Assigned EToken2, immutable.
EToken2Interface public etoken2;
// Assigned symbol, immutable.
bytes32 public etoken2Symbol;
// Assigned name, immutable. For UI.
string public name;
string public symbol;
/**
* Sets EToken2 address, assigns symbol and name.
*
* Can be set only once.
*
* @param _etoken2 EToken2 contract address.
* @param _symbol assigned symbol.
* @param _name assigned name.
*
* @return success.
*/
function init(EToken2Interface _etoken2, string _symbol, string _name) returns(bool) {
if (address(etoken2) != 0x0) {
return false;
}
etoken2 = _etoken2;
etoken2Symbol = _bytes32(_symbol);
name = _name;
symbol = _symbol;
return true;
}
/**
* Only EToken2 is allowed to call.
*/
modifier onlyEToken2() {
if (msg.sender == address(etoken2)) {
_;
}
}
/**
* Only current asset owner is allowed to call.
*/
modifier onlyAssetOwner() {
if (etoken2.isOwner(msg.sender, etoken2Symbol)) {
_;
}
}
/**
* Returns asset implementation contract for current caller.
*
* @return asset implementation contract.
*/
function _getAsset() internal returns(AssetInterface) {
return AssetInterface(getVersionFor(msg.sender));
}
function recoverTokens(uint _value) onlyAssetOwner() returns(bool) {
return this.transferWithReference(msg.sender, _value, 'Tokens recovery');
}
/**
* Returns asset total supply.
*
* @return asset total supply.
*/
function totalSupply() constant returns(uint) {
return etoken2.totalSupply(etoken2Symbol);
}
/**
* Returns asset balance for a particular holder.
*
* @param _owner holder address.
*
* @return holder balance.
*/
function balanceOf(address _owner) constant returns(uint) {
return etoken2.balanceOf(_owner, etoken2Symbol);
}
/**
* Returns asset allowance from one holder to another.
*
* @param _from holder that allowed spending.
* @param _spender holder that is allowed to spend.
*
* @return holder to spender allowance.
*/
function allowance(address _from, address _spender) constant returns(uint) {
return etoken2.allowance(_from, _spender, etoken2Symbol);
}
/**
* Returns asset decimals.
*
* @return asset decimals.
*/
function decimals() constant returns(uint8) {
return etoken2.baseUnit(etoken2Symbol);
}
/**
* Transfers asset balance from the caller to specified receiver.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transfer(address _to, uint _value) returns(bool) {
return transferWithReference(_to, _value, '');
}
/**
* Transfers asset balance from the caller to specified receiver adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferWithReference(address _to, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferWithReference(_to, _value, _reference, msg.sender);
}
/**
* Transfers asset balance from the caller to specified ICAP.
*
* @param _icap recipient ICAP to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferToICAP(bytes32 _icap, uint _value) returns(bool) {
return transferToICAPWithReference(_icap, _value, '');
}
/**
* Transfers asset balance from the caller to specified ICAP adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _icap recipient ICAP to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferToICAPWithReference(bytes32 _icap, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferToICAPWithReference(_icap, _value, _reference, msg.sender);
}
/**
* Prforms allowance transfer of asset balance between holders.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferFrom(address _from, address _to, uint _value) returns(bool) {
return transferFromWithReference(_from, _to, _value, '');
}
/**
* Prforms allowance transfer of asset balance between holders adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferFromWithReference(address _from, address _to, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferFromWithReference(_from, _to, _value, _reference, msg.sender);
}
/**
* Performs transfer call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardTransferFromWithReference(address _from, address _to, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyTransferFromWithReference(_from, _to, _value, etoken2Symbol, _reference, _sender);
}
/**
* Prforms allowance transfer of asset balance between holders.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
*
* @return success.
*/
function transferFromToICAP(address _from, bytes32 _icap, uint _value) returns(bool) {
return transferFromToICAPWithReference(_from, _icap, _value, '');
}
/**
* Prforms allowance transfer of asset balance between holders adding specified comment.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
*
* @return success.
*/
function transferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference) returns(bool) {
return _getAsset()._performTransferFromToICAPWithReference(_from, _icap, _value, _reference, msg.sender);
}
/**
* Performs allowance transfer to ICAP call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _from holder address to take from.
* @param _icap recipient ICAP address to give to.
* @param _value amount to transfer.
* @param _reference transfer comment to be included in a EToken2's Transfer event.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyTransferFromToICAPWithReference(_from, _icap, _value, _reference, _sender);
}
/**
* Sets asset spending allowance for a specified spender.
* Resolves asset implementation contract for the caller and forwards there arguments along with
* the caller address.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
*
* @return success.
*/
function approve(address _spender, uint _value) returns(bool) {
return _getAsset()._performApprove(_spender, _value, msg.sender);
}
/**
* Performs allowance setting call on the EToken2 by the name of specified sender.
*
* Can only be called by asset implementation contract assigned to sender.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
* @param _sender initial caller.
*
* @return success.
*/
function _forwardApprove(address _spender, uint _value, address _sender) onlyImplementationFor(_sender) returns(bool) {
return etoken2.proxyApprove(_spender, _value, etoken2Symbol, _sender);
}
/**
* Emits ERC20 Transfer event on this contract.
*
* Can only be, and, called by assigned EToken2 when asset transfer happens.
*/
function emitTransfer(address _from, address _to, uint _value) onlyEToken2() {
Transfer(_from, _to, _value);
}
/**
* Emits ERC20 Approval event on this contract.
*
* Can only be, and, called by assigned EToken2 when asset allowance set happens.
*/
function emitApprove(address _from, address _spender, uint _value) onlyEToken2() {
Approval(_from, _spender, _value);
}
/**
* Resolves asset implementation contract for the caller and forwards there transaction data,
* along with the value. This allows for proxy interface growth.
*/
function () payable {
_getAsset()._performGeneric.value(msg.value)(msg.data, msg.sender);
_returnReturnData(true);
}
// Interface functions to allow specifying ICAP addresses as strings.
function transferToICAP(string _icap, uint _value) returns(bool) {
return transferToICAPWithReference(_icap, _value, '');
}
function transferToICAPWithReference(string _icap, uint _value, string _reference) returns(bool) {
return transferToICAPWithReference(_bytes32(_icap), _value, _reference);
}
function transferFromToICAP(address _from, string _icap, uint _value) returns(bool) {
return transferFromToICAPWithReference(_from, _icap, _value, '');
}
function transferFromToICAPWithReference(address _from, string _icap, uint _value, string _reference) returns(bool) {
return transferFromToICAPWithReference(_from, _bytes32(_icap), _value, _reference);
}
/**
* Indicates an upgrade freeze-time start, and the next asset implementation contract.
*/
event UpgradeProposal(address newVersion);
// Current asset implementation contract address.
address latestVersion;
// Proposed next asset implementation contract address.
address pendingVersion;
// Upgrade freeze-time start.
uint pendingVersionTimestamp;
// Timespan for users to review the new implementation and make decision.
uint constant UPGRADE_FREEZE_TIME = 3 days;
// Asset implementation contract address that user decided to stick with.
// 0x0 means that user uses latest version.
mapping(address => address) userOptOutVersion;
/**
* Only asset implementation contract assigned to sender is allowed to call.
*/
modifier onlyImplementationFor(address _sender) {
if (getVersionFor(_sender) == msg.sender) {
_;
}
}
/**
* Returns asset implementation contract address assigned to sender.
*
* @param _sender sender address.
*
* @return asset implementation contract address.
*/
function getVersionFor(address _sender) constant returns(address) {
return userOptOutVersion[_sender] == 0 ? latestVersion : userOptOutVersion[_sender];
}
/**
* Returns current asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getLatestVersion() constant returns(address) {
return latestVersion;
}
/**
* Returns proposed next asset implementation contract address.
*
* @return asset implementation contract address.
*/
function getPendingVersion() constant returns(address) {
return pendingVersion;
}
/**
* Returns upgrade freeze-time start.
*
* @return freeze-time start.
*/
function getPendingVersionTimestamp() constant returns(uint) {
return pendingVersionTimestamp;
}
/**
* Propose next asset implementation contract address.
*
* Can only be called by current asset owner.
*
* Note: freeze-time should not be applied for the initial setup.
*
* @param _newVersion asset implementation contract address.
*
* @return success.
*/
function proposeUpgrade(address _newVersion) onlyAssetOwner() returns(bool) {
// Should not already be in the upgrading process.
if (pendingVersion != 0x0) {
return false;
}
// New version address should be other than 0x0.
if (_newVersion == 0x0) {
return false;
}
// Don't apply freeze-time for the initial setup.
if (latestVersion == 0x0) {
latestVersion = _newVersion;
return true;
}
pendingVersion = _newVersion;
pendingVersionTimestamp = now;
UpgradeProposal(_newVersion);
return true;
}
/**
* Cancel the pending upgrade process.
*
* Can only be called by current asset owner.
*
* @return success.
*/
function purgeUpgrade() onlyAssetOwner() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Finalize an upgrade process setting new asset implementation contract address.
*
* Can only be called after an upgrade freeze-time.
*
* @return success.
*/
function commitUpgrade() returns(bool) {
if (pendingVersion == 0x0) {
return false;
}
if (pendingVersionTimestamp + UPGRADE_FREEZE_TIME > now) {
return false;
}
latestVersion = pendingVersion;
delete pendingVersion;
delete pendingVersionTimestamp;
return true;
}
/**
* Disagree with proposed upgrade, and stick with current asset implementation
* until further explicit agreement to upgrade.
*
* @return success.
*/
function optOut() returns(bool) {
if (userOptOutVersion[msg.sender] != 0x0) {
return false;
}
userOptOutVersion[msg.sender] = latestVersion;
return true;
}
/**
* Implicitly agree to upgrade to current and future asset implementation upgrades,
* until further explicit disagreement.
*
* @return success.
*/
function optIn() returns(bool) {
delete userOptOutVersion[msg.sender];
return true;
}
// Backwards compatibility.
function multiAsset() constant returns(EToken2Interface) {
return etoken2;
}
}
contract UniBrightToken is AssetProxy {
function change(string _symbol, string _name) onlyAssetOwner() returns(bool) {
if (etoken2.isLocked(etoken2Symbol)) {
return false;
}
name = _name;
symbol = _symbol;
return true;
}
}File 4 of 5: EToken2
// This software is a subject to Ambisafe License Agreement.
// No use or distribution is allowed without written permission from Ambisafe.
// https://ambisafe.com/terms.pdf
pragma solidity 0.4.8;
contract Ambi2 {
function claimFor(address _address, address _owner) returns(bool);
function hasRole(address _from, bytes32 _role, address _to) constant returns(bool);
function isOwner(address _node, address _owner) constant returns(bool);
}
contract Ambi2Enabled {
Ambi2 ambi2;
modifier onlyRole(bytes32 _role) {
if (address(ambi2) != 0x0 && ambi2.hasRole(this, _role, msg.sender)) {
_;
}
}
// Perform only after claiming the node, or claim in the same tx.
function setupAmbi2(Ambi2 _ambi2) returns(bool) {
if (address(ambi2) != 0x0) {
return false;
}
ambi2 = _ambi2;
return true;
}
}
contract Ambi2EnabledFull is Ambi2Enabled {
// Setup and claim atomically.
function setupAmbi2(Ambi2 _ambi2) returns(bool) {
if (address(ambi2) != 0x0) {
return false;
}
if (!_ambi2.claimFor(this, msg.sender) && !_ambi2.isOwner(this, msg.sender)) {
return false;
}
ambi2 = _ambi2;
return true;
}
}
contract RegistryICAPInterface {
function parse(bytes32 _icap) constant returns(address, bytes32, bool);
function institutions(bytes32 _institution) constant returns(address);
}
contract Cosigner {
function consumeOperation(bytes32 _opHash, uint _required) returns(bool);
}
contract Emitter {
function emitTransfer(address _from, address _to, bytes32 _symbol, uint _value, string _reference);
function emitTransferToICAP(address _from, address _to, bytes32 _icap, uint _value, string _reference);
function emitIssue(bytes32 _symbol, uint _value, address _by);
function emitRevoke(bytes32 _symbol, uint _value, address _by);
function emitOwnershipChange(address _from, address _to, bytes32 _symbol);
function emitApprove(address _from, address _spender, bytes32 _symbol, uint _value);
function emitRecovery(address _from, address _to, address _by);
function emitError(bytes32 _message);
function emitChange(bytes32 _symbol);
}
contract Proxy {
function emitTransfer(address _from, address _to, uint _value);
function emitApprove(address _from, address _spender, uint _value);
}
/**
* @title EToken2.
*
* The official Ambisafe assets platform powering all kinds of tokens.
* EToken2 uses EventsHistory contract to keep events, so that in case it needs to be redeployed
* at some point, all the events keep appearing at the same place.
*
* Every asset is meant to be used through a proxy contract. Only one proxy contract have access
* rights for a particular asset.
*
* Features: assets issuance, transfers, allowances, supply adjustments, lost wallet access recovery.
* cosignature check, ICAP.
*
* Note: all the non constant functions return false instead of throwing in case if state change
* didn't happen yet.
*/
contract EToken2 is Ambi2EnabledFull {
mapping(bytes32 => bool) switches;
function isEnabled(bytes32 _switch) constant returns(bool) {
return switches[_switch];
}
function enableSwitch(bytes32 _switch) onlyRole('issuance') returns(bool) {
switches[_switch] = true;
return true;
}
modifier checkEnabledSwitch(bytes32 _switch) {
if (!isEnabled(_switch)) {
_error('Feature is disabled');
} else {
_;
}
}
enum Features { Issue, TransferWithReference, Revoke, ChangeOwnership, Allowances, ICAP }
// Structure of a particular asset.
struct Asset {
uint owner; // Asset's owner id.
uint totalSupply; // Asset's total supply.
string name; // Asset's name, for information purposes.
string description; // Asset's description, for information purposes.
bool isReissuable; // Indicates if asset have dynamic of fixed supply.
uint8 baseUnit; // Proposed number of decimals.
bool isLocked; // Are changes still allowed.
mapping(uint => Wallet) wallets; // Holders wallets.
}
// Structure of an asset holder wallet for particular asset.
struct Wallet {
uint balance;
mapping(uint => uint) allowance;
}
// Structure of an asset holder.
struct Holder {
address addr; // Current address of the holder.
Cosigner cosigner; // Cosigner contract for 2FA and recovery.
mapping(address => bool) trust; // Addresses that are trusted with recovery proocedure.
}
// Iterable mapping pattern is used for holders.
uint public holdersCount;
mapping(uint => Holder) public holders;
// This is an access address mapping. Many addresses may have access to a single holder.
mapping(address => uint) holderIndex;
// Asset symbol to asset mapping.
mapping(bytes32 => Asset) public assets;
// Asset symbol to asset proxy mapping.
mapping(bytes32 => address) public proxies;
// ICAP registry contract.
RegistryICAPInterface public registryICAP;
// Should use interface of the emitter, but address of events history.
Emitter public eventsHistory;
/**
* Emits Error event with specified error message.
*
* Should only be used if no state changes happened.
*
* @param _message error message.
*/
function _error(bytes32 _message) internal {
eventsHistory.emitError(_message);
}
/**
* Sets EventsHstory contract address.
*
* Can be set only once, and only by contract owner.
*
* @param _eventsHistory EventsHistory contract address.
*
* @return success.
*/
function setupEventsHistory(Emitter _eventsHistory) onlyRole('setup') returns(bool) {
if (address(eventsHistory) != 0) {
return false;
}
eventsHistory = _eventsHistory;
return true;
}
/**
* Sets RegistryICAP contract address.
*
* Can be set only once, and only by contract owner.
*
* @param _registryICAP RegistryICAP contract address.
*
* @return success.
*/
function setupRegistryICAP(RegistryICAPInterface _registryICAP) onlyRole('setup') returns(bool) {
if (address(registryICAP) != 0) {
return false;
}
registryICAP = _registryICAP;
return true;
}
/**
* Emits Error if called not by asset owner.
*/
modifier onlyOwner(bytes32 _symbol) {
if (_isSignedOwner(_symbol)) {
_;
} else {
_error('Only owner: access denied');
}
}
/**
* Emits Error if called not by asset proxy.
*/
modifier onlyProxy(bytes32 _symbol) {
if (_isProxy(_symbol)) {
_;
} else {
_error('Only proxy: access denied');
}
}
/**
* Emits Error if _from doesn't trust _to.
*/
modifier checkTrust(address _from, address _to) {
if (isTrusted(_from, _to)) {
_;
} else {
_error('Only trusted: access denied');
}
}
function _isSignedOwner(bytes32 _symbol) internal checkSigned(getHolderId(msg.sender), 1) returns(bool) {
return isOwner(msg.sender, _symbol);
}
/**
* Check asset existance.
*
* @param _symbol asset symbol.
*
* @return asset existance.
*/
function isCreated(bytes32 _symbol) constant returns(bool) {
return assets[_symbol].owner != 0;
}
function isLocked(bytes32 _symbol) constant returns(bool) {
return assets[_symbol].isLocked;
}
/**
* Returns asset decimals.
*
* @param _symbol asset symbol.
*
* @return asset decimals.
*/
function baseUnit(bytes32 _symbol) constant returns(uint8) {
return assets[_symbol].baseUnit;
}
/**
* Returns asset name.
*
* @param _symbol asset symbol.
*
* @return asset name.
*/
function name(bytes32 _symbol) constant returns(string) {
return assets[_symbol].name;
}
/**
* Returns asset description.
*
* @param _symbol asset symbol.
*
* @return asset description.
*/
function description(bytes32 _symbol) constant returns(string) {
return assets[_symbol].description;
}
/**
* Returns asset reissuability.
*
* @param _symbol asset symbol.
*
* @return asset reissuability.
*/
function isReissuable(bytes32 _symbol) constant returns(bool) {
return assets[_symbol].isReissuable;
}
/**
* Returns asset owner address.
*
* @param _symbol asset symbol.
*
* @return asset owner address.
*/
function owner(bytes32 _symbol) constant returns(address) {
return holders[assets[_symbol].owner].addr;
}
/**
* Check if specified address has asset owner rights.
*
* @param _owner address to check.
* @param _symbol asset symbol.
*
* @return owner rights availability.
*/
function isOwner(address _owner, bytes32 _symbol) constant returns(bool) {
return isCreated(_symbol) && (assets[_symbol].owner == getHolderId(_owner));
}
/**
* Returns asset total supply.
*
* @param _symbol asset symbol.
*
* @return asset total supply.
*/
function totalSupply(bytes32 _symbol) constant returns(uint) {
return assets[_symbol].totalSupply;
}
/**
* Returns asset balance for current address of a particular holder.
*
* @param _holder holder address.
* @param _symbol asset symbol.
*
* @return holder balance.
*/
function balanceOf(address _holder, bytes32 _symbol) constant returns(uint) {
uint holderId = getHolderId(_holder);
return holders[holderId].addr == _holder ? _balanceOf(holderId, _symbol) : 0;
}
/**
* Returns asset balance for a particular holder id.
*
* @param _holderId holder id.
* @param _symbol asset symbol.
*
* @return holder balance.
*/
function _balanceOf(uint _holderId, bytes32 _symbol) constant internal returns(uint) {
return assets[_symbol].wallets[_holderId].balance;
}
/**
* Returns current address for a particular holder id.
*
* @param _holderId holder id.
*
* @return holder address.
*/
function _address(uint _holderId) constant internal returns(address) {
return holders[_holderId].addr;
}
function _isProxy(bytes32 _symbol) constant internal returns(bool) {
return proxies[_symbol] == msg.sender;
}
/**
* Sets Proxy contract address for a particular asset.
*
* Can be set only once for each asset, and only by contract owner.
*
* @param _address Proxy contract address.
* @param _symbol asset symbol.
*
* @return success.
*/
function setProxy(address _address, bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
if (proxies[_symbol] != 0x0 && assets[_symbol].isLocked) {
return false;
}
proxies[_symbol] = _address;
return true;
}
/**
* Transfers asset balance between holders wallets.
*
* @param _fromId holder id to take from.
* @param _toId holder id to give to.
* @param _value amount to transfer.
* @param _symbol asset symbol.
*/
function _transferDirect(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
assets[_symbol].wallets[_fromId].balance -= _value;
assets[_symbol].wallets[_toId].balance += _value;
}
/**
* Transfers asset balance between holders wallets.
*
* Performs sanity checks and takes care of allowances adjustment.
*
* @param _fromId holder id to take from.
* @param _toId holder id to give to.
* @param _value amount to transfer.
* @param _symbol asset symbol.
* @param _reference transfer comment to be included in a Transfer event.
* @param _senderId transfer initiator holder id.
*
* @return success.
*/
function _transfer(uint _fromId, uint _toId, uint _value, bytes32 _symbol, string _reference, uint _senderId) internal checkSigned(_senderId, 1) returns(bool) {
// Should not allow to send to oneself.
if (_fromId == _toId) {
_error('Cannot send to oneself');
return false;
}
// Should have positive value.
if (_value == 0) {
_error('Cannot send 0 value');
return false;
}
// Should have enough balance.
if (_balanceOf(_fromId, _symbol) < _value) {
_error('Insufficient balance');
return false;
}
// Should allow references.
if (bytes(_reference).length > 0 && !isEnabled(sha3(_symbol, Features.TransferWithReference))) {
_error('References feature is disabled');
return false;
}
// Should have enough allowance.
if (_fromId != _senderId && _allowance(_fromId, _senderId, _symbol) < _value) {
_error('Not enough allowance');
return false;
}
// Adjust allowance.
if (_fromId != _senderId) {
assets[_symbol].wallets[_fromId].allowance[_senderId] -= _value;
}
_transferDirect(_fromId, _toId, _value, _symbol);
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitTransfer(_address(_fromId), _address(_toId), _symbol, _value, _reference);
_proxyTransferEvent(_fromId, _toId, _value, _symbol);
return true;
}
// Feature and proxy checks done internally due to unknown symbol when the function is called.
function _transferToICAP(uint _fromId, bytes32 _icap, uint _value, string _reference, uint _senderId) internal returns(bool) {
var (to, symbol, success) = registryICAP.parse(_icap);
if (!success) {
_error('ICAP is not registered');
return false;
}
if (!isEnabled(sha3(symbol, Features.ICAP))) {
_error('ICAP feature is disabled');
return false;
}
if (!_isProxy(symbol)) {
_error('Only proxy: access denied');
return false;
}
uint toId = _createHolderId(to);
if (!_transfer(_fromId, toId, _value, symbol, _reference, _senderId)) {
return false;
}
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitTransferToICAP(_address(_fromId), _address(toId), _icap, _value, _reference);
return true;
}
function proxyTransferFromToICAPWithReference(address _from, bytes32 _icap, uint _value, string _reference, address _sender) returns(bool) {
return _transferToICAP(getHolderId(_from), _icap, _value, _reference, getHolderId(_sender));
}
/**
* Ask asset Proxy contract to emit ERC20 compliant Transfer event.
*
* @param _fromId holder id to take from.
* @param _toId holder id to give to.
* @param _value amount to transfer.
* @param _symbol asset symbol.
*/
function _proxyTransferEvent(uint _fromId, uint _toId, uint _value, bytes32 _symbol) internal {
if (proxies[_symbol] != 0x0) {
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
Proxy(proxies[_symbol]).emitTransfer(_address(_fromId), _address(_toId), _value);
}
}
/**
* Returns holder id for the specified address.
*
* @param _holder holder address.
*
* @return holder id.
*/
function getHolderId(address _holder) constant returns(uint) {
return holderIndex[_holder];
}
/**
* Returns holder id for the specified address, creates it if needed.
*
* @param _holder holder address.
*
* @return holder id.
*/
function _createHolderId(address _holder) internal returns(uint) {
uint holderId = holderIndex[_holder];
if (holderId == 0) {
holderId = ++holdersCount;
holders[holderId].addr = _holder;
holderIndex[_holder] = holderId;
}
return holderId;
}
/**
* Issues new asset token on the platform.
*
* Tokens issued with this call go straight to contract owner.
* Each symbol can be issued only once, and only by contract owner.
*
* _isReissuable is included in checkEnabledSwitch because it should be
* explicitly allowed before issuing new asset.
*
* @param _symbol asset symbol.
* @param _value amount of tokens to issue immediately.
* @param _name name of the asset.
* @param _description description for the asset.
* @param _baseUnit number of decimals.
* @param _isReissuable dynamic or fixed supply.
*
* @return success.
*/
function issueAsset(bytes32 _symbol, uint _value, string _name, string _description, uint8 _baseUnit, bool _isReissuable) checkEnabledSwitch(sha3(_symbol, _isReissuable, Features.Issue)) returns(bool) {
// Should have positive value if supply is going to be fixed.
if (_value == 0 && !_isReissuable) {
_error('Cannot issue 0 value fixed asset');
return false;
}
// Should not be issued yet.
if (isCreated(_symbol)) {
_error('Asset already issued');
return false;
}
uint holderId = _createHolderId(msg.sender);
assets[_symbol] = Asset(holderId, _value, _name, _description, _isReissuable, _baseUnit, false);
assets[_symbol].wallets[holderId].balance = _value;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitIssue(_symbol, _value, _address(holderId));
return true;
}
function changeAsset(bytes32 _symbol, string _name, string _description, uint8 _baseUnit) onlyOwner(_symbol) returns(bool) {
if (isLocked(_symbol)) {
_error('Asset is locked');
return false;
}
assets[_symbol].name = _name;
assets[_symbol].description = _description;
assets[_symbol].baseUnit = _baseUnit;
eventsHistory.emitChange(_symbol);
return true;
}
function lockAsset(bytes32 _symbol) onlyOwner(_symbol) returns(bool) {
if (isLocked(_symbol)) {
_error('Asset is locked');
return false;
}
assets[_symbol].isLocked = true;
return true;
}
/**
* Issues additional asset tokens if the asset have dynamic supply.
*
* Tokens issued with this call go straight to asset owner.
* Can only be called by asset owner.
*
* @param _symbol asset symbol.
* @param _value amount of additional tokens to issue.
*
* @return success.
*/
function reissueAsset(bytes32 _symbol, uint _value) onlyOwner(_symbol) returns(bool) {
// Should have positive value.
if (_value == 0) {
_error('Cannot reissue 0 value');
return false;
}
Asset asset = assets[_symbol];
// Should have dynamic supply.
if (!asset.isReissuable) {
_error('Cannot reissue fixed asset');
return false;
}
// Resulting total supply should not overflow.
if (asset.totalSupply + _value < asset.totalSupply) {
_error('Total supply overflow');
return false;
}
uint holderId = getHolderId(msg.sender);
asset.wallets[holderId].balance += _value;
asset.totalSupply += _value;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitIssue(_symbol, _value, _address(holderId));
_proxyTransferEvent(0, holderId, _value, _symbol);
return true;
}
/**
* Destroys specified amount of senders asset tokens.
*
* @param _symbol asset symbol.
* @param _value amount of tokens to destroy.
*
* @return success.
*/
function revokeAsset(bytes32 _symbol, uint _value) checkEnabledSwitch(sha3(_symbol, Features.Revoke)) checkSigned(getHolderId(msg.sender), 1) returns(bool) {
// Should have positive value.
if (_value == 0) {
_error('Cannot revoke 0 value');
return false;
}
Asset asset = assets[_symbol];
uint holderId = getHolderId(msg.sender);
// Should have enough tokens.
if (asset.wallets[holderId].balance < _value) {
_error('Not enough tokens to revoke');
return false;
}
asset.wallets[holderId].balance -= _value;
asset.totalSupply -= _value;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitRevoke(_symbol, _value, _address(holderId));
_proxyTransferEvent(holderId, 0, _value, _symbol);
return true;
}
/**
* Passes asset ownership to specified address.
*
* Only ownership is changed, balances are not touched.
* Can only be called by asset owner.
*
* @param _symbol asset symbol.
* @param _newOwner address to become a new owner.
*
* @return success.
*/
function changeOwnership(bytes32 _symbol, address _newOwner) checkEnabledSwitch(sha3(_symbol, Features.ChangeOwnership)) onlyOwner(_symbol) returns(bool) {
Asset asset = assets[_symbol];
uint newOwnerId = _createHolderId(_newOwner);
// Should pass ownership to another holder.
if (asset.owner == newOwnerId) {
_error('Cannot pass ownership to oneself');
return false;
}
address oldOwner = _address(asset.owner);
asset.owner = newOwnerId;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitOwnershipChange(oldOwner, _address(newOwnerId), _symbol);
return true;
}
function setCosignerAddress(Cosigner _cosigner) checkSigned(_createHolderId(msg.sender), 1) returns(bool) {
if (!_checkSigned(_cosigner, getHolderId(msg.sender), 1)) {
_error('Invalid cosigner');
return false;
}
holders[_createHolderId(msg.sender)].cosigner = _cosigner;
return true;
}
function isCosignerSet(uint _holderId) constant returns(bool) {
return address(holders[_holderId].cosigner) != 0x0;
}
function _checkSigned(Cosigner _cosigner, uint _holderId, uint _required) internal returns(bool) {
return _cosigner.consumeOperation(sha3(msg.data, _holderId), _required);
}
modifier checkSigned(uint _holderId, uint _required) {
if (!isCosignerSet(_holderId) || _checkSigned(holders[_holderId].cosigner, _holderId, _required)) {
_;
} else {
_error('Cosigner: access denied');
}
}
/**
* Check if specified holder trusts an address with recovery procedure.
*
* @param _from truster.
* @param _to trustee.
*
* @return trust existance.
*/
function isTrusted(address _from, address _to) constant returns(bool) {
return holders[getHolderId(_from)].trust[_to];
}
/**
* Trust an address to perform recovery procedure for the caller.
*
* @param _to trustee.
*
* @return success.
*/
function trust(address _to) returns(bool) {
uint fromId = _createHolderId(msg.sender);
// Should trust to another address.
if (fromId == getHolderId(_to)) {
_error('Cannot trust to oneself');
return false;
}
// Should trust to yet untrusted.
if (isTrusted(msg.sender, _to)) {
_error('Already trusted');
return false;
}
holders[fromId].trust[_to] = true;
return true;
}
/**
* Revoke trust to perform recovery procedure from an address.
*
* @param _to trustee.
*
* @return success.
*/
function distrust(address _to) checkTrust(msg.sender, _to) returns(bool) {
holders[getHolderId(msg.sender)].trust[_to] = false;
return true;
}
/**
* Perform recovery procedure.
*
* This function logic is actually more of an grantAccess(uint _holderId, address _to).
* It grants another address access to recovery subject wallets.
* Can only be called by trustee of recovery subject.
* If cosigning is enabled, should have atleast 2 confirmations.
*
* @dev Deprecated. Backward compatibility.
*
* @param _from holder address to recover from.
* @param _to address to grant access to.
*
* @return success.
*/
function recover(address _from, address _to) checkTrust(_from, msg.sender) returns(bool) {
return _grantAccess(getHolderId(_from), _to);
}
/**
* Perform recovery procedure.
*
* This function logic is actually more of an grantAccess(uint _holderId, address _to).
* It grants another address access to subject holder wallets.
* Can only be called if pre-confirmed by atleast 2 cosign oracles.
*
* @param _from holder address to recover from.
* @param _to address to grant access to.
*
* @return success.
*/
function grantAccess(address _from, address _to) returns(bool) {
if (!isCosignerSet(getHolderId(_from))) {
_error('Cosigner not set');
return false;
}
return _grantAccess(getHolderId(_from), _to);
}
function _grantAccess(uint _fromId, address _to) internal checkSigned(_fromId, 2) returns(bool) {
// Should recover to previously unused address.
if (getHolderId(_to) != 0) {
_error('Should recover to new address');
return false;
}
// We take current holder address because it might not equal _from.
// It is possible to recover from any old holder address, but event should have the current one.
address from = holders[_fromId].addr;
holders[_fromId].addr = _to;
holderIndex[_to] = _fromId;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitRecovery(from, _to, msg.sender);
return true;
}
/**
* Sets asset spending allowance for a specified spender.
*
* Note: to revoke allowance, one needs to set allowance to 0.
*
* @param _spenderId holder id to set allowance for.
* @param _value amount to allow.
* @param _symbol asset symbol.
* @param _senderId approve initiator holder id.
*
* @return success.
*/
function _approve(uint _spenderId, uint _value, bytes32 _symbol, uint _senderId) internal checkEnabledSwitch(sha3(_symbol, Features.Allowances)) checkSigned(_senderId, 1) returns(bool) {
// Asset should exist.
if (!isCreated(_symbol)) {
_error('Asset is not issued');
return false;
}
// Should allow to another holder.
if (_senderId == _spenderId) {
_error('Cannot approve to oneself');
return false;
}
assets[_symbol].wallets[_senderId].allowance[_spenderId] = _value;
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
eventsHistory.emitApprove(_address(_senderId), _address(_spenderId), _symbol, _value);
if (proxies[_symbol] != 0x0) {
// Internal Out Of Gas/Throw: revert this transaction too;
// Recursive Call: safe, all changes already made.
Proxy(proxies[_symbol]).emitApprove(_address(_senderId), _address(_spenderId), _value);
}
return true;
}
/**
* Sets asset spending allowance for a specified spender.
*
* Can only be called by asset proxy.
*
* @param _spender holder address to set allowance to.
* @param _value amount to allow.
* @param _symbol asset symbol.
* @param _sender approve initiator address.
*
* @return success.
*/
function proxyApprove(address _spender, uint _value, bytes32 _symbol, address _sender) onlyProxy(_symbol) returns(bool) {
return _approve(_createHolderId(_spender), _value, _symbol, _createHolderId(_sender));
}
/**
* Returns asset allowance from one holder to another.
*
* @param _from holder that allowed spending.
* @param _spender holder that is allowed to spend.
* @param _symbol asset symbol.
*
* @return holder to spender allowance.
*/
function allowance(address _from, address _spender, bytes32 _symbol) constant returns(uint) {
return _allowance(getHolderId(_from), getHolderId(_spender), _symbol);
}
/**
* Returns asset allowance from one holder to another.
*
* @param _fromId holder id that allowed spending.
* @param _toId holder id that is allowed to spend.
* @param _symbol asset symbol.
*
* @return holder to spender allowance.
*/
function _allowance(uint _fromId, uint _toId, bytes32 _symbol) constant internal returns(uint) {
return assets[_symbol].wallets[_fromId].allowance[_toId];
}
/**
* Prforms allowance transfer of asset balance between holders wallets.
*
* Can only be called by asset proxy.
*
* @param _from holder address to take from.
* @param _to holder address to give to.
* @param _value amount to transfer.
* @param _symbol asset symbol.
* @param _reference transfer comment to be included in a Transfer event.
* @param _sender allowance transfer initiator address.
*
* @return success.
*/
function proxyTransferFromWithReference(address _from, address _to, uint _value, bytes32 _symbol, string _reference, address _sender) onlyProxy(_symbol) returns(bool) {
return _transfer(getHolderId(_from), _createHolderId(_to), _value, _symbol, _reference, getHolderId(_sender));
}
}File 5 of 5: UniswapV2Pair
// File: contracts/interfaces/IUniswapV2Pair.sol
pragma solidity >=0.5.0;
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
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 (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
// File: contracts/interfaces/IUniswapV2ERC20.sol
pragma solidity >=0.5.0;
interface IUniswapV2ERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
}
// File: contracts/libraries/SafeMath.sol
pragma solidity =0.5.16;
// a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
library SafeMath {
function add(uint x, uint y) internal pure returns (uint z) {
require((z = x + y) >= x, 'ds-math-add-overflow');
}
function sub(uint x, uint y) internal pure returns (uint z) {
require((z = x - y) <= x, 'ds-math-sub-underflow');
}
function mul(uint x, uint y) internal pure returns (uint z) {
require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
}
}
// File: contracts/UniswapV2ERC20.sol
pragma solidity =0.5.16;
contract UniswapV2ERC20 is IUniswapV2ERC20 {
using SafeMath for uint;
string public constant name = 'Uniswap V2';
string public constant symbol = 'UNI-V2';
uint8 public constant decimals = 18;
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
bytes32 public DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint) public nonces;
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
constructor() public {
uint chainId;
assembly {
chainId := chainid
}
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
keccak256(bytes(name)),
keccak256(bytes('1')),
chainId,
address(this)
)
);
}
function _mint(address to, uint value) internal {
totalSupply = totalSupply.add(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(address(0), to, value);
}
function _burn(address from, uint value) internal {
balanceOf[from] = balanceOf[from].sub(value);
totalSupply = totalSupply.sub(value);
emit Transfer(from, address(0), value);
}
function _approve(address owner, address spender, uint value) private {
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function _transfer(address from, address to, uint value) private {
balanceOf[from] = balanceOf[from].sub(value);
balanceOf[to] = balanceOf[to].add(value);
emit Transfer(from, to, value);
}
function approve(address spender, uint value) external returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
function transfer(address to, uint value) external returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint value) external returns (bool) {
if (allowance[from][msg.sender] != uint(-1)) {
allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
}
_transfer(from, to, value);
return true;
}
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
bytes32 digest = keccak256(
abi.encodePacked(
'\x19\x01',
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
_approve(owner, spender, value);
}
}
// File: contracts/libraries/Math.sol
pragma solidity =0.5.16;
// a library for performing various math operations
library Math {
function min(uint x, uint y) internal pure returns (uint z) {
z = x < y ? x : y;
}
// babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
function sqrt(uint y) internal pure returns (uint z) {
if (y > 3) {
z = y;
uint x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
}
// File: contracts/libraries/UQ112x112.sol
pragma solidity =0.5.16;
// a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
// range: [0, 2**112 - 1]
// resolution: 1 / 2**112
library UQ112x112 {
uint224 constant Q112 = 2**112;
// encode a uint112 as a UQ112x112
function encode(uint112 y) internal pure returns (uint224 z) {
z = uint224(y) * Q112; // never overflows
}
// divide a UQ112x112 by a uint112, returning a UQ112x112
function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
z = x / uint224(y);
}
}
// File: contracts/interfaces/IERC20.sol
pragma solidity >=0.5.0;
interface IERC20 {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(address from, address to, uint value) external returns (bool);
}
// File: contracts/interfaces/IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
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(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
// File: contracts/interfaces/IUniswapV2Callee.sol
pragma solidity >=0.5.0;
interface IUniswapV2Callee {
function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
}
// File: contracts/UniswapV2Pair.sol
pragma solidity =0.5.16;
contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
using SafeMath for uint;
using UQ112x112 for uint224;
uint public constant MINIMUM_LIQUIDITY = 10**3;
bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
address public factory;
address public token0;
address public token1;
uint112 private reserve0; // uses single storage slot, accessible via getReserves
uint112 private reserve1; // uses single storage slot, accessible via getReserves
uint32 private blockTimestampLast; // uses single storage slot, accessible via getReserves
uint public price0CumulativeLast;
uint public price1CumulativeLast;
uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
uint private unlocked = 1;
modifier lock() {
require(unlocked == 1, 'UniswapV2: LOCKED');
unlocked = 0;
_;
unlocked = 1;
}
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
function _safeTransfer(address token, address to, uint value) private {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
}
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
constructor() public {
factory = msg.sender;
}
// called once by the factory at time of deployment
function initialize(address _token0, address _token1) external {
require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
token0 = _token0;
token1 = _token1;
}
// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
uint32 blockTimestamp = uint32(block.timestamp % 2**32);
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
// * never overflows, and + overflow is desired
price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
}
reserve0 = uint112(balance0);
reserve1 = uint112(balance1);
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
// if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
address feeTo = IUniswapV2Factory(factory).feeTo();
feeOn = feeTo != address(0);
uint _kLast = kLast; // gas savings
if (feeOn) {
if (_kLast != 0) {
uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
uint numerator = totalSupply.mul(rootK.sub(rootKLast));
uint denominator = rootK.mul(5).add(rootKLast);
uint liquidity = numerator / denominator;
if (liquidity > 0) _mint(feeTo, liquidity);
}
}
} else if (_kLast != 0) {
kLast = 0;
}
}
// this low-level function should be called from a contract which performs important safety checks
function mint(address to) external lock returns (uint liquidity) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
uint balance0 = IERC20(token0).balanceOf(address(this));
uint balance1 = IERC20(token1).balanceOf(address(this));
uint amount0 = balance0.sub(_reserve0);
uint amount1 = balance1.sub(_reserve1);
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
}
require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
_mint(to, liquidity);
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Mint(msg.sender, amount0, amount1);
}
// this low-level function should be called from a contract which performs important safety checks
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
uint balance0 = IERC20(_token0).balanceOf(address(this));
uint balance1 = IERC20(_token1).balanceOf(address(this));
uint liquidity = balanceOf[address(this)];
bool feeOn = _mintFee(_reserve0, _reserve1);
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
_burn(address(this), liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
_update(balance0, balance1, _reserve0, _reserve1);
if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
emit Burn(msg.sender, amount0, amount1, to);
}
// this low-level function should be called from a contract which performs important safety checks
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
(uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
uint balance0;
uint balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
address _token0 = token0;
address _token1 = token1;
require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
balance0 = IERC20(_token0).balanceOf(address(this));
balance1 = IERC20(_token1).balanceOf(address(this));
}
uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
}
_update(balance0, balance1, _reserve0, _reserve1);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// force balances to match reserves
function skim(address to) external lock {
address _token0 = token0; // gas savings
address _token1 = token1; // gas savings
_safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
_safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
}
// force reserves to match balances
function sync() external lock {
_update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
}
}