Contract Overview
Latest 6 internal transactions
[ Download CSV Export ]
Contract Name:
FnbswapExchange20
Compiler Version
v0.7.4+commit.3f05b770
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; pragma experimental ABIEncoderV2; import "./swap/contracts/interfaces/IFnbswapExchange20.sol"; import "./swap/contracts/utils/ReentrancyGuard.sol"; import "./swap/contracts/utils/DelegatedOwnable.sol"; import "./swap/contracts/interfaces/IERC2981.sol"; import "./swap/contracts/interfaces/IERC1155Metadata.sol"; import "./swap/contracts/interfaces/IDelegatedERC1155Metadata.sol"; import "./erc-1155/contracts/interfaces/IERC20.sol"; import "./erc-1155/contracts/interfaces/IERC165.sol"; import "./erc-1155/contracts/interfaces/IERC1155.sol"; import "./erc-1155/contracts/interfaces/IERC1155TokenReceiver.sol"; import "./erc-1155/contracts/tokens/ERC1155/ERC1155MintBurn.sol"; import "./TransferHelper.sol"; /** * This Uniswap-like implementation supports ERC-1155 standard tokens * with an ERC-20 based token used as a currency instead of Ether. * * Liquidity tokens are also ERC-1155 tokens you can find the ERC-1155 * implementation used here: * https://github.com/horizon-games/multi-token-standard/tree/master/contracts/tokens/ERC1155 * * @dev Like Uniswap, tokens with 0 decimals and low supply are susceptible to significant rounding * errors when it comes to removing liquidity, possibly preventing them to be withdrawn without * some collaboration between liquidity providers. * * @dev ERC-777 tokens may be vulnerable if used as currency in Fnbswap. Please review the code * carefully before using it with ERC-777 tokens. */ contract FnbswapExchange20 is ReentrancyGuard, ERC1155MintBurn, IFnbswapExchange20, IERC1155Metadata, DelegatedOwnable { using SafeMath for uint256; /***********************************| | Variables & Constants | |__________________________________*/ // Variables IERC1155 internal immutable token; // address of the ERC-1155 token contract address internal immutable currency; // address of the ERC-20 currency used for exchange address internal immutable factory; // address for the factory that created this contract uint256 internal immutable FEE_MULTIPLIER; // multiplier that calculates the LP fee (1.0%) // Royalty variables bool internal immutable IS_ERC2981; // whether token contract supports ERC-2981 uint256 internal globalRoyaltyFee; // global royalty fee multiplier if ERC2981 is not used address internal globalRoyaltyRecipient; // global royalty fee recipient if ERC2981 is not used // Mapping variables mapping(uint256 => uint256) internal totalSupplies; // Liquidity pool token supply per Token id mapping(uint256 => uint256) internal currencyReserves; // currency Token reserve per Token id mapping(address => uint256) internal royaltiesNumerator; // Mapping tracking how much royalties can be claimed per address uint256 internal constant ROYALTIES_DENOMINATOR = 10000; uint256 internal constant MAX_ROYALTY = ROYALTIES_DENOMINATOR / 4; bool public test = true; /***********************************| | Constructor | |__________________________________*/ /** * @notice Create instance of exchange contract with respective token and currency token * @dev If token supports ERC-2981, then royalty fee will be queried per token on the * token contract. Else royalty fee will need to be manually set by admin. * @param _tokenAddr The address of the ERC-1155 Token * @param _currencyAddr The address of the ERC-20 currency Token * @param _currencyAddr Address of the admin, which should be the same as the factory owner * @param _lpFee Fee that will go to LPs. * Number between 0 and 1000, where 10 is 1.0% and 100 is 10%. */ constructor(address _tokenAddr, address _currencyAddr, uint256 _lpFee) DelegatedOwnable(msg.sender) { require( _tokenAddr != address(0) && _currencyAddr != address(0), "NE20#1" // FnbswapExchange20#constructor:INVALID_INPUT ); require( _lpFee >= 0 && _lpFee <= 1000, "NE20#2" // FnbswapExchange20#constructor:INVALID_LP_FEE ); factory = msg.sender; token = IERC1155(_tokenAddr); currency = _currencyAddr; FEE_MULTIPLIER = 1000 - _lpFee; // If global royalty, lets check for ERC-2981 support try IERC1155(_tokenAddr).supportsInterface(type(IERC2981).interfaceId) returns (bool supported) { IS_ERC2981 = supported; } catch {} } /***********************************| | Metadata Functions | |__________________________________*/ /** @notice A distinct Uniform Resource Identifier (URI) for a given token. @dev URIs are defined in RFC 3986. The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". @return URI string */ function uri(uint256 _id) external override view returns (string memory) { return IDelegatedERC1155Metadata(factory).metadataProvider().uri(_id); } /***********************************| | Exchange Functions | |__________________________________*/ /** * @notice Convert currency tokens to Tokens _id and transfers Tokens to recipient. */ function _currencyToToken( uint256[] memory _tokenIds, uint256[] memory _tokensBoughtAmounts, uint256 _maxCurrency, uint256 _deadline, address _recipient ) internal nonReentrant() returns (uint256[] memory currencySold) { // Input validation require(_deadline >= block.timestamp, "NE20#3"); // FnbswapExchange20#_currencyToToken: DEADLINE_EXCEEDED // Number of Token IDs to deposit uint256 nTokens = _tokenIds.length; uint256 totalRefundCurrency = _maxCurrency; // Initialize variables currencySold = new uint256[](nTokens); // Amount of currency tokens sold per ID // Get token reserves uint256[] memory tokenReserves = _getTokenReserves(_tokenIds); // Assumes the currency Tokens are already received by contract, but not // the Tokens Ids // Remove liquidity for each Token ID in _tokenIds for (uint256 i = 0; i < nTokens; i++) { // Store current id and amount from argument arrays uint256 idBought = _tokenIds[i]; uint256 amountBought = _tokensBoughtAmounts[i]; uint256 tokenReserve = tokenReserves[i]; require(amountBought > 0, "NE20#4"); // FnbswapExchange20#_currencyToToken: NULL_TOKENS_BOUGHT // Load currency token and Token _id reserves uint256 currencyReserve = currencyReserves[idBought]; // Get amount of currency tokens to send for purchase // Neither reserves amount have been changed so far in this transaction, so // no adjustment to the inputs is needed uint256 currencyAmount = getBuyPrice(amountBought, currencyReserve, tokenReserve); // If royalty, increase amount buyer will need to pay after LP fees were calculated // Note: Royalty will be a bit higher since LF fees are added first (address royaltyRecipient, uint256 royaltyAmount) = getRoyaltyInfo(idBought, currencyAmount); if (royaltyAmount > 0) { royaltiesNumerator[royaltyRecipient] = royaltiesNumerator[royaltyRecipient].add(royaltyAmount.mul(ROYALTIES_DENOMINATOR)); } // Calculate currency token amount to refund (if any) where whatever is not used will be returned // Will throw if total cost exceeds _maxCurrency totalRefundCurrency = totalRefundCurrency.sub(currencyAmount).sub(royaltyAmount); // Append Token id, Token id amount and currency token amount to tracking arrays currencySold[i] = currencyAmount.add(royaltyAmount); // Update individual currency reseve amount (royalty is not added to liquidity) currencyReserves[idBought] = currencyReserve.add(currencyAmount); } // Send Tokens all tokens purchased token.safeBatchTransferFrom(address(this), _recipient, _tokenIds, _tokensBoughtAmounts, ""); // Refund currency token if any if (totalRefundCurrency > 0) { TransferHelper.safeTransfer(currency, _recipient, totalRefundCurrency); } return currencySold; } /** * @dev Pricing function used for converting between currency token to Tokens. * @param _assetBoughtAmount Amount of Tokens being bought. * @param _assetSoldReserve Amount of currency tokens in exchange reserves. * @param _assetBoughtReserve Amount of Tokens (output type) in exchange reserves. * @return price Amount of currency tokens to send to Fnbswap. */ function getBuyPrice( uint256 _assetBoughtAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) override public view returns (uint256 price) { // Reserves must not be empty require(_assetSoldReserve > 0 && _assetBoughtReserve > 0, "NE20#5"); // FnbswapExchange20#getBuyPrice: EMPTY_RESERVE // Calculate price with fee uint256 numerator = _assetSoldReserve.mul(_assetBoughtAmount).mul(1000); uint256 denominator = (_assetBoughtReserve.sub(_assetBoughtAmount)).mul(FEE_MULTIPLIER); (price, ) = divRound(numerator, denominator); return price; // Will add 1 if rounding error } /** * @dev Pricing function used for converting Tokens to currency token (including royalty fee) * @param _tokenId Id ot token being sold * @param _assetBoughtAmount Amount of Tokens being bought. * @param _assetSoldReserve Amount of currency tokens in exchange reserves. * @param _assetBoughtReserve Amount of Tokens (output type) in exchange reserves. * @return price Amount of currency tokens to send to Fnbswap. */ function getBuyPriceWithRoyalty( uint256 _tokenId, uint256 _assetBoughtAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) override public view returns (uint256 price) { uint256 cost = getBuyPrice(_assetBoughtAmount, _assetSoldReserve, _assetBoughtReserve); (, uint256 royaltyAmount) = getRoyaltyInfo(_tokenId, cost); return cost.add(royaltyAmount); } /** * @notice Convert Tokens _id to currency tokens and transfers Tokens to recipient. * @dev User specifies EXACT Tokens _id sold and MINIMUM currency tokens received. * @dev Assumes that all trades will be valid, or the whole tx will fail * @dev Sorting _tokenIds is mandatory for efficient way of preventing duplicated IDs (which would lead to errors) * @param _tokenIds Array of Token IDs that are sold * @param _tokensSoldAmounts Array of Amount of Tokens sold for each id in _tokenIds. * @param _minCurrency Minimum amount of currency tokens to receive * @param _deadline Timestamp after which this transaction will be reverted * @param _recipient The address that receives output currency tokens. * @param _extraFeeRecipients Array of addresses that will receive extra fee * @param _extraFeeAmounts Array of amounts of currency that will be sent as extra fee * @return currencyBought How much currency was actually purchased. */ function _tokenToCurrency( uint256[] memory _tokenIds, uint256[] memory _tokensSoldAmounts, uint256 _minCurrency, uint256 _deadline, address _recipient, address[] memory _extraFeeRecipients, uint256[] memory _extraFeeAmounts ) internal nonReentrant() returns (uint256[] memory currencyBought) { // Number of Token IDs to deposit uint256 nTokens = _tokenIds.length; // Input validation require(_deadline >= block.timestamp, "NE20#6"); // FnbswapExchange20#_tokenToCurrency: DEADLINE_EXCEEDED // Initialize variables uint256 totalCurrency = 0; // Total amount of currency tokens to transfer currencyBought = new uint256[](nTokens); // Get token reserves uint256[] memory tokenReserves = _getTokenReserves(_tokenIds); // Assumes the Tokens ids are already received by contract, but not // the Tokens Ids. Will return cards not sold if invalid price. // Remove liquidity for each Token ID in _tokenIds for (uint256 i = 0; i < nTokens; i++) { // Store current id and amount from argument arrays uint256 idSold = _tokenIds[i]; uint256 amountSold = _tokensSoldAmounts[i]; uint256 tokenReserve = tokenReserves[i]; // If 0 tokens send for this ID, revert require(amountSold > 0, "NE20#7"); // FnbswapExchange20#_tokenToCurrency: NULL_TOKENS_SOLD // Load currency token and Token _id reserves uint256 currencyReserve = currencyReserves[idSold]; // Get amount of currency that will be received // Need to sub amountSold because tokens already added in reserve, which would bias the calculation // Don't need to add it for currencyReserve because the amount is added after this calculation uint256 currencyAmount = getSellPrice(amountSold, tokenReserve.sub(amountSold), currencyReserve); // If royalty, substract amount seller will receive after LP fees were calculated // Note: Royalty will be a bit lower since LF fees are substracted first (address royaltyRecipient, uint256 royaltyAmount) = getRoyaltyInfo(idSold, currencyAmount); if (royaltyAmount > 0) { royaltiesNumerator[royaltyRecipient] = royaltiesNumerator[royaltyRecipient].add(royaltyAmount.mul(ROYALTIES_DENOMINATOR)); } // Increase total amount of currency to receive (minus royalty to pay) totalCurrency = totalCurrency.add(currencyAmount.sub(royaltyAmount)); // Update individual currency reseve amount currencyReserves[idSold] = currencyReserve.sub(currencyAmount); // Append Token id, Token id amount and currency token amount to tracking arrays currencyBought[i] = currencyAmount.sub(royaltyAmount); } // Set the extra fees aside to recipients after sale for (uint256 i = 0; i < _extraFeeAmounts.length; i++) { if (_extraFeeAmounts[i] > 0) { totalCurrency = totalCurrency.sub(_extraFeeAmounts[i]); royaltiesNumerator[_extraFeeRecipients[i]] = royaltiesNumerator[_extraFeeRecipients[i]].add(_extraFeeAmounts[i].mul(ROYALTIES_DENOMINATOR)); } } // If minCurrency is not met require(totalCurrency >= _minCurrency, "NE20#8"); // FnbswapExchange20#_tokenToCurrency: INSUFFICIENT_CURRENCY_AMOUNT // Transfer currency here TransferHelper.safeTransfer(currency, _recipient, totalCurrency); return currencyBought; } /** * @dev Pricing function used for converting Tokens to currency token. * @param _assetSoldAmount Amount of Tokens being sold. * @param _assetSoldReserve Amount of Tokens in exchange reserves. * @param _assetBoughtReserve Amount of currency tokens in exchange reserves. * @return price Amount of currency tokens to receive from Fnbswap. */ function getSellPrice( uint256 _assetSoldAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) override public view returns (uint256 price) { //Reserves must not be empty require(_assetSoldReserve > 0 && _assetBoughtReserve > 0, "NE20#9"); // FnbswapExchange20#getSellPrice: EMPTY_RESERVE // Calculate amount to receive (with fee) before royalty uint256 _assetSoldAmount_withFee = _assetSoldAmount.mul(FEE_MULTIPLIER); uint256 numerator = _assetSoldAmount_withFee.mul(_assetBoughtReserve); uint256 denominator = _assetSoldReserve.mul(1000).add(_assetSoldAmount_withFee); return numerator / denominator; //Rounding errors will favor Fnbswap, so nothing to do } /** * @dev Pricing function used for converting Tokens to currency token (including royalty fee) * @param _tokenId Id ot token being sold * @param _assetSoldAmount Amount of Tokens being sold. * @param _assetSoldReserve Amount of Tokens in exchange reserves. * @param _assetBoughtReserve Amount of currency tokens in exchange reserves. * @return price Amount of currency tokens to receive from Fnbswap. */ function getSellPriceWithRoyalty( uint256 _tokenId, uint256 _assetSoldAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) override public view returns (uint256 price) { uint256 sellAmount = getSellPrice(_assetSoldAmount, _assetSoldReserve, _assetBoughtReserve); (, uint256 royaltyAmount) = getRoyaltyInfo(_tokenId, sellAmount); return sellAmount.sub(royaltyAmount); } /***********************************| | Liquidity Functions | |__________________________________*/ /** * @notice Deposit less than max currency tokens && exact Tokens (token ID) at current ratio to mint liquidity pool tokens. * @dev min_liquidity does nothing when total liquidity pool token supply is 0. * @dev Assumes that sender approved this contract on the currency * @dev Sorting _tokenIds is mandatory for efficient way of preventing duplicated IDs (which would lead to errors) * @param _provider Address that provides liquidity to the reserve * @param _tokenIds Array of Token IDs where liquidity is added * @param _tokenAmounts Array of amount of Tokens deposited corresponding to each ID provided in _tokenIds * @param _maxCurrency Array of maximum number of tokens deposited for each ID provided in _tokenIds. * Deposits max amount if total liquidity pool token supply is 0. * @param _deadline Timestamp after which this transaction will be reverted */ function _addLiquidity( address _provider, uint256[] memory _tokenIds, uint256[] memory _tokenAmounts, uint256[] memory _maxCurrency, uint256 _deadline) internal nonReentrant() { // Requirements require(_deadline >= block.timestamp, "NE20#10"); // FnbswapExchange20#_addLiquidity: DEADLINE_EXCEEDED // Initialize variables uint256 nTokens = _tokenIds.length; // Number of Token IDs to deposit uint256 totalCurrency = 0; // Total amount of currency tokens to transfer // Initialize arrays uint256[] memory liquiditiesToMint = new uint256[](nTokens); uint256[] memory currencyAmounts = new uint256[](nTokens); // Get token reserves uint256[] memory tokenReserves = _getTokenReserves(_tokenIds); // Assumes tokens _ids are deposited already, but not currency tokens // as this is calculated and executed below. // Loop over all Token IDs to deposit for (uint256 i = 0; i < nTokens; i ++) { // Store current id and amount from argument arrays uint256 tokenId = _tokenIds[i]; uint256 amount = _tokenAmounts[i]; // Check if input values are acceptable require(_maxCurrency[i] > 0, "NE20#11"); // FnbswapExchange20#_addLiquidity: NULL_MAX_CURRENCY require(amount > 0, "NE20#12"); // FnbswapExchange20#_addLiquidity: NULL_TOKENS_AMOUNT // Current total liquidity calculated in currency token uint256 totalLiquidity = totalSupplies[tokenId]; // When reserve for this token already exists if (totalLiquidity > 0) { // Load currency token and Token reserve's supply of Token id uint256 currencyReserve = currencyReserves[tokenId]; // Amount not yet in reserve uint256 tokenReserve = tokenReserves[i]; /** * Amount of currency tokens to send to token id reserve: * X/Y = dx/dy * dx = X*dy/Y * where * X: currency total liquidity * Y: Token _id total liquidity (before tokens were received) * dy: Amount of token _id deposited * dx: Amount of currency to deposit * * Adding .add(1) if rounding errors so to not favor users incorrectly */ (uint256 currencyAmount, bool rounded) = divRound(amount.mul(currencyReserve), tokenReserve.sub(amount)); require(_maxCurrency[i] >= currencyAmount, "NE20#13"); // FnbswapExchange20#_addLiquidity: MAX_CURRENCY_AMOUNT_EXCEEDED // Update currency reserve size for Token id before transfer currencyReserves[tokenId] = currencyReserve.add(currencyAmount); // Update totalCurrency totalCurrency = totalCurrency.add(currencyAmount); // Proportion of the liquidity pool to give to current liquidity provider // If rounding error occured, round down to favor previous liquidity providers liquiditiesToMint[i] = (currencyAmount.sub(rounded ? 1 : 0)).mul(totalLiquidity) / currencyReserve; currencyAmounts[i] = currencyAmount; // Mint liquidity ownership tokens and increase liquidity supply accordingly totalSupplies[tokenId] = totalLiquidity.add(liquiditiesToMint[i]); } else { uint256 maxCurrency = _maxCurrency[i]; // Otherwise rounding error could end up being significant on second deposit require(maxCurrency >= 1000, "NE20#14"); // FnbswapExchange20#_addLiquidity: INVALID_CURRENCY_AMOUNT // Update currency reserve size for Token id before transfer currencyReserves[tokenId] = maxCurrency; // Update totalCurrency totalCurrency = totalCurrency.add(maxCurrency); // Initial liquidity is amount deposited (Incorrect pricing will be arbitraged) // uint256 initialLiquidity = _maxCurrency; totalSupplies[tokenId] = maxCurrency; // Liquidity to mints liquiditiesToMint[i] = maxCurrency; currencyAmounts[i] = maxCurrency; } } // Transfer all currency to this contract TransferHelper.safeTransferFrom(currency, _provider, address(this), totalCurrency); // Mint liquidity pool tokens _batchMint(_provider, _tokenIds, liquiditiesToMint, ""); // Emit event emit LiquidityAdded(_provider, _tokenIds, _tokenAmounts, currencyAmounts); } /** * @dev Convert pool participation into amounts of token and currency. * @dev Rounding error of the asset with lower resolution is traded for the other asset. * @param _amountPool Participation to be converted to tokens and currency. * @param _tokenReserve Amount of tokens on the AMM reserve. * @param _currencyReserve Amount of currency on the AMM reserve. * @param _totalLiquidity Total liquidity on the pool. * * @return currencyAmount Currency corresponding to pool amount plus rounded tokens. * @return tokenAmount Token corresponding to pool amount plus rounded currency. */ function _toRoundedLiquidity( uint256 _tokenId, uint256 _amountPool, uint256 _tokenReserve, uint256 _currencyReserve, uint256 _totalLiquidity ) internal view returns ( uint256 currencyAmount, uint256 tokenAmount, uint256 soldTokenNumerator, uint256 boughtCurrencyNumerator, address royaltyRecipient, uint256 royaltyNumerator ) { uint256 currencyNumerator = _amountPool.mul(_currencyReserve); uint256 tokenNumerator = _amountPool.mul(_tokenReserve); // Convert all tokenProduct rest to currency soldTokenNumerator = tokenNumerator % _totalLiquidity; if (soldTokenNumerator != 0) { // The trade happens "after" funds are out of the pool // so we need to remove these funds before computing the rate uint256 virtualTokenReserve = _tokenReserve.sub(tokenNumerator / _totalLiquidity).mul(_totalLiquidity); uint256 virtualCurrencyReserve = _currencyReserve.sub(currencyNumerator / _totalLiquidity).mul(_totalLiquidity); // Skip process if any of the two reserves is left empty // this step is important to avoid an error withdrawing all left liquidity if (virtualCurrencyReserve != 0 && virtualTokenReserve != 0) { boughtCurrencyNumerator = getSellPrice(soldTokenNumerator, virtualTokenReserve, virtualCurrencyReserve); // Discount royalty currency (royaltyRecipient, royaltyNumerator) = getRoyaltyInfo(_tokenId, boughtCurrencyNumerator); boughtCurrencyNumerator = boughtCurrencyNumerator.sub(royaltyNumerator); currencyNumerator = currencyNumerator.add(boughtCurrencyNumerator); // Add royalty numerator (needs to be converted to ROYALTIES_DENOMINATOR) royaltyNumerator = royaltyNumerator.mul(ROYALTIES_DENOMINATOR) / _totalLiquidity; } } // Calculate amounts currencyAmount = currencyNumerator / _totalLiquidity; tokenAmount = tokenNumerator / _totalLiquidity; } /** * @dev Burn liquidity pool tokens to withdraw currency && Tokens at current ratio. * @dev Sorting _tokenIds is mandatory for efficient way of preventing duplicated IDs (which would lead to errors) * @param _provider Address that removes liquidity to the reserve * @param _tokenIds Array of Token IDs where liquidity is removed * @param _poolTokenAmounts Array of Amount of liquidity pool tokens burned for each Token id in _tokenIds. * @param _minCurrency Minimum currency withdrawn for each Token id in _tokenIds. * @param _minTokens Minimum Tokens id withdrawn for each Token id in _tokenIds. * @param _deadline Timestamp after which this transaction will be reverted */ function _removeLiquidity( address _provider, uint256[] memory _tokenIds, uint256[] memory _poolTokenAmounts, uint256[] memory _minCurrency, uint256[] memory _minTokens, uint256 _deadline) internal nonReentrant() { // Input validation require(_deadline > block.timestamp, "NE20#15"); // FnbswapExchange20#_removeLiquidity: DEADLINE_EXCEEDED // Initialize variables uint256 nTokens = _tokenIds.length; // Number of Token IDs to deposit uint256 totalCurrency = 0; // Total amount of currency to transfer uint256[] memory tokenAmounts = new uint256[](nTokens); // Amount of Tokens to transfer for each id // Structs contain most information for the event // notice: tokenAmounts and tokenIds are absent because we already // either have those arrays constructed or we need to construct them for other reasons LiquidityRemovedEventObj[] memory eventObjs = new LiquidityRemovedEventObj[](nTokens); // Get token reserves uint256[] memory tokenReserves = _getTokenReserves(_tokenIds); // Assumes NIFTY liquidity tokens are already received by contract, but not // the currency nor the Tokens Ids // Remove liquidity for each Token ID in _tokenIds for (uint256 i = 0; i < nTokens; i++) { // Store current id and amount from argument arrays uint256 id = _tokenIds[i]; uint256 amountPool = _poolTokenAmounts[i]; // Load total liquidity pool token supply for Token _id uint256 totalLiquidity = totalSupplies[id]; require(totalLiquidity > 0, "NE20#16"); // FnbswapExchange20#_removeLiquidity: NULL_TOTAL_LIQUIDITY // Load currency and Token reserve's supply of Token id uint256 currencyReserve = currencyReserves[id]; // Calculate amount to withdraw for currency and Token _id uint256 currencyAmount; uint256 tokenAmount; { uint256 tokenReserve = tokenReserves[i]; uint256 soldTokenNumerator; uint256 boughtCurrencyNumerator; address royaltyRecipient; uint256 royaltyNumerator; ( currencyAmount, tokenAmount, soldTokenNumerator, boughtCurrencyNumerator, royaltyRecipient, royaltyNumerator ) = _toRoundedLiquidity(id, amountPool, tokenReserve, currencyReserve, totalLiquidity); // Add royalties royaltiesNumerator[royaltyRecipient] = royaltiesNumerator[royaltyRecipient].add(royaltyNumerator); // Add trade info to event eventObjs[i].soldTokenNumerator = soldTokenNumerator; eventObjs[i].boughtCurrencyNumerator = boughtCurrencyNumerator; eventObjs[i].totalSupply = totalLiquidity; } // Verify if amounts to withdraw respect minimums specified require(currencyAmount >= _minCurrency[i], "NE20#17"); // FnbswapExchange20#_removeLiquidity: INSUFFICIENT_CURRENCY_AMOUNT require(tokenAmount >= _minTokens[i], "NE20#18"); // FnbswapExchange20#_removeLiquidity: INSUFFICIENT_TOKENS // Update total liquidity pool token supply of Token _id totalSupplies[id] = totalLiquidity.sub(amountPool); // Update currency reserve size for Token id currencyReserves[id] = currencyReserve.sub(currencyAmount); // Update totalCurrency and tokenAmounts totalCurrency = totalCurrency.add(currencyAmount); tokenAmounts[i] = tokenAmount; eventObjs[i].currencyAmount = currencyAmount; } // Burn liquidity pool tokens for offchain supplies _batchBurn(address(this), _tokenIds, _poolTokenAmounts); // Transfer total currency and all Tokens ids TransferHelper.safeTransfer(currency, _provider, totalCurrency); token.safeBatchTransferFrom(address(this), _provider, _tokenIds, tokenAmounts, ""); // Emit event emit LiquidityRemoved(_provider, _tokenIds, tokenAmounts, eventObjs); } /***********************************| | Receiver Methods Handler | |__________________________________*/ // Method signatures for onReceive control logic // bytes4(keccak256( // "_tokenToCurrency(uint256[],uint256[],uint256,uint256,address,address[],uint256[])" // )); bytes4 internal constant SELLTOKENS_SIG = 0xade79c7a; // bytes4(keccak256( // "_addLiquidity(address,uint256[],uint256[],uint256[],uint256)" // )); bytes4 internal constant ADDLIQUIDITY_SIG = 0x82da2b73; // bytes4(keccak256( // "_removeLiquidity(address,uint256[],uint256[],uint256[],uint256[],uint256)" // )); bytes4 internal constant REMOVELIQUIDITY_SIG = 0x5c0bf259; // bytes4(keccak256( // "DepositTokens()" // )); bytes4 internal constant DEPOSIT_SIG = 0xc8c323f9; /***********************************| | Buying Tokens | |__________________________________*/ /** * @notice Convert currency tokens to Tokens _id and transfers Tokens to recipient. * @dev User specifies MAXIMUM inputs (_maxCurrency) and EXACT outputs. * @dev Assumes that all trades will be successful, or revert the whole tx * @dev Exceeding currency tokens sent will be refunded to recipient * @dev Sorting IDs is mandatory for efficient way of preventing duplicated IDs (which would lead to exploit) * @param _tokenIds Array of Tokens ID that are bought * @param _tokensBoughtAmounts Amount of Tokens id bought for each corresponding Token id in _tokenIds * @param _maxCurrency Total maximum amount of currency tokens to spend for all Token ids * @param _deadline Timestamp after which this transaction will be reverted * @param _recipient The address that receives output Tokens and refund * @param _extraFeeRecipients Array of addresses that will receive extra fee * @param _extraFeeAmounts Array of amounts of currency that will be sent as extra fee * @return currencySold How much currency was actually sold. */ function buyTokens( uint256[] memory _tokenIds, uint256[] memory _tokensBoughtAmounts, uint256 _maxCurrency, uint256 _deadline, address _recipient, address[] memory _extraFeeRecipients, uint256[] memory _extraFeeAmounts ) override external returns (uint256[] memory) { require(_deadline >= block.timestamp, "NE20#19"); // FnbswapExchange20#buyTokens: DEADLINE_EXCEEDED require(_tokenIds.length > 0, "NE20#20"); // FnbswapExchange20#buyTokens: INVALID_CURRENCY_IDS_AMOUNT // Transfer the tokens for purchase TransferHelper.safeTransferFrom(currency, msg.sender, address(this), _maxCurrency); address recipient = _recipient == address(0x0) ? msg.sender : _recipient; // Set the extra fee aside to recipients ahead of purchase, if any. uint256 maxCurrency = _maxCurrency; uint256 nExtraFees = _extraFeeRecipients.length; require(nExtraFees == _extraFeeAmounts.length, "NE20#21"); // FnbswapExchange20#buyTokens: EXTRA_FEES_ARRAYS_ARE_NOT_SAME_LENGTH for (uint256 i = 0; i < nExtraFees; i++) { if (_extraFeeAmounts[i] > 0) { maxCurrency = maxCurrency.sub(_extraFeeAmounts[i]); royaltiesNumerator[_extraFeeRecipients[i]] = royaltiesNumerator[_extraFeeRecipients[i]].add(_extraFeeAmounts[i].mul(ROYALTIES_DENOMINATOR)); } } // Execute trade and retrieve amount of currency spent uint256[] memory currencySold = _currencyToToken(_tokenIds, _tokensBoughtAmounts, maxCurrency, _deadline, recipient); emit TokensPurchase(msg.sender, recipient, _tokenIds, _tokensBoughtAmounts, currencySold, _extraFeeRecipients, _extraFeeAmounts); return currencySold; } /** * @notice Handle which method is being called on transfer * @dev `_data` must be encoded as follow: abi.encode(bytes4, MethodObj) * where bytes4 argument is the MethodObj object signature passed as defined * in the `Signatures for onReceive control logic` section above * @param _from The address which previously owned the Token * @param _ids An array containing ids of each Token being transferred * @param _amounts An array containing amounts of each Token being transferred * @param _data Method signature and corresponding encoded arguments for method to call on *this* contract * @return bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)") */ function onERC1155BatchReceived( address, // _operator, address _from, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data) override public returns(bytes4) { // This function assumes that the ERC-1155 token contract can // only call `onERC1155BatchReceived()` via a valid token transfer. // Users must be responsible and only use this Fnbswap exchange // contract with ERC-1155 compliant token contracts. // Obtain method to call via object signature bytes4 functionSignature = abi.decode(_data, (bytes4)); /***********************************| | Selling Tokens | |__________________________________*/ if (functionSignature == SELLTOKENS_SIG) { // Tokens received need to be Token contract require(msg.sender == address(token), "NE20#22"); // FnbswapExchange20#onERC1155BatchReceived: INVALID_TOKENS_TRANSFERRED // Decode SellTokensObj from _data to call _tokenToCurrency() SellTokensObj memory obj; (, obj) = abi.decode(_data, (bytes4, SellTokensObj)); address recipient = obj.recipient == address(0x0) ? _from : obj.recipient; // Validate fee arrays require(obj.extraFeeRecipients.length == obj.extraFeeAmounts.length, "NE20#23"); // FnbswapExchange20#buyTokens: EXTRA_FEES_ARRAYS_ARE_NOT_SAME_LENGTH // Execute trade and retrieve amount of currency received uint256[] memory currencyBought = _tokenToCurrency(_ids, _amounts, obj.minCurrency, obj.deadline, recipient, obj.extraFeeRecipients, obj.extraFeeAmounts); emit CurrencyPurchase(_from, recipient, _ids, _amounts, currencyBought, obj.extraFeeRecipients, obj.extraFeeAmounts); /***********************************| | Adding Liquidity Tokens | |__________________________________*/ } else if (functionSignature == ADDLIQUIDITY_SIG) { // Only allow to receive ERC-1155 tokens from `token` contract require(msg.sender == address(token), "NE20#24"); // FnbswapExchange20#onERC1155BatchReceived: INVALID_TOKEN_TRANSFERRED // Decode AddLiquidityObj from _data to call _addLiquidity() AddLiquidityObj memory obj; (, obj) = abi.decode(_data, (bytes4, AddLiquidityObj)); _addLiquidity(_from, _ids, _amounts, obj.maxCurrency, obj.deadline); /***********************************| | Removing iquidity Tokens | |__________________________________*/ } else if (functionSignature == REMOVELIQUIDITY_SIG) { // Tokens received need to be NIFTY-1155 tokens require(msg.sender == address(this), "NE20#25"); // FnbswapExchange20#onERC1155BatchReceived: INVALID_NIFTY_TOKENS_TRANSFERRED // Decode RemoveLiquidityObj from _data to call _removeLiquidity() RemoveLiquidityObj memory obj; (, obj) = abi.decode(_data, (bytes4, RemoveLiquidityObj)); _removeLiquidity(_from, _ids, _amounts, obj.minCurrency, obj.minTokens, obj.deadline); /***********************************| | Deposits & Invalid Calls | |__________________________________*/ } else if (functionSignature == DEPOSIT_SIG) { // Do nothing for when contract is self depositing // This could be use to deposit currency "by accident", which would be locked require(msg.sender == address(currency), "NE20#26"); // FnbswapExchange20#onERC1155BatchReceived: INVALID_TOKENS_DEPOSITED } else { revert("NE20#27"); // FnbswapExchange20#onERC1155BatchReceived: INVALID_METHOD } return ERC1155_BATCH_RECEIVED_VALUE; } /** * @dev Will pass to onERC115Batch5Received */ function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _amount, bytes memory _data) override public returns(bytes4) { uint256[] memory ids = new uint256[](1); uint256[] memory amounts = new uint256[](1); ids[0] = _id; amounts[0] = _amount; require( ERC1155_BATCH_RECEIVED_VALUE == onERC1155BatchReceived(_operator, _from, ids, amounts, _data), "NE20#28" // FnbswapExchange20#onERC1155Received: INVALID_ONRECEIVED_MESSAGE ); return ERC1155_RECEIVED_VALUE; } /** * @notice Prevents receiving Ether or calls to unsuported methods */ fallback () external { revert("NE20#29"); // FnbswapExchange20:UNSUPPORTED_METHOD } /***********************************| | Royalty Functions | |__________________________________*/ /** * @notice Will set the royalties fees and recipient for contracts that don't support ERC-2981 * @param _fee Fee pourcentage with a 10000 basis (e.g. 0.3% is 3 and 1% is 10 and 100% is 1000) * @param _recipient Address where to send the fees to */ function setRoyaltyInfo(uint256 _fee, address _recipient) onlyOwner public { // Don't use IS_ERC2981 in case token contract was updated bool isERC2981 = token.supportsInterface(type(IERC2981).interfaceId); require(!isERC2981, "NE20#30"); // FnbswapExchange20#setRoyaltyInfo: TOKEN SUPPORTS ERC-2981 require(_fee <= MAX_ROYALTY, "NE20#31"); // FnbswapExchange20#setRoyaltyInfo: ROYALTY_FEE_IS_TOO_HIGH globalRoyaltyFee = _fee; globalRoyaltyRecipient = _recipient; emit RoyaltyChanged(_recipient, _fee); } /** * @notice Will send the royalties that _royaltyRecipient can claim, if any * @dev Anyone can call this function such that payout could be distributed * regularly instead of being claimed. * @param _royaltyRecipient Address that is able to claim royalties */ function sendRoyalties(address _royaltyRecipient) override external { uint256 royaltyAmount = royaltiesNumerator[_royaltyRecipient] / ROYALTIES_DENOMINATOR; royaltiesNumerator[_royaltyRecipient] = royaltiesNumerator[_royaltyRecipient] % ROYALTIES_DENOMINATOR; TransferHelper.safeTransfer(currency, _royaltyRecipient, royaltyAmount); } /** * @notice Will return how much of currency need to be paid for the royalty * @notice Royalty is capped at 25% of the total amount of currency * @param _tokenId ID of the erc-1155 token being traded * @param _cost Amount of currency sent/received for the trade * @return recipient Address that will be able to claim the royalty * @return royalty Amount of currency that will be sent to royalty recipient */ function getRoyaltyInfo(uint256 _tokenId, uint256 _cost) public view returns (address recipient, uint256 royalty) { if (IS_ERC2981) { // Add a try/catch in-case token *removed* ERC-2981 support try IERC2981(address(token)).royaltyInfo(_tokenId, _cost) returns(address _r, uint256 _c) { // Cap to 25% of the total amount of currency uint256 max = _cost.mul(MAX_ROYALTY) / ROYALTIES_DENOMINATOR; return (_r, _c > max ? max : _c); } catch { // Default back to global setting if error occurs return (globalRoyaltyRecipient, (_cost.mul(globalRoyaltyFee)) / ROYALTIES_DENOMINATOR); } } else { return (globalRoyaltyRecipient, (_cost.mul(globalRoyaltyFee)) / ROYALTIES_DENOMINATOR); } } /***********************************| | Getter Functions | |__________________________________*/ /** * @notice Get amount of currency in reserve for each Token _id in _ids * @param _ids Array of ID sto query currency reserve of * @return amount of currency in reserve for each Token _id */ function getCurrencyReserves( uint256[] calldata _ids) override external view returns (uint256[] memory) { uint256 nIds = _ids.length; uint256[] memory currencyReservesReturn = new uint256[](nIds); for (uint256 i = 0; i < nIds; i++) { currencyReservesReturn[i] = currencyReserves[_ids[i]]; } return currencyReservesReturn; } /** * @notice Return price for `currency => Token _id` trades with an exact token amount. * @param _ids Array of ID of tokens bought. * @param _tokensBought Amount of Tokens bought. * @return Amount of currency needed to buy Tokens in _ids for amounts in _tokensBought */ function getPrice_currencyToToken( uint256[] calldata _ids, uint256[] calldata _tokensBought) override external view returns (uint256[] memory) { uint256 nIds = _ids.length; uint256[] memory prices = new uint256[](nIds); for (uint256 i = 0; i < nIds; i++) { // Load Token id reserve uint256 tokenReserve = token.balanceOf(address(this), _ids[i]); prices[i] = getBuyPriceWithRoyalty(_ids[i], _tokensBought[i], currencyReserves[_ids[i]], tokenReserve); } // Return prices return prices; } /** * @notice Return price for `Token _id => currency` trades with an exact token amount. * @param _ids Array of IDs token sold. * @param _tokensSold Array of amount of each Token sold. * @return Amount of currency that can be bought for Tokens in _ids for amounts in _tokensSold */ function getPrice_tokenToCurrency( uint256[] calldata _ids, uint256[] calldata _tokensSold) override external view returns (uint256[] memory) { uint256 nIds = _ids.length; uint256[] memory prices = new uint256[](nIds); for (uint256 i = 0; i < nIds; i++) { // Load Token id reserve uint256 tokenReserve = token.balanceOf(address(this), _ids[i]); prices[i] = getSellPriceWithRoyalty(_ids[i], _tokensSold[i], tokenReserve, currencyReserves[_ids[i]]); } // Return price return prices; } /** * @return Address of Token that is sold on this exchange. */ function getTokenAddress() override external view returns (address) { return address(token); } /** * @return LP fee per 1000 units */ function getLPFee() override external view returns (uint256) { return 1000-FEE_MULTIPLIER; } /** * @return Address of the currency contract that is used as currency */ function getCurrencyInfo() override external view returns (address) { return (address(currency)); } /** * @notice Get total supply of liquidity tokens * @param _ids ID of the Tokens * @return The total supply of each liquidity token id provided in _ids */ function getTotalSupply(uint256[] calldata _ids) override external view returns (uint256[] memory) { // Number of ids uint256 nIds = _ids.length; // Variables uint256[] memory batchTotalSupplies = new uint256[](nIds); // Iterate over each owner and token ID for (uint256 i = 0; i < nIds; i++) { batchTotalSupplies[i] = totalSupplies[_ids[i]]; } return batchTotalSupplies; } /** * @return Address of factory that created this exchange. */ function getFactoryAddress() override external view returns (address) { return factory; } /** * @return Global royalty fee % if not supporting ERC-2981 */ function getGlobalRoyaltyFee() override external view returns (uint256) { return globalRoyaltyFee; } /** * @return Global royalty recipient if token not supporting ERC-2981 */ function getGlobalRoyaltyRecipient() override external view returns (address) { return globalRoyaltyRecipient; } /** * @return Get amount of currency in royalty an address can claim * @param _royaltyRecipient Address to check the claimable royalties */ function getRoyalties(address _royaltyRecipient) override external view returns (uint256) { return royaltiesNumerator[_royaltyRecipient] / ROYALTIES_DENOMINATOR; } function getRoyaltiesNumerator(address _royaltyRecipient) override external view returns (uint256) { return royaltiesNumerator[_royaltyRecipient]; } /***********************************| | Utility Functions | |__________________________________*/ /** * @notice Divides two numbers and add 1 if there is a rounding error * @param a Numerator * @param b Denominator */ function divRound(uint256 a, uint256 b) internal pure returns (uint256, bool) { return a % b == 0 ? (a/b, false) : ((a/b).add(1), true); } /** * @notice Return Token reserves for given Token ids * @dev Assumes that ids are sorted from lowest to highest with no duplicates. * This assumption allows for checking the token reserves only once, otherwise * token reserves need to be re-checked individually or would have to do more expensive * duplication checks. * @param _tokenIds Array of IDs to query their Reserve balance. * @return Array of Token ids' reserves */ function _getTokenReserves( uint256[] memory _tokenIds) internal view returns (uint256[] memory) { uint256 nTokens = _tokenIds.length; // Regular balance query if only 1 token, otherwise batch query if (nTokens == 1) { uint256[] memory tokenReserves = new uint256[](1); tokenReserves[0] = token.balanceOf(address(this), _tokenIds[0]); return tokenReserves; } else { // Lazy check preventing duplicates & build address array for query address[] memory thisAddressArray = new address[](nTokens); thisAddressArray[0] = address(this); for (uint256 i = 1; i < nTokens; i++) { require(_tokenIds[i-1] < _tokenIds[i], "NE20#32"); // FnbswapExchange20#_getTokenReserves: UNSORTED_OR_DUPLICATE_TOKEN_IDS thisAddressArray[i] = address(this); } return token.balanceOfBatch(thisAddressArray, _tokenIds); } } /** * @notice Indicates whether a contract implements the `ERC1155TokenReceiver` functions and so can accept ERC1155 token types. * @param interfaceID The ERC-165 interface ID that is queried for support.s * @dev This function MUST return true if it implements the ERC1155TokenReceiver interface and ERC-165 interface. * This function MUST NOT consume more thsan 5,000 gas. * @return Whether a given interface is supported */ function supportsInterface(bytes4 interfaceID) public override pure returns (bool) { return interfaceID == type(IERC20).interfaceId || interfaceID == type(IERC165).interfaceId || interfaceID == type(IERC1155).interfaceId || interfaceID == type(IERC1155TokenReceiver).interfaceId || interfaceID == type(IERC1155Metadata).interfaceId; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.6.0; // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false library TransferHelper { function safeApprove( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('approve(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::safeApprove: approve failed' ); } function safeTransfer( address token, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transfer(address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::safeTransfer: transfer failed' ); } function safeTransferFrom( address token, address from, address to, uint256 value ) internal { // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); require( success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper::transferFrom: transferFrom failed' ); } function safeTransferETH(address to, uint256 value) internal { (bool success, ) = to.call{value: value}(new bytes(0)); require(success, 'TransferHelper::safeTransferETH: ETH transfer failed'); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol) pragma solidity 0.7.4; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { // On the first call to nonReentrant, _notEntered will be true require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; _; // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } }
pragma solidity 0.7.4; pragma experimental ABIEncoderV2; import "../interfaces/IOwnable.sol"; /** * @title Ownable * @dev The Ownable contract inherits the owner of a parent contract as its owner, * and provides basic authorization control functions, this simplifies the * implementation of "user permissions". */ contract DelegatedOwnable { address internal ownableParent; event ParentOwnerChanged(address indexed previousParent, address indexed newParent); /** * @dev The Ownable constructor sets the original `ownableParent` of the contract to the specied address * @param _firstOwnableParent Address of the first ownable parent contract */ constructor (address _firstOwnableParent) { try IOwnable(_firstOwnableParent).getOwner() { // Do nothing if parent has ownable function } catch { revert("DO#1"); // PARENT IS NOT OWNABLE } ownableParent = _firstOwnableParent; emit ParentOwnerChanged(address(0), _firstOwnableParent); } /** * @dev Throws if called by any account other than the master owner. */ modifier onlyOwner() { require(msg.sender == getOwner(), "DO#2"); // DelegatedOwnable#onlyOwner: SENDER_IS_NOT_OWNER _; } /** * @notice Will use the owner address of another parent contract * @param _newParent Address of the new owner */ function changeOwnableParent(address _newParent) public onlyOwner { require(_newParent != address(0), "D3"); // DelegatedOwnable#changeOwnableParent: INVALID_ADDRESS ownableParent = _newParent; emit ParentOwnerChanged(ownableParent, _newParent); } /** * @notice Returns the address of the owner. */ function getOwner() public view returns (address) { return IOwnable(ownableParent).getOwner(); } }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; pragma experimental ABIEncoderV2; interface IFnbswapExchange20 { /***********************************| | Events | |__________________________________*/ event TokensPurchase( address indexed buyer, address indexed recipient, uint256[] tokensBoughtIds, uint256[] tokensBoughtAmounts, uint256[] currencySoldAmounts, address[] extraFeeRecipients, uint256[] extraFeeAmounts ); event CurrencyPurchase( address indexed buyer, address indexed recipient, uint256[] tokensSoldIds, uint256[] tokensSoldAmounts, uint256[] currencyBoughtAmounts, address[] extraFeeRecipients, uint256[] extraFeeAmounts ); event LiquidityAdded( address indexed provider, uint256[] tokenIds, uint256[] tokenAmounts, uint256[] currencyAmounts ); struct LiquidityRemovedEventObj { uint256 currencyAmount; uint256 soldTokenNumerator; uint256 boughtCurrencyNumerator; uint256 totalSupply; } event LiquidityRemoved( address indexed provider, uint256[] tokenIds, uint256[] tokenAmounts, LiquidityRemovedEventObj[] details ); event RoyaltyChanged( address indexed royaltyRecipient, uint256 royaltyFee ); struct SellTokensObj { address recipient; // Who receives the currency uint256 minCurrency; // Total minimum number of currency expected for all tokens sold address[] extraFeeRecipients; // Array of addresses that will receive extra fee uint256[] extraFeeAmounts; // Array of amounts of currency that will be sent as extra fee uint256 deadline; // Timestamp after which the tx isn't valid anymore } struct AddLiquidityObj { uint256[] maxCurrency; // Maximum number of currency to deposit with tokens uint256 deadline; // Timestamp after which the tx isn't valid anymore } struct RemoveLiquidityObj { uint256[] minCurrency; // Minimum number of currency to withdraw uint256[] minTokens; // Minimum number of tokens to withdraw uint256 deadline; // Timestamp after which the tx isn't valid anymore } /***********************************| | Purchasing Functions | |__________________________________*/ /** * @notice Convert currency tokens to Tokens _id and transfers Tokens to recipient. * @dev User specifies MAXIMUM inputs (_maxCurrency) and EXACT outputs. * @dev Assumes that all trades will be successful, or revert the whole tx * @dev Exceeding currency tokens sent will be refunded to recipient * @dev Sorting IDs is mandatory for efficient way of preventing duplicated IDs (which would lead to exploit) * @param _tokenIds Array of Tokens ID that are bought * @param _tokensBoughtAmounts Amount of Tokens id bought for each corresponding Token id in _tokenIds * @param _maxCurrency Total maximum amount of currency tokens to spend for all Token ids * @param _deadline Timestamp after which this transaction will be reverted * @param _recipient The address that receives output Tokens and refund * @param _extraFeeRecipients Array of addresses that will receive extra fee * @param _extraFeeAmounts Array of amounts of currency that will be sent as extra fee * @return currencySold How much currency was actually sold. */ function buyTokens( uint256[] memory _tokenIds, uint256[] memory _tokensBoughtAmounts, uint256 _maxCurrency, uint256 _deadline, address _recipient, address[] memory _extraFeeRecipients, uint256[] memory _extraFeeAmounts ) external returns (uint256[] memory); /***********************************| | Royalties Functions | |__________________________________*/ /** * @notice Will send the royalties that _royaltyRecipient can claim, if any * @dev Anyone can call this function such that payout could be distributed * regularly instead of being claimed. * @param _royaltyRecipient Address that is able to claim royalties */ function sendRoyalties(address _royaltyRecipient) external; /***********************************| | OnReceive Functions | |__________________________________*/ /** * @notice Handle which method is being called on Token transfer * @dev `_data` must be encoded as follow: abi.encode(bytes4, MethodObj) * where bytes4 argument is the MethodObj object signature passed as defined * in the `Signatures for onReceive control logic` section above * @param _operator The address which called the `safeTransferFrom` function * @param _from The address which previously owned the token * @param _id The id of the token being transferred * @param _amount The amount of tokens being transferred * @param _data Method signature and corresponding encoded arguments for method to call on *this* contract * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` */ function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _amount, bytes calldata _data) external returns(bytes4); /** * @notice Handle which method is being called on transfer * @dev `_data` must be encoded as follow: abi.encode(bytes4, MethodObj) * where bytes4 argument is the MethodObj object signature passed as defined * in the `Signatures for onReceive control logic` section above * @param _from The address which previously owned the Token * @param _ids An array containing ids of each Token being transferred * @param _amounts An array containing amounts of each Token being transferred * @param _data Method signature and corresponding encoded arguments for method to call on *this* contract * @return bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)") */ function onERC1155BatchReceived(address, address _from, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data) external returns(bytes4); /***********************************| | Getter Functions | |__________________________________*/ /** * @dev Pricing function used for converting between currency token to Tokens. * @param _assetBoughtAmount Amount of Tokens being bought. * @param _assetSoldReserve Amount of currency tokens in exchange reserves. * @param _assetBoughtReserve Amount of Tokens (output type) in exchange reserves. * @return Amount of currency tokens to send to Niftyswap. */ function getBuyPrice(uint256 _assetBoughtAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) external view returns (uint256); /** * @dev Pricing function used for converting Tokens to currency token (including royalty fee) * @param _tokenId Id ot token being sold * @param _assetBoughtAmount Amount of Tokens being bought. * @param _assetSoldReserve Amount of currency tokens in exchange reserves. * @param _assetBoughtReserve Amount of Tokens (output type) in exchange reserves. * @return price Amount of currency tokens to send to Niftyswap. */ function getBuyPriceWithRoyalty(uint256 _tokenId, uint256 _assetBoughtAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) external view returns (uint256 price); /** * @dev Pricing function used for converting Tokens to currency token. * @param _assetSoldAmount Amount of Tokens being sold. * @param _assetSoldReserve Amount of Tokens in exchange reserves. * @param _assetBoughtReserve Amount of currency tokens in exchange reserves. * @return Amount of currency tokens to receive from Niftyswap. */ function getSellPrice(uint256 _assetSoldAmount,uint256 _assetSoldReserve, uint256 _assetBoughtReserve) external view returns (uint256); /** * @dev Pricing function used for converting Tokens to currency token (including royalty fee) * @param _tokenId Id ot token being sold * @param _assetSoldAmount Amount of Tokens being sold. * @param _assetSoldReserve Amount of Tokens in exchange reserves. * @param _assetBoughtReserve Amount of currency tokens in exchange reserves. * @return price Amount of currency tokens to receive from Niftyswap. */ function getSellPriceWithRoyalty(uint256 _tokenId, uint256 _assetSoldAmount, uint256 _assetSoldReserve, uint256 _assetBoughtReserve) external view returns (uint256 price); /** * @notice Get amount of currency in reserve for each Token _id in _ids * @param _ids Array of ID sto query currency reserve of * @return amount of currency in reserve for each Token _id */ function getCurrencyReserves(uint256[] calldata _ids) external view returns (uint256[] memory); /** * @notice Return price for `currency => Token _id` trades with an exact token amount. * @param _ids Array of ID of tokens bought. * @param _tokensBought Amount of Tokens bought. * @return Amount of currency needed to buy Tokens in _ids for amounts in _tokensBought */ function getPrice_currencyToToken(uint256[] calldata _ids, uint256[] calldata _tokensBought) external view returns (uint256[] memory); /** * @notice Return price for `Token _id => currency` trades with an exact token amount. * @param _ids Array of IDs token sold. * @param _tokensSold Array of amount of each Token sold. * @return Amount of currency that can be bought for Tokens in _ids for amounts in _tokensSold */ function getPrice_tokenToCurrency(uint256[] calldata _ids, uint256[] calldata _tokensSold) external view returns (uint256[] memory); /** * @notice Get total supply of liquidity tokens * @param _ids ID of the Tokens * @return The total supply of each liquidity token id provided in _ids */ function getTotalSupply(uint256[] calldata _ids) external view returns (uint256[] memory); /** * @return Address of Token that is sold on this exchange. */ function getTokenAddress() external view returns (address); /** * @return LP fee per 1000 units */ function getLPFee() external view returns (uint256); /** * @return Address of the currency contract that is used as currency */ function getCurrencyInfo() external view returns (address); /** * @return Address of factory that created this exchange. */ function getFactoryAddress() external view returns (address); /** * @return Global royalty fee % if not supporting ERC-2981 */ function getGlobalRoyaltyFee() external view returns (uint256); /** * @return Global royalty recipient if token not supporting ERC-2981 */ function getGlobalRoyaltyRecipient() external view returns (address); /** * @return Get amount of currency in royalty an address can claim * @param _royaltyRecipient Address to check the claimable royalties */ function getRoyalties(address _royaltyRecipient) external view returns (uint256); function getRoyaltiesNumerator(address _royaltyRecipient) external view returns (uint256); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; import "contracts/FnbswapExchange/erc-1155/contracts/interfaces/IERC165.sol"; /** * @dev Interface for the NFT Royalty Standard */ interface IERC2981 is IERC165 { /** * @notice Called with the sale price to determine how much royalty * is owed and to whom. * @param _tokenId - the NFT asset queried for royalty information * @param _salePrice - the sale price of the NFT asset specified by _tokenId * @return receiver - address of who should be sent the royalty payment * @return royaltyAmount - the royalty payment amount for _salePrice */ function royaltyInfo( uint256 _tokenId, uint256 _salePrice ) external view returns ( address receiver, uint256 royaltyAmount ); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; /** * @dev ERC-1155 interface for accepting safe transfers. */ interface IERC1155TokenReceiver { /** * @notice Handle the receipt of a single ERC1155 token type * @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeTransferFrom` after the balance has been updated * This function MAY throw to revert and reject the transfer * Return of other amount than the magic value MUST result in the transaction being reverted * Note: The token contract address is always the message sender * @param _operator The address which called the `safeTransferFrom` function * @param _from The address which previously owned the token * @param _id The id of the token being transferred * @param _amount The amount of tokens being transferred * @param _data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` */ function onERC1155Received(address _operator, address _from, uint256 _id, uint256 _amount, bytes calldata _data) external returns(bytes4); /** * @notice Handle the receipt of multiple ERC1155 token types * @dev An ERC1155-compliant smart contract MUST call this function on the token recipient contract, at the end of a `safeBatchTransferFrom` after the balances have been updated * This function MAY throw to revert and reject the transfer * Return of other amount than the magic value WILL result in the transaction being reverted * Note: The token contract address is always the message sender * @param _operator The address which called the `safeBatchTransferFrom` function * @param _from The address which previously owned the token * @param _ids An array containing ids of each token being transferred * @param _amounts An array containing amounts of each token being transferred * @param _data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` */ function onERC1155BatchReceived(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data) external returns(bytes4); }
pragma solidity ^0.7.4; import "./IERC1155Metadata.sol"; interface IDelegatedERC1155Metadata { function metadataProvider() external view returns (IERC1155Metadata); }
pragma solidity ^0.7.4; /** Note: The ERC-165 identifier for this interface is 0x0e89341c. */ interface IERC1155Metadata { /** @notice A distinct Uniform Resource Identifier (URI) for a given token. @dev URIs are defined in RFC 3986. The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema". @return URI string */ function uri(uint256 _id) external view returns (string memory); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; /** * @title ERC20 interface * @dev see https://eips.ethereum.org/EIPS/eip-20 */ interface IERC20 { function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; /** * @title ERC165 * @dev https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md */ interface IERC165 { /** * @notice Query if a contract implements an interface * @dev Interface identification is specified in ERC-165. This function * uses less than 30,000 gas * @param _interfaceId The interface identifier, as specified in ERC-165 */ function supportsInterface(bytes4 _interfaceId) external view returns (bool); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; import './IERC165.sol'; interface IERC1155 is IERC165 { /****************************************| | Events | |_______________________________________*/ /** * @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, including zero amount transfers as well as minting or burning * Operator MUST be msg.sender * When minting/creating tokens, the `_from` field MUST be set to `0x0` * When burning/destroying tokens, the `_to` field MUST be set to `0x0` * The total amount transferred from address 0x0 minus the total amount transferred to 0x0 may be used by clients and exchanges to be added to the "circulating supply" for a given token ID * To broadcast the existence of a token ID with no initial balance, the contract SHOULD emit the TransferSingle event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_amount` of 0 */ event TransferSingle(address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _amount); /** * @dev Either TransferSingle or TransferBatch MUST emit when tokens are transferred, including zero amount transfers as well as minting or burning * Operator MUST be msg.sender * When minting/creating tokens, the `_from` field MUST be set to `0x0` * When burning/destroying tokens, the `_to` field MUST be set to `0x0` * The total amount transferred from address 0x0 minus the total amount transferred to 0x0 may be used by clients and exchanges to be added to the "circulating supply" for a given token ID * To broadcast the existence of multiple token IDs with no initial balance, this SHOULD emit the TransferBatch event from `0x0` to `0x0`, with the token creator as `_operator`, and a `_amount` of 0 */ event TransferBatch(address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _amounts); /** * @dev MUST emit when an approval is updated */ event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); /****************************************| | Functions | |_______________________________________*/ /** * @notice Transfers amount of an _id from the _from address to the _to address specified * @dev MUST emit TransferSingle event on success * Caller must be approved to manage the _from account's tokens (see isApprovedForAll) * MUST throw if `_to` is the zero address * MUST throw if balance of sender for token `_id` is lower than the `_amount` sent * MUST throw on any other error * When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). If so, it MUST call `onERC1155Received` on `_to` and revert if the return amount is not `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * @param _from Source address * @param _to Target address * @param _id ID of the token type * @param _amount Transfered amount * @param _data Additional data with no specified format, sent in call to `_to` */ function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes calldata _data) external; /** * @notice Send multiple types of Tokens from the _from address to the _to address (with safety call) * @dev MUST emit TransferBatch event on success * Caller must be approved to manage the _from account's tokens (see isApprovedForAll) * MUST throw if `_to` is the zero address * MUST throw if length of `_ids` is not the same as length of `_amounts` * MUST throw if any of the balance of sender for token `_ids` is lower than the respective `_amounts` sent * MUST throw on any other error * When transfer is complete, this function MUST check if `_to` is a smart contract (code size > 0). If so, it MUST call `onERC1155BatchReceived` on `_to` and revert if the return amount is not `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * Transfers and events MUST occur in the array order they were submitted (_ids[0] before _ids[1], etc) * @param _from Source addresses * @param _to Target addresses * @param _ids IDs of each token type * @param _amounts Transfer amounts per token type * @param _data Additional data with no specified format, sent in call to `_to` */ function safeBatchTransferFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _amounts, bytes calldata _data) external; /** * @notice Get the balance of an account's Tokens * @param _owner The address of the token holder * @param _id ID of the Token * @return The _owner's balance of the Token type requested */ function balanceOf(address _owner, uint256 _id) external view returns (uint256); /** * @notice Get the balance of multiple account/token pairs * @param _owners The addresses of the token holders * @param _ids ID of the Tokens * @return The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair) */ function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory); /** * @notice Enable or disable approval for a third party ("operator") to manage all of caller's tokens * @dev MUST emit the ApprovalForAll event on success * @param _operator Address to add to the set of authorized operators * @param _approved True if the operator is approved, false to revoke approval */ function setApprovalForAll(address _operator, bool _approved) external; /** * @notice Queries the approval status of an operator for a given owner * @param _owner The owner of the Tokens * @param _operator Address of authorized operator * @return isOperator True if the operator is approved, false if not */ function isApprovedForAll(address _owner, address _operator) external view returns (bool isOperator); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; import "./ERC1155.sol"; /** * @dev Multi-Fungible Tokens with minting and burning methods. These methods assume * a parent contract to be executed as they are `internal` functions */ contract ERC1155MintBurn is ERC1155 { using SafeMath for uint256; /****************************************| | Minting Functions | |_______________________________________*/ /** * @notice Mint _amount of tokens of a given id * @param _to The address to mint tokens to * @param _id Token id to mint * @param _amount The amount to be minted * @param _data Data to pass if receiver is contract */ function _mint(address _to, uint256 _id, uint256 _amount, bytes memory _data) internal { // Add _amount balances[_to][_id] = balances[_to][_id].add(_amount); // Emit event emit TransferSingle(msg.sender, address(0x0), _to, _id, _amount); // Calling onReceive method if recipient is contract _callonERC1155Received(address(0x0), _to, _id, _amount, gasleft(), _data); } /** * @notice Mint tokens for each ids in _ids * @param _to The address to mint tokens to * @param _ids Array of ids to mint * @param _amounts Array of amount of tokens to mint per id * @param _data Data to pass if receiver is contract */ function _batchMint(address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data) internal { require(_ids.length == _amounts.length, "ERC1155MintBurn#batchMint: INVALID_ARRAYS_LENGTH"); // Number of mints to execute uint256 nMint = _ids.length; // Executing all minting for (uint256 i = 0; i < nMint; i++) { // Update storage balance balances[_to][_ids[i]] = balances[_to][_ids[i]].add(_amounts[i]); } // Emit batch mint event emit TransferBatch(msg.sender, address(0x0), _to, _ids, _amounts); // Calling onReceive method if recipient is contract _callonERC1155BatchReceived(address(0x0), _to, _ids, _amounts, gasleft(), _data); } /****************************************| | Burning Functions | |_______________________________________*/ /** * @notice Burn _amount of tokens of a given token id * @param _from The address to burn tokens from * @param _id Token id to burn * @param _amount The amount to be burned */ function _burn(address _from, uint256 _id, uint256 _amount) internal { //Substract _amount balances[_from][_id] = balances[_from][_id].sub(_amount); // Emit event emit TransferSingle(msg.sender, _from, address(0x0), _id, _amount); } /** * @notice Burn tokens of given token id for each (_ids[i], _amounts[i]) pair * @param _from The address to burn tokens from * @param _ids Array of token ids to burn * @param _amounts Array of the amount to be burned */ function _batchBurn(address _from, uint256[] memory _ids, uint256[] memory _amounts) internal { // Number of mints to execute uint256 nBurn = _ids.length; require(nBurn == _amounts.length, "ERC1155MintBurn#batchBurn: INVALID_ARRAYS_LENGTH"); // Executing all minting for (uint256 i = 0; i < nBurn; i++) { // Update storage balance balances[_from][_ids[i]] = balances[_from][_ids[i]].sub(_amounts[i]); } // Emit batch mint event emit TransferBatch(msg.sender, _from, address(0x0), _ids, _amounts); } }
pragma solidity 0.7.4; pragma experimental ABIEncoderV2; interface IOwnable { /** * @notice Transfers the ownership of the contract to new address * @param _newOwner Address of the new owner */ function transferOwnership(address _newOwner) external; /** * @notice Returns the address of the owner. */ function getOwner() external view returns (address); }
// SPDX-License-Identifier: Apache-2.0 pragma solidity 0.7.4; import "../../utils/SafeMath.sol"; import "../../interfaces/IERC1155TokenReceiver.sol"; import "../../interfaces/IERC1155.sol"; import "../../utils/Address.sol"; import "../../utils/ERC165.sol"; /** * @dev Implementation of Multi-Token Standard contract */ contract ERC1155 is IERC1155, ERC165 { using SafeMath for uint256; using Address for address; /***********************************| | Variables and Events | |__________________________________*/ // onReceive function signatures bytes4 constant internal ERC1155_RECEIVED_VALUE = 0xf23a6e61; bytes4 constant internal ERC1155_BATCH_RECEIVED_VALUE = 0xbc197c81; // Objects balances mapping (address => mapping(uint256 => uint256)) internal balances; // Operator Functions mapping (address => mapping(address => bool)) internal operators; /***********************************| | Public Transfer Functions | |__________________________________*/ /** * @notice Transfers amount amount of an _id from the _from address to the _to address specified * @param _from Source address * @param _to Target address * @param _id ID of the token type * @param _amount Transfered amount * @param _data Additional data with no specified format, sent in call to `_to` */ function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount, bytes memory _data) public override { require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155#safeTransferFrom: INVALID_OPERATOR"); require(_to != address(0),"ERC1155#safeTransferFrom: INVALID_RECIPIENT"); // require(_amount <= balances[_from][_id]) is not necessary since checked with safemath operations _safeTransferFrom(_from, _to, _id, _amount); _callonERC1155Received(_from, _to, _id, _amount, gasleft(), _data); } /** * @notice Send multiple types of Tokens from the _from address to the _to address (with safety call) * @param _from Source addresses * @param _to Target addresses * @param _ids IDs of each token type * @param _amounts Transfer amounts per token type * @param _data Additional data with no specified format, sent in call to `_to` */ function safeBatchTransferFrom(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts, bytes memory _data) public override { // Requirements require((msg.sender == _from) || isApprovedForAll(_from, msg.sender), "ERC1155#safeBatchTransferFrom: INVALID_OPERATOR"); require(_to != address(0), "ERC1155#safeBatchTransferFrom: INVALID_RECIPIENT"); _safeBatchTransferFrom(_from, _to, _ids, _amounts); _callonERC1155BatchReceived(_from, _to, _ids, _amounts, gasleft(), _data); } /***********************************| | Internal Transfer Functions | |__________________________________*/ /** * @notice Transfers amount amount of an _id from the _from address to the _to address specified * @param _from Source address * @param _to Target address * @param _id ID of the token type * @param _amount Transfered amount */ function _safeTransferFrom(address _from, address _to, uint256 _id, uint256 _amount) internal { // Update balances balances[_from][_id] = balances[_from][_id].sub(_amount); // Subtract amount balances[_to][_id] = balances[_to][_id].add(_amount); // Add amount // Emit event emit TransferSingle(msg.sender, _from, _to, _id, _amount); } /** * @notice Verifies if receiver is contract and if so, calls (_to).onERC1155Received(...) */ function _callonERC1155Received(address _from, address _to, uint256 _id, uint256 _amount, uint256 _gasLimit, bytes memory _data) internal { // Check if recipient is contract if (_to.isContract()) { bytes4 retval = IERC1155TokenReceiver(_to).onERC1155Received{gas: _gasLimit}(msg.sender, _from, _id, _amount, _data); require(retval == ERC1155_RECEIVED_VALUE, "ERC1155#_callonERC1155Received: INVALID_ON_RECEIVE_MESSAGE"); } } /** * @notice Send multiple types of Tokens from the _from address to the _to address (with safety call) * @param _from Source addresses * @param _to Target addresses * @param _ids IDs of each token type * @param _amounts Transfer amounts per token type */ function _safeBatchTransferFrom(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts) internal { require(_ids.length == _amounts.length, "ERC1155#_safeBatchTransferFrom: INVALID_ARRAYS_LENGTH"); // Number of transfer to execute uint256 nTransfer = _ids.length; // Executing all transfers for (uint256 i = 0; i < nTransfer; i++) { // Update storage balance of previous bin balances[_from][_ids[i]] = balances[_from][_ids[i]].sub(_amounts[i]); balances[_to][_ids[i]] = balances[_to][_ids[i]].add(_amounts[i]); } // Emit event emit TransferBatch(msg.sender, _from, _to, _ids, _amounts); } /** * @notice Verifies if receiver is contract and if so, calls (_to).onERC1155BatchReceived(...) */ function _callonERC1155BatchReceived(address _from, address _to, uint256[] memory _ids, uint256[] memory _amounts, uint256 _gasLimit, bytes memory _data) internal { // Pass data if recipient is contract if (_to.isContract()) { bytes4 retval = IERC1155TokenReceiver(_to).onERC1155BatchReceived{gas: _gasLimit}(msg.sender, _from, _ids, _amounts, _data); require(retval == ERC1155_BATCH_RECEIVED_VALUE, "ERC1155#_callonERC1155BatchReceived: INVALID_ON_RECEIVE_MESSAGE"); } } /***********************************| | Operator Functions | |__________________________________*/ /** * @notice Enable or disable approval for a third party ("operator") to manage all of caller's tokens * @param _operator Address to add to the set of authorized operators * @param _approved True if the operator is approved, false to revoke approval */ function setApprovalForAll(address _operator, bool _approved) external override { // Update operator status operators[msg.sender][_operator] = _approved; emit ApprovalForAll(msg.sender, _operator, _approved); } /** * @notice Queries the approval status of an operator for a given owner * @param _owner The owner of the Tokens * @param _operator Address of authorized operator * @return isOperator True if the operator is approved, false if not */ function isApprovedForAll(address _owner, address _operator) public override view returns (bool isOperator) { return operators[_owner][_operator]; } /***********************************| | Balance Functions | |__________________________________*/ /** * @notice Get the balance of an account's Tokens * @param _owner The address of the token holder * @param _id ID of the Token * @return The _owner's balance of the Token type requested */ function balanceOf(address _owner, uint256 _id) public override view returns (uint256) { return balances[_owner][_id]; } /** * @notice Get the balance of multiple account/token pairs * @param _owners The addresses of the token holders * @param _ids ID of the Tokens * @return The _owner's balance of the Token types requested (i.e. balance for each (owner, id) pair) */ function balanceOfBatch(address[] memory _owners, uint256[] memory _ids) public override view returns (uint256[] memory) { require(_owners.length == _ids.length, "ERC1155#balanceOfBatch: INVALID_ARRAY_LENGTH"); // Variables uint256[] memory batchBalances = new uint256[](_owners.length); // Iterate over each owner and token ID for (uint256 i = 0; i < _owners.length; i++) { batchBalances[i] = balances[_owners[i]][_ids[i]]; } return batchBalances; } /***********************************| | ERC165 Functions | |__________________________________*/ /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` and */ function supportsInterface(bytes4 _interfaceID) public override(ERC165, IERC165) virtual pure returns (bool) { if (_interfaceID == type(IERC1155).interfaceId) { return true; } return super.supportsInterface(_interfaceID); } }
pragma solidity 0.7.4; /** * Utility library of inline functions on addresses */ library Address { // Default hash for EOA accounts returned by extcodehash bytes32 constant internal ACCOUNT_HASH = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; /** * Returns whether the target address is a contract * @dev This function will return false if invoked during the constructor of a contract. * @param _address address of the account to check * @return Whether the target address is a contract */ function isContract(address _address) internal view returns (bool) { bytes32 codehash; // Currently there is no better way to check if there is a contract in an address // than to check the size of the code at that address or if it has a non-zero code hash or account hash assembly { codehash := extcodehash(_address) } return (codehash != 0x0 && codehash != ACCOUNT_HASH); } }
pragma solidity 0.7.4; import "../interfaces/IERC165.sol"; abstract contract ERC165 is IERC165 { /** * @notice Query if a contract implements an interface * @param _interfaceID The interface identifier, as specified in ERC-165 * @return `true` if the contract implements `_interfaceID` */ function supportsInterface(bytes4 _interfaceID) virtual override public pure returns (bool) { return _interfaceID == this.supportsInterface.selector; } }
pragma solidity 0.7.4; /** * @title SafeMath * @dev Unsigned math operations with safety checks that revert on error */ library SafeMath { /** * @dev Multiplies two unsigned integers, reverts on overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath#mul: OVERFLOW"); return c; } /** * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { // Solidity only automatically asserts when dividing by 0 require(b > 0, "SafeMath#div: DIVISION_BY_ZERO"); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath#sub: UNDERFLOW#!"); uint256 c = a - b; return c; } /** * @dev Adds two unsigned integers, reverts on overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath#add: OVERFLOW"); return c; } /** * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), * reverts when dividing by zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b != 0, "SafeMath#mod: DIVISION_BY_ZERO"); return a % b; } }
{ "optimizer": { "enabled": true, "runs": 200 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_tokenAddr","type":"address"},{"internalType":"address","name":"_currencyAddr","type":"address"},{"internalType":"uint256","name":"_lpFee","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":false,"internalType":"bool","name":"_approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokensSoldIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"tokensSoldAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"currencyBoughtAmounts","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"extraFeeRecipients","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"extraFeeAmounts","type":"uint256[]"}],"name":"CurrencyPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"currencyAmounts","type":"uint256[]"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"components":[{"internalType":"uint256","name":"currencyAmount","type":"uint256"},{"internalType":"uint256","name":"soldTokenNumerator","type":"uint256"},{"internalType":"uint256","name":"boughtCurrencyNumerator","type":"uint256"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"indexed":false,"internalType":"struct IFnbswapExchange20.LiquidityRemovedEventObj[]","name":"details","type":"tuple[]"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousParent","type":"address"},{"indexed":true,"internalType":"address","name":"newParent","type":"address"}],"name":"ParentOwnerChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"royaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"royaltyFee","type":"uint256"}],"name":"RoyaltyChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"buyer","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"tokensBoughtIds","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"tokensBoughtAmounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"currencySoldAmounts","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"extraFeeRecipients","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"extraFeeAmounts","type":"uint256[]"}],"name":"TokensPurchase","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"TransferBatch","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_operator","type":"address"},{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"TransferSingle","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_owners","type":"address[]"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"balanceOfBatch","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokensBoughtAmounts","type":"uint256[]"},{"internalType":"uint256","name":"_maxCurrency","type":"uint256"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"address[]","name":"_extraFeeRecipients","type":"address[]"},{"internalType":"uint256[]","name":"_extraFeeAmounts","type":"uint256[]"}],"name":"buyTokens","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newParent","type":"address"}],"name":"changeOwnableParent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetBoughtAmount","type":"uint256"},{"internalType":"uint256","name":"_assetSoldReserve","type":"uint256"},{"internalType":"uint256","name":"_assetBoughtReserve","type":"uint256"}],"name":"getBuyPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_assetBoughtAmount","type":"uint256"},{"internalType":"uint256","name":"_assetSoldReserve","type":"uint256"},{"internalType":"uint256","name":"_assetBoughtReserve","type":"uint256"}],"name":"getBuyPriceWithRoyalty","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrencyInfo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"getCurrencyReserves","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFactoryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalRoyaltyFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGlobalRoyaltyRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLPFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokensBought","type":"uint256[]"}],"name":"getPrice_currencyToToken","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_tokensSold","type":"uint256[]"}],"name":"getPrice_tokenToCurrency","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_royaltyRecipient","type":"address"}],"name":"getRoyalties","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_royaltyRecipient","type":"address"}],"name":"getRoyaltiesNumerator","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_cost","type":"uint256"}],"name":"getRoyaltyInfo","outputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"royalty","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetSoldAmount","type":"uint256"},{"internalType":"uint256","name":"_assetSoldReserve","type":"uint256"},{"internalType":"uint256","name":"_assetBoughtReserve","type":"uint256"}],"name":"getSellPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_tokenId","type":"uint256"},{"internalType":"uint256","name":"_assetSoldAmount","type":"uint256"},{"internalType":"uint256","name":"_assetSoldReserve","type":"uint256"},{"internalType":"uint256","name":"_assetBoughtReserve","type":"uint256"}],"name":"getSellPriceWithRoyalty","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"_ids","type":"uint256[]"}],"name":"getTotalSupply","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"isOperator","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onERC1155BatchReceived","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256[]","name":"_ids","type":"uint256[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeBatchTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_id","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_royaltyRecipient","type":"address"}],"name":"sendRoyalties","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_operator","type":"address"},{"internalType":"bool","name":"_approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"setRoyaltyInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"test","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_id","type":"uint256"}],"name":"uri","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
6101206040526009805460ff191660011790553480156200001f57600080fd5b50604051620054533803806200545383398101604081905262000042916200029b565b336001600081905550806001600160a01b031663893d20e86040518163ffffffff1660e01b815260040160206040518083038186803b1580156200008557600080fd5b505afa925050508015620000b8575060408051601f3d908101601f19168201909252620000b59181019062000277565b60015b620000e05760405162461bcd60e51b8152600401620000d79062000352565b60405180910390fd5b50600380546001600160a01b0319166001600160a01b0383169081179091556040516000907ffca4bc097843727c93a8ab8d241a38481cccf8ec96dda1a342574712cfe8eb40908290a3506001600160a01b038316158015906200014c57506001600160a01b03821615155b6200016b5760405162461bcd60e51b8152600401620000d79062000332565b6103e8811115620001905760405162461bcd60e51b8152600401620000d79062000312565b33606090811b60c0526001600160601b031984821b81166080529083901b1660a0526103e881900360e0526040516301ffc9a760e01b81526001600160a01b038416906301ffc9a790620001f09063152a902d60e11b90600401620002fd565b60206040518083038186803b1580156200020957600080fd5b505afa9250505080156200023c575060408051601f3d908101601f191682019092526200023991810190620002db565b60015b620002475762000251565b151560f81b610100525b50505062000370565b80516001600160a01b03811681146200027257600080fd5b919050565b60006020828403121562000289578081fd5b62000294826200025a565b9392505050565b600080600060608486031215620002b0578182fd5b620002bb846200025a565b9250620002cb602085016200025a565b9150604084015190509250925092565b600060208284031215620002ed578081fd5b8151801515811462000294578182fd5b6001600160e01b031991909116815260200190565b60208082526006908201526527229918119960d11b604082015260600190565b6020808252600690820152654e453230233160d01b604082015260600190565b602080825260049082015263444f233160e01b604082015260600190565b60805160601c60a05160601c60c05160601c60e0516101005160f81c61503362000420600039806114ba5250806108a35280610a555280611a4852508061055b5280610e655250806108e65280610e0452806112ce52806116505280612453528061282d5280612c31528061311152508061066b5280610b0e5280610f03528061106e52806111bc528061139152806114f55280612c6e5280613096528061344a528061361d52506150336000f3fe608060405234801561001057600080fd5b50600436106101f95760003560e01c8063893d20e81161011a578063be571468116100ad578063e985e9c51161007c578063e985e9c514610448578063f23a6e611461045b578063f242432a1461046e578063f8a8fd6d14610481578063fca16c3b14610489576101f9565b8063be571468146103f9578063c7ed115e1461040c578063d93e8aaa1461042d578063e523d3fc14610440576101f9565b8063a9c2e36c116100e9578063a9c2e36c146103ab578063aeaad208146103b3578063b5de3d14146103c6578063bc197c81146103d9576101f9565b8063893d20e81461036a578063a22cb46514610372578063a631387514610385578063a7380f6e14610398576101f9565b80632eb2c2d6116101925780634e1273f4116101615780634e1273f41461031e5780636ee8e13414610331578063863ed3001461034457806389382ca014610357576101f9565b80632eb2c2d6146102e65780633750a8bd146102fb57806338b49e2d1461030357806346adf5ca14610316576101f9565b806310fe9ae8116101ce57806310fe9ae81461028b57806314556a56146102a0578063209b96c5146102b35780632bef5e38146102d3576101f9565b80628e09d81461021a578062fdd58e1461023857806301ffc9a71461024b5780630e89341c1461026b575b60405162461bcd60e51b8152600401610211906149dd565b60405180910390fd5b61022261049c565b60405161022f9190614cad565b60405180910390f35b610222610246366004613fbc565b6104a2565b61025e61025936600461423c565b6104cd565b60405161022f9190614849565b61027e61027936600461450f565b610557565b60405161022f9190614869565b610293610669565b60405161022f9190614652565b6102226102ae366004613e10565b61068d565b6102c66102c1366004614074565b6106ad565b60405161022f91906146fc565b6102c66102e1366004614074565b61074d565b6102f96102f4366004613e80565b6107e4565b005b6102226108a1565b610222610311366004613e10565b6108c9565b6102936108e4565b6102c661032c366004614014565b610908565b61022261033f366004614584565b610a20565b6102c66103523660046140b3565b610ab8565b6102f9610365366004613e10565b610c3d565b610293610ceb565b6102f9610380366004613f8f565b610d6d565b6102f9610393366004613e10565b610ddb565b6102226103a63660046145af565b610e2e565b610293610e63565b6102226103c13660046145af565b610e87565b6102f96103d436600461453f565b610eb1565b6103ec6103e7366004613e80565b611031565b60405161022f9190614854565b6102c66104073660046140b3565b61133b565b61041f61041a366004614563565b6114b5565b60405161022f9291906146be565b6102c661043b366004614155565b611608565b610293611831565b61025e610456366004613e48565b611840565b6103ec610469366004613f29565b61186e565b6102f961047c366004613f29565b61193b565b61025e6119f1565b610222610497366004614584565b6119fa565b60045490565b6001600160a01b03821660009081526001602090815260408083208484529091529020545b92915050565b60006001600160e01b031982166336372b0760e01b14806104fe57506001600160e01b031982166301ffc9a760e01b145b8061051957506001600160e01b03198216636cdb3d1360e11b145b8061053457506001600160e01b03198216630271189760e51b145b8061054f57506001600160e01b031982166303a24d0760e21b145b90505b919050565b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d3d72d2a6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105b257600080fd5b505afa1580156105c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105ea9190613e2c565b6001600160a01b0316630e89341c836040518263ffffffff1660e01b81526004016106159190614cad565b60006040518083038186803b15801561062d57600080fd5b505afa158015610641573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261054f919081019061449d565b7f000000000000000000000000000000000000000000000000000000000000000090565b6001600160a01b0316600090815260086020526040902054612710900490565b60608181816001600160401b03811180156106c757600080fd5b506040519080825280602002602001820160405280156106f1578160200160208202803683370190505b50905060005b82811015610744576007600087878481811061070f57fe5b9050602002013581526020019081526020016000205482828151811061073157fe5b60209081029190910101526001016106f7565b50949350505050565b60608181816001600160401b038111801561076757600080fd5b50604051908082528060200260200182016040528015610791578160200160208202803683370190505b50905060005b8281101561074457600660008787848181106107af57fe5b905060200201358152602001908152602001600020548282815181106107d157fe5b6020908102919091010152600101610797565b336001600160a01b038616148061080057506108008533611840565b61083b5760405162461bcd60e51b815260040180806020018281038252602f815260200180614ef9602f913960400191505060405180910390fd5b6001600160a01b0384166108805760405162461bcd60e51b8152600401808060200182810382526030815260200180614e6d6030913960400191505060405180910390fd5b61088c85858585611a7d565b61089a858585855a86611d18565b5050505050565b7f00000000000000000000000000000000000000000000000000000000000000006103e80390565b6001600160a01b031660009081526008602052604090205490565b7f000000000000000000000000000000000000000000000000000000000000000090565b6060815183511461094a5760405162461bcd60e51b815260040180806020018281038252602c815260200180614ecd602c913960400191505060405180910390fd5b606083516001600160401b038111801561096357600080fd5b5060405190808252806020026020018201604052801561098d578160200160208202803683370190505b50905060005b8451811015610a1857600160008683815181106109ac57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060008583815181106109e257fe5b6020026020010151815260200190815260200160002054828281518110610a0557fe5b6020908102919091010152600101610993565b509392505050565b60008083118015610a315750600082115b610a4d5760405162461bcd60e51b8152600401610211906149fe565b6000610a79857f0000000000000000000000000000000000000000000000000000000000000000611f10565b90506000610a878285611f10565b90506000610aa183610a9b886103e8611f10565b90611f7f565b9050808281610aac57fe5b04979650505050505050565b60608381816001600160401b0381118015610ad257600080fd5b50604051908082528060200260200182016040528015610afc578160200160208202803683370190505b50905060005b82811015610c325760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031662fdd58e308b8b86818110610b4757fe5b905060200201356040518363ffffffff1660e01b8152600401610b6b9291906146be565b60206040518083038186803b158015610b8357600080fd5b505afa158015610b97573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bbb9190614527565b9050610c12898984818110610bcc57fe5b90506020020135888885818110610bdf57fe5b9050602002013583600760008e8e89818110610bf757fe5b90506020020135815260200190815260200160002054610e2e565b838381518110610c1e57fe5b602090810291909101015250600101610b02565b509695505050505050565b610c45610ceb565b6001600160a01b0316336001600160a01b031614610c755760405162461bcd60e51b8152600401610211906148bd565b6001600160a01b038116610c9b5760405162461bcd60e51b8152600401610211906148fc565b600380546001600160a01b0319166001600160a01b0383811691821792839055604051919216907ffca4bc097843727c93a8ab8d241a38481cccf8ec96dda1a342574712cfe8eb4090600090a350565b60035460408051631127a41d60e31b815290516000926001600160a01b03169163893d20e8916004808301926020929190829003018186803b158015610d3057600080fd5b505afa158015610d44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d689190613e2c565b905090565b3360008181526002602090815260408083206001600160a01b03871680855290835292819020805460ff1916861515908117909155815190815290519293927f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31929181900390910190a35050565b6001600160a01b0381166000908152600860205260409020805461271080820690925504610e2a7f00000000000000000000000000000000000000000000000000000000000000008383611fd2565b5050565b600080610e3c858585610a20565b90506000610e4a87836114b5565b9150610e589050828261211f565b979650505050505050565b7f000000000000000000000000000000000000000000000000000000000000000090565b600080610e958585856119fa565b90506000610ea387836114b5565b9150610e5890508282611f7f565b610eb9610ceb565b6001600160a01b0316336001600160a01b031614610ee95760405162461bcd60e51b8152600401610211906148bd565b6040516301ffc9a760e01b81526000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906301ffc9a790610f3f9063152a902d60e11b90600401614854565b60206040518083038186803b158015610f5757600080fd5b505afa158015610f6b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f8f9190614220565b90508015610faf5760405162461bcd60e51b815260040161021190614ae2565b6109c4831115610fd15760405162461bcd60e51b815260040161021190614c8c565b6004839055600580546001600160a01b0319166001600160a01b0384169081179091556040517f02365318429bf1d603e8383b62068288a077545c5c9e709201d563b3f56ce2b390611024908690614cad565b60405180910390a2505050565b600080828060200190518101906110489190614258565b90506001600160e01b031981166356f3ce3d60e11b141561119857336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146110ab5760405162461bcd60e51b815260040161021190614b24565b6110b3613b36565b838060200190518101906110c791906143c7565b8051909250600091506001600160a01b0316156110e55781516110e7565b875b9050816060015151826040015151146111125760405162461bcd60e51b815260040161021190614ac1565b6060611133888885602001518660800151868860400151896060015161217c565b9050816001600160a01b0316896001600160a01b03167fb57378559821141c0e7ae964206b7523234d19e5783ade99b3d665eee495c9978a8a85886040015189606001516040516111889594939291906147dc565b60405180910390a3505050611328565b6001600160e01b031981166382da2b7360e01b141561123657336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146111f95760405162461bcd60e51b815260040161021190614aa0565b611201613b6e565b838060200190518101906112159190614274565b9050809150506112308787878460000151856020015161248c565b50611328565b6001600160e01b03198116635c0bf25960e01b14156112aa5733301461126e5760405162461bcd60e51b815260040161021190614c6b565b611276613b88565b8380602001905181019061128a919061430c565b9050809150506112308787878460000151856020015186604001516128c5565b6001600160e01b0319811663c8c323f960e01b141561131057336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461130b5760405162461bcd60e51b8152600401610211906149bc565b611328565b60405162461bcd60e51b815260040161021190614b03565b5063bc197c8160e01b9695505050505050565b60608381816001600160401b038111801561135557600080fd5b5060405190808252806020026020018201604052801561137f578160200160208202803683370190505b50905060005b82811015610c325760007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031662fdd58e308b8b868181106113ca57fe5b905060200201356040518363ffffffff1660e01b81526004016113ee9291906146be565b60206040518083038186803b15801561140657600080fd5b505afa15801561141a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143e9190614527565b905061149589898481811061144f57fe5b9050602002013588888581811061146257fe5b90506020020135600760008d8d8881811061147957fe5b9050602002013581526020019081526020016000205484610e87565b8383815181106114a157fe5b602090810291909101015250600101611385565b6000807f0000000000000000000000000000000000000000000000000000000000000000156115e05760405163152a902d60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632a55205a9061152c9087908790600401614cb6565b604080518083038186803b15801561154357600080fd5b505afa925050508015611573575060408051601f3d908101601f1916820190925261157091810190613fe7565b60015b6115a9576005546004546001600160a01b039091169061271090611598908690611f10565b8161159f57fe5b0491509150611601565b60006127106115ba876109c4611f10565b816115c157fe5b049050828183116115d257826115d4565b815b94509450505050611601565b6005546004546001600160a01b039091169061271090611598908690611f10565b9250929050565b60604285101561162a5760405162461bcd60e51b8152600401610211906148db565b600088511161164b5760405162461bcd60e51b815260040161021190614a5e565b6116777f0000000000000000000000000000000000000000000000000000000000000000333089612d32565b60006001600160a01b0385161561168e5784611690565b335b84518451919250889181146116b75760405162461bcd60e51b815260040161021190614ba8565b60005b818110156117bd5760008682815181106116d057fe5b602002602001015111156117b5576117048682815181106116ed57fe5b60200260200101518461211f90919063ffffffff16565b925061177861173161271088848151811061171b57fe5b6020026020010151611f1090919063ffffffff16565b600860008a858151811061174157fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002054611f7f90919063ffffffff16565b6008600089848151811061178857fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055505b6001016116ba565b5060606117cd8c8c858c88612e87565b9050836001600160a01b0316336001600160a01b03167f28fb4c13c000d11e44ba9e45029f385ed02ffeae62dc5704e2c0706a4dab5ecb8e8e858c8c60405161181a9594939291906147dc565b60405180910390a39b9a5050505050505050505050565b6005546001600160a01b031690565b6001600160a01b03918216600090815260026020908152604080832093909416825291909152205460ff1690565b6040805160018082528183019092526000916060919060208083019080368337505060408051600180825281830190925292935060609291506020808301908036833701905050905085826000815181106118c557fe5b60200260200101818152505084816000815181106118df57fe5b6020026020010181815250506118f88888848488611031565b6001600160e01b03191663bc197c8160e01b146119275760405162461bcd60e51b815260040161021190614b45565b5063f23a6e6160e01b979650505050505050565b336001600160a01b038616148061195757506119578533611840565b6119925760405162461bcd60e51b815260040180806020018281038252602a815260200180614ddd602a913960400191505060405180910390fd5b6001600160a01b0384166119d75760405162461bcd60e51b815260040180806020018281038252602b815260200180614d92602b913960400191505060405180910390fd5b6119e385858585613148565b61089a858585855a86613229565b60095460ff1681565b60008083118015611a0b5750600082115b611a275760405162461bcd60e51b815260040161021190614be9565b6000611a3f6103e8611a398688611f10565b90611f10565b90506000611a717f0000000000000000000000000000000000000000000000000000000000000000611a39868961211f565b9050610c32828261339b565b8051825114611abd5760405162461bcd60e51b8152600401808060200182810382526035815260200180614e386035913960400191505060405180910390fd5b815160005b81811015611c3757611b39838281518110611ad957fe5b602002602001015160016000896001600160a01b03166001600160a01b031681526020019081526020016000206000878581518110611b1457fe5b602002602001015181526020019081526020016000205461211f90919063ffffffff16565b6001600160a01b03871660009081526001602052604081208651909190879085908110611b6257fe5b6020026020010151815260200190815260200160002081905550611beb838281518110611b8b57fe5b602002602001015160016000886001600160a01b03166001600160a01b031681526020019081526020016000206000878581518110611bc657fe5b6020026020010151815260200190815260200160002054611f7f90919063ffffffff16565b6001600160a01b03861660009081526001602052604081208651909190879085908110611c1457fe5b602090810291909101810151825281019190915260400160002055600101611ac2565b50836001600160a01b0316856001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8686604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015611cbd578181015183820152602001611ca5565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015611cfc578181015183820152602001611ce4565b5050505090500194505050505060405180910390a45050505050565b611d2a856001600160a01b03166133e1565b15611f08576000856001600160a01b031663bc197c8184338a8989886040518763ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b03168152602001806020018060200180602001848103845287818151815260200191508051906020019060200280838360005b83811015611dbb578181015183820152602001611da3565b50505050905001848103835286818151815260200191508051906020019060200280838360005b83811015611dfa578181015183820152602001611de2565b50505050905001848103825285818151815260200191508051906020019080838360005b83811015611e36578181015183820152602001611e1e565b50505050905090810190601f168015611e635780820380516001836020036101000a031916815260200191505b5098505050505050505050602060405180830381600088803b158015611e8857600080fd5b5087f1158015611e9c573d6000803e3d6000fd5b50505050506040513d6020811015611eb357600080fd5b505190506001600160e01b0319811663bc197c8160e01b14611f065760405162461bcd60e51b815260040180806020018281038252603f815260200180614f58603f913960400191505060405180910390fd5b505b505050505050565b600082611f1f575060006104c7565b82820282848281611f2c57fe5b0414611f78576040805162461bcd60e51b8152602060048201526016602482015275536166654d617468236d756c3a204f564552464c4f5760501b604482015290519081900360640190fd5b9392505050565b600082820183811015611f78576040805162461bcd60e51b8152602060048201526016602482015275536166654d617468236164643a204f564552464c4f5760501b604482015290519081900360640190fd5b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b178152925182516000946060949389169392918291908083835b6020831061204f5780518252601f199092019160209182019101612030565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d80600081146120b1576040519150601f19603f3d011682016040523d82523d6000602084013e6120b6565b606091505b50915091508180156120e45750805115806120e457508080602001905160208110156120e157600080fd5b50515b61089a5760405162461bcd60e51b815260040180806020018281038252602d815260200180614fd1602d913960400191505060405180910390fd5b600082821115612176576040805162461bcd60e51b815260206004820152601960248201527f536166654d617468237375623a20554e444552464c4f57232100000000000000604482015290519081900360640190fd5b50900390565b6060600260005414156121c4576040805162461bcd60e51b815260206004820152601f6024820152600080516020614dbd833981519152604482015290519081900360640190fd5b60026000558751428610156121eb5760405162461bcd60e51b815260040161021190614a3e565b6000816001600160401b038111801561220357600080fd5b5060405190808252806020026020018201604052801561222d578160200160208202803683370190505b509250606061223b8b613418565b905060005b838110156123995760008c828151811061225657fe5b6020026020010151905060008c838151811061226e57fe5b60200260200101519050600084848151811061228657fe5b60200260200101519050600082116122b05760405162461bcd60e51b815260040161021190614bc9565b600083815260076020526040812054906122d4846122ce858261211f565b84610a20565b90506000806122e387846114b5565b909250905080156123365761231c6122fd82612710611f10565b6001600160a01b03841660009081526008602052604090205490611f7f565b6001600160a01b0383166000908152600860205260409020555b61234a612343848361211f565b8b90611f7f565b9950612356848461211f565b60008881526007602052604090205561236f838261211f565b8c898151811061237b57fe5b60209081029190910101525050600190950194506122409350505050565b5060005b855181101561242d5760008682815181106123b457fe5b60200260200101511115612425576123d18682815181106116ed57fe5b92506123e861173161271088848151811061171b57fe5b600860008984815181106123f857fe5b60200260200101516001600160a01b03166001600160a01b03168152602001908152602001600020819055505b60010161239d565b508882101561244e5760405162461bcd60e51b815260040161021190614a1e565b6124797f00000000000000000000000000000000000000000000000000000000000000008884611fd2565b5050506001600055979650505050505050565b600260005414156124d2576040805162461bcd60e51b815260206004820152601f6024820152600080516020614dbd833981519152604482015290519081900360640190fd5b6002600055428110156124f75760405162461bcd60e51b815260040161021190614c4a565b835160006060826001600160401b038111801561251357600080fd5b5060405190808252806020026020018201604052801561253d578160200160208202803683370190505b5090506060836001600160401b038111801561255857600080fd5b50604051908082528060200260200182016040528015612582578160200160208202803683370190505b509050606061259089613418565b905060005b858110156128275760008a82815181106125ab57fe5b6020026020010151905060008a83815181106125c357fe5b6020026020010151905060008a84815181106125db57fe5b6020026020010151116126005760405162461bcd60e51b815260040161021190614b66565b600081116126205760405162461bcd60e51b815260040161021190614c29565b600082815260066020526040902054801561277c57600083815260076020526040812054865190919087908790811061265557fe5b602002602001015190506000806126876126788588611f1090919063ffffffff16565b612682858961211f565b61339b565b91509150818f898151811061269857fe5b602002602001015110156126be5760405162461bcd60e51b815260040161021190614918565b6126c88483611f7f565b6000888152600760205260409020556126e18c83611f7f565b9b508361270486611a39846126f75760006126fa565b60015b869060ff1661211f565b8161270b57fe5b048b898151811061271857fe5b602002602001018181525050818a898151811061273157fe5b6020026020010181815250506127638b898151811061274c57fe5b602002602001015186611f7f90919063ffffffff16565b6000888152600660205260409020555061281c92505050565b60008b858151811061278a57fe5b602002602001015190506103e88110156127b65760405162461bcd60e51b81526004016102119061499b565b60008481526007602052604090208190556127d18982611f7f565b6000858152600660205260409020829055885190995081908990879081106127f557fe5b6020026020010181815250508087868151811061280e57fe5b602002602001018181525050505b505050600101612595565b506128547f00000000000000000000000000000000000000000000000000000000000000008b3087612d32565b61286f8a8a85604051806020016040528060008152506136b1565b896001600160a01b03167f403f9dc4582dae52d3eeb4a22d37540ffb13c32d964c92ec5ac0d3d5628da3168a8a856040516128ac93929190614799565b60405180910390a2505060016000555050505050505050565b6002600054141561290b576040805162461bcd60e51b815260206004820152601f6024820152600080516020614dbd833981519152604482015290519081900360640190fd5b600260005542811161292f5760405162461bcd60e51b815260040161021190614959565b845160006060826001600160401b038111801561294b57600080fd5b50604051908082528060200260200182016040528015612975578160200160208202803683370190505b5090506060836001600160401b038111801561299057600080fd5b506040519080825280602002602001820160405280156129ca57816020015b6129b7613ba9565b8152602001906001900390816129af5790505b50905060606129d88a613418565b905060005b85811015612c205760008b82815181106129f357fe5b6020026020010151905060008b8381518110612a0b57fe5b6020026020010151905060006006600084815260200190815260200160002054905060008111612a4d5760405162461bcd60e51b81526004016102119061497a565b600083815260076020526040812054865190919081908190899089908110612a7157fe5b60200260200101519050600080600080612a8e8b8b878b8d61387e565b6001600160a01b038216600090815260086020526040902054959c50939a50919750955093509150612ac09082611f7f565b6001600160a01b0383166000908152600860205260409020558d5184908f908e908110612ae957fe5b60200260200101516020018181525050828e8d81518110612b0657fe5b60200260200101516040018181525050888e8d81518110612b2357fe5b6020026020010151606001818152505050505050508e8781518110612b4457fe5b6020026020010151821015612b6b5760405162461bcd60e51b815260040161021190614b87565b8d8781518110612b7757fe5b6020026020010151811015612b9e5760405162461bcd60e51b81526004016102119061489c565b612ba8848661211f565b600087815260066020526040902055612bc1838361211f565b600087815260076020526040902055612bda8b83611f7f565b9a50808a8881518110612be957fe5b60200260200101818152505081898881518110612c0257fe5b6020908102919091010151525050600190940193506129dd92505050565b50612c2c308b8b613973565b612c577f00000000000000000000000000000000000000000000000000000000000000008c86611fd2565b604051631759616b60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632eb2c2d690612ca99030908f908f908990600401614666565b600060405180830381600087803b158015612cc357600080fd5b505af1158015612cd7573d6000803e3d6000fd5b505050508a6001600160a01b03167f3024a3223ce9e6a0b0324a52224694f329e7f092c1a7b74067b8f3cbfa1885718b8585604051612d189392919061470f565b60405180910390a250506001600055505050505050505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b17815292518251600094606094938a169392918291908083835b60208310612db75780518252601f199092019160209182019101612d98565b6001836020036101000a0380198251168184511680821785525050505050509050019150506000604051808303816000865af19150503d8060008114612e19576040519150601f19603f3d011682016040523d82523d6000602084013e612e1e565b606091505b5091509150818015612e4c575080511580612e4c5750808060200190516020811015612e4957600080fd5b50515b611f085760405162461bcd60e51b8152600401808060200182810382526031815260200180614e076031913960400191505060405180910390fd5b606060026000541415612ecf576040805162461bcd60e51b815260206004820152601f6024820152600080516020614dbd833981519152604482015290519081900360640190fd5b600260005542831015612ef45760405162461bcd60e51b815260040161021190614c09565b855184816001600160401b0381118015612f0d57600080fd5b50604051908082528060200260200182016040528015612f37578160200160208202803683370190505b5092506060612f4589613418565b905060005b8381101561307e5760008a8281518110612f6057fe5b6020026020010151905060008a8381518110612f7857fe5b602002602001015190506000848481518110612f9057fe5b6020026020010151905060008211612fba5760405162461bcd60e51b815260040161021190614939565b60008381526007602052604081205490612fd58483856119fa565b9050600080612fe487846114b5565b9092509050801561301857612ffe6122fd82612710611f10565b6001600160a01b0383166000908152600860205260409020555b61302c816130268c8661211f565b9061211f565b99506130388382611f7f565b8c898151811061304457fe5b60209081029190910101526130598484611f7f565b60009788526007602052604090972096909655505060019094019350612f4a92505050565b50604051631759616b60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690632eb2c2d6906130d190309089908e908e90600401614666565b600060405180830381600087803b1580156130eb57600080fd5b505af11580156130ff573d6000803e3d6000fd5b505050506000821115613137576131377f00000000000000000000000000000000000000000000000000000000000000008684611fd2565b505050600160005595945050505050565b6001600160a01b0384166000908152600160209081526040808320858452909152902054613176908261211f565b6001600160a01b0380861660009081526001602081815260408084208885528252808420959095559287168252825282812085825290915220546131ba9082611f7f565b6001600160a01b038085166000818152600160209081526040808320888452825291829020949094558051868152938401859052805191939288169233927fc3d58168c5ae7397731d063d5bbf3d657854427343f4c083240f7aacaa2d0f62929181900390910190a450505050565b61323b856001600160a01b03166133e1565b15611f08576000856001600160a01b031663f23a6e6184338a8989886040518763ffffffff1660e01b815260040180866001600160a01b03168152602001856001600160a01b0316815260200184815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b838110156132cd5781810151838201526020016132b5565b50505050905090810190601f1680156132fa5780820380516001836020036101000a031916815260200191505b509650505050505050602060405180830381600088803b15801561331d57600080fd5b5087f1158015613331573d6000803e3d6000fd5b50505050506040513d602081101561334857600080fd5b505190506001600160e01b0319811663f23a6e6160e01b14611f065760405162461bcd60e51b815260040180806020018281038252603a815260200180614f97603a913960400191505060405180910390fd5b6000808284816133a757fe5b06156133c9576133c260018486816133bb57fe5b0490611f7f565b60016133d6565b8284816133d257fe5b0460005b915091509250929050565b6000813f8015801590611f7857507fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470141592915050565b8051606090600181141561351a57604080516001808252818301909252606091602080830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031662fdd58e308660008151811061348457fe5b60200260200101516040518363ffffffff1660e01b81526004016134a99291906146be565b60206040518083038186803b1580156134c157600080fd5b505afa1580156134d5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134f99190614527565b8160008151811061350657fe5b602090810291909101015291506105529050565b6060816001600160401b038111801561353257600080fd5b5060405190808252806020026020018201604052801561355c578160200160208202803683370190505b509050308160008151811061356d57fe5b6001600160a01b039092166020928302919091019091015260015b828110156136055784818151811061359c57fe5b60200260200101518560018303815181106135b357fe5b6020026020010151106135d85760405162461bcd60e51b815260040161021190614a7f565b308282815181106135e557fe5b6001600160a01b0390921660209283029190910190910152600101613588565b506040516313849cfd60e21b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690634e1273f49061365490849088906004016146d7565b60006040518083038186803b15801561366c57600080fd5b505afa158015613680573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136a8919081019061411b565b92505050610552565b81518351146136f15760405162461bcd60e51b8152600401808060200182810382526030815260200180614f286030913960400191505060405180910390fd5b825160005b818110156137945761374884828151811061370d57fe5b602002602001015160016000896001600160a01b03166001600160a01b031681526020019081526020016000206000888581518110611bc657fe5b6001600160a01b0387166000908152600160205260408120875190919088908590811061377157fe5b6020908102919091018101518252810191909152604001600020556001016136f6565b50846001600160a01b031660006001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8787604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b8381101561381b578181015183820152602001613803565b50505050905001838103825284818151815260200191508051906020019060200280838360005b8381101561385a578181015183820152602001613842565b5050505090500194505050505060405180910390a461089a60008686865a87611d18565b60008080808080806138908b8a611f10565b9050600061389e8c8c611f10565b90508881816138a957fe5b069550851561394c5760006138d48a611a398c85816138c457fe5b048f61211f90919063ffffffff16565b905060006138e88b611a398d87816138c457fe5b905080158015906138f857508115155b1561394957613908888383610a20565b96506139148f886114b5565b9096509450613923878661211f565b965061392f8488611f7f565b93508a61393e86612710611f10565b8161394557fe5b0494505b50505b88828161395557fe5b04975088818161396157fe5b04965050509550955095509550955095565b8151815181146139b45760405162461bcd60e51b8152600401808060200182810382526030815260200180614e9d6030913960400191505060405180910390fd5b60005b81811015613a5557613a098382815181106139ce57fe5b602002602001015160016000886001600160a01b03166001600160a01b031681526020019081526020016000206000878581518110611b1457fe5b6001600160a01b03861660009081526001602052604081208651909190879085908110613a3257fe5b6020908102919091018101518252810191909152604001600020556001016139b7565b5060006001600160a01b0316846001600160a01b0316336001600160a01b03167f4a39dc06d4c0dbc64b70af90fd698a233a518aa5d07e595d983b8c0526c8f7fb8686604051808060200180602001838103835285818151815260200191508051906020019060200280838360005b83811015613adc578181015183820152602001613ac4565b50505050905001838103825284818151815260200191508051906020019060200280838360005b83811015613b1b578181015183820152602001613b03565b5050505090500194505050505060405180910390a450505050565b6040518060a0016040528060006001600160a01b03168152602001600081526020016060815260200160608152602001600081525090565b604051806040016040528060608152602001600081525090565b60405180606001604052806060815260200160608152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b803561055281614d55565b805161055281614d55565b600082601f830112613bf7578081fd5b8135613c0a613c0582614ce7565b614cc4565b818152915060208083019084810181840286018201871015613c2b57600080fd5b60005b84811015613c53578135613c4181614d55565b84529282019290820190600101613c2e565b505050505092915050565b600082601f830112613c6e578081fd5b8151613c7c613c0582614ce7565b818152915060208083019084810181840286018201871015613c9d57600080fd5b60005b84811015613c53578151613cb381614d55565b84529282019290820190600101613ca0565b60008083601f840112613cd6578182fd5b5081356001600160401b03811115613cec578182fd5b602083019150836020808302850101111561160157600080fd5b600082601f830112613d16578081fd5b8135613d24613c0582614ce7565b818152915060208083019084810181840286018201871015613d4557600080fd5b60005b84811015613c5357813584529282019290820190600101613d48565b600082601f830112613d74578081fd5b8151613d82613c0582614ce7565b818152915060208083019084810181840286018201871015613da357600080fd5b60005b84811015613c5357815184529282019290820190600101613da6565b600082601f830112613dd2578081fd5b8135613de0613c0582614d04565b9150808252836020828501011115613df757600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215613e21578081fd5b8135611f7881614d55565b600060208284031215613e3d578081fd5b8151611f7881614d55565b60008060408385031215613e5a578081fd5b8235613e6581614d55565b91506020830135613e7581614d55565b809150509250929050565b600080600080600060a08688031215613e97578081fd5b8535613ea281614d55565b94506020860135613eb281614d55565b935060408601356001600160401b0380821115613ecd578283fd5b613ed989838a01613d06565b94506060880135915080821115613eee578283fd5b613efa89838a01613d06565b93506080880135915080821115613f0f578283fd5b50613f1c88828901613dc2565b9150509295509295909350565b600080600080600060a08688031215613f40578283fd5b8535613f4b81614d55565b94506020860135613f5b81614d55565b9350604086013592506060860135915060808601356001600160401b03811115613f83578182fd5b613f1c88828901613dc2565b60008060408385031215613fa1578182fd5b8235613fac81614d55565b91506020830135613e7581614d6d565b60008060408385031215613fce578182fd5b8235613fd981614d55565b946020939093013593505050565b60008060408385031215613ff9578182fd5b825161400481614d55565b6020939093015192949293505050565b60008060408385031215614026578182fd5b82356001600160401b038082111561403c578384fd5b61404886838701613be7565b9350602085013591508082111561405d578283fd5b5061406a85828601613d06565b9150509250929050565b60008060208385031215614086578182fd5b82356001600160401b0381111561409b578283fd5b6140a785828601613cc5565b90969095509350505050565b600080600080604085870312156140c8578182fd5b84356001600160401b03808211156140de578384fd5b6140ea88838901613cc5565b90965094506020870135915080821115614102578384fd5b5061410f87828801613cc5565b95989497509550505050565b60006020828403121561412c578081fd5b81516001600160401b03811115614141578182fd5b61414d84828501613d64565b949350505050565b600080600080600080600060e0888a03121561416f578485fd5b87356001600160401b0380821115614185578687fd5b6141918b838c01613d06565b985060208a01359150808211156141a6578687fd5b6141b28b838c01613d06565b975060408a0135965060608a013595506141ce60808b01613bd1565b945060a08a01359150808211156141e3578384fd5b6141ef8b838c01613be7565b935060c08a0135915080821115614204578283fd5b506142118a828b01613d06565b91505092959891949750929550565b600060208284031215614231578081fd5b8151611f7881614d6d565b60006020828403121561424d578081fd5b8135611f7881614d7b565b600060208284031215614269578081fd5b8151611f7881614d7b565b60008060408385031215614286578182fd5b825161429181614d7b565b60208401519092506001600160401b03808211156142ad578283fd5b90840190604082870312156142c0578283fd5b6040516040810181811083821117156142d557fe5b6040528251828111156142e6578485fd5b6142f288828601613d64565b825250602083015160208201528093505050509250929050565b6000806040838503121561431e578182fd5b825161432981614d7b565b60208401519092506001600160401b0380821115614345578283fd5b9084019060608287031215614358578283fd5b60405160608101818110838211171561436d57fe5b60405282518281111561437e578485fd5b61438a88828601613d64565b82525060208301518281111561439e578485fd5b6143aa88828601613d64565b602083015250604083015160408201528093505050509250929050565b600080604083850312156143d9578182fd5b82516143e481614d7b565b60208401519092506001600160401b0380821115614400578283fd5b9084019060a08287031215614413578283fd5b60405160a08101818110838211171561442857fe5b60405261443483613bdc565b815260208301516020820152604083015182811115614451578485fd5b61445d88828601613c5e565b604083015250606083015182811115614474578485fd5b61448088828601613d64565b606083015250608083015160808201528093505050509250929050565b6000602082840312156144ae578081fd5b81516001600160401b038111156144c3578182fd5b8201601f810184136144d3578182fd5b80516144e1613c0582614d04565b8181528560208385010111156144f5578384fd5b614506826020830160208601614d25565b95945050505050565b600060208284031215614520578081fd5b5035919050565b600060208284031215614538578081fd5b5051919050565b60008060408385031215614551578182fd5b823591506020830135613e7581614d55565b60008060408385031215614575578182fd5b50508035926020909101359150565b600080600060608486031215614598578081fd5b505081359360208301359350604090920135919050565b600080600080608085870312156145c4578182fd5b5050823594602084013594506040840135936060013592509050565b6000815180845260208085019450808401835b838110156146185781516001600160a01b0316875295820195908201906001016145f3565b509495945050505050565b6000815180845260208085019450808401835b8381101561461857815187529582019590820190600101614636565b6001600160a01b0391909116815260200190565b6001600160a01b0385811682528416602082015260a06040820181905260009061469290830185614623565b82810360608401526146a48185614623565b838103608090940193909352508152602001949350505050565b6001600160a01b03929092168252602082015260400190565b6000604082526146ea60408301856145e0565b82810360208401526145068185614623565b600060208252611f786020830184614623565b6000606080835261472281840187614623565b6020848203818601526147358288614623565b915060408583038187015282875180855283850191508389019450865b8181101561478957855180518452858101518685015284810151858501528701518784015294840194608090920191600101614752565b50909a9950505050505050505050565b6000606082526147ac6060830186614623565b82810360208401526147be8186614623565b905082810360408401526147d28185614623565b9695505050505050565b600060a082526147ef60a0830188614623565b82810360208401526148018188614623565b905082810360408401526148158187614623565b9050828103606084015261482981866145e0565b9050828103608084015261483d8185614623565b98975050505050505050565b901515815260200190565b6001600160e01b031991909116815260200190565b6000602082528251806020840152614888816040850160208701614d25565b601f01601f19169190910160400192915050565b60208082526007908201526609c8a64604662760cb1b604082015260600190565b6020808252600490820152632227919960e11b604082015260600190565b6020808252600790820152664e45323023313960c81b604082015260600190565b602080825260029082015261443360f01b604082015260600190565b6020808252600790820152664e45323023313360c81b604082015260600190565b60208082526006908201526513914c8c08cd60d21b604082015260600190565b6020808252600790820152664e45323023313560c81b604082015260600190565b6020808252600790820152662722991811989b60c91b604082015260600190565b60208082526007908201526613914c8c08cc4d60ca1b604082015260600190565b6020808252600790820152662722991811991b60c91b604082015260600190565b6020808252600790820152664e45323023323960c81b604082015260600190565b6020808252600690820152654e453230233960d01b604082015260600190565b60208082526006908201526509c8a646046760d31b604082015260600190565b60208082526006908201526527229918119b60d11b604082015260600190565b60208082526007908201526604e4532302332360cc1b604082015260600190565b6020808252600790820152662722991811999960c91b604082015260600190565b60208082526007908201526613914c8c08cc8d60ca1b604082015260600190565b6020808252600790820152664e45323023323360c81b604082015260600190565b60208082526007908201526604e4532302333360cc1b604082015260600190565b6020808252600790820152664e45323023323760c81b604082015260600190565b6020808252600790820152662722991811991960c91b604082015260600190565b60208082526007908201526609c8a64604664760cb1b604082015260600190565b6020808252600790820152664e45323023313160c81b604082015260600190565b6020808252600790820152664e45323023313760c81b604082015260600190565b6020808252600790820152664e45323023323160c81b604082015260600190565b6020808252600690820152654e453230233760d01b604082015260600190565b6020808252600690820152654e453230233560d01b604082015260600190565b6020808252600690820152654e453230233360d01b604082015260600190565b6020808252600790820152662722991811989960c91b604082015260600190565b60208082526007908201526604e4532302331360cc1b604082015260600190565b6020808252600790820152664e45323023323560c81b604082015260600190565b6020808252600790820152664e45323023333160c81b604082015260600190565b90815260200190565b918252602082015260400190565b6040518181016001600160401b0381118282101715614cdf57fe5b604052919050565b60006001600160401b03821115614cfa57fe5b5060209081020190565b60006001600160401b03821115614d1757fe5b50601f01601f191660200190565b60005b83811015614d40578181015183820152602001614d28565b83811115614d4f576000848401525b50505050565b6001600160a01b0381168114614d6a57600080fd5b50565b8015158114614d6a57600080fd5b6001600160e01b031981168114614d6a57600080fdfe4552433131353523736166655472616e7366657246726f6d3a20494e56414c49445f524543495049454e545265656e7472616e637947756172643a207265656e7472616e742063616c6c004552433131353523736166655472616e7366657246726f6d3a20494e56414c49445f4f50455241544f525472616e7366657248656c7065723a3a7472616e7366657246726f6d3a207472616e7366657246726f6d206661696c656445524331313535235f7361666542617463685472616e7366657246726f6d3a20494e56414c49445f4152524159535f4c454e47544845524331313535237361666542617463685472616e7366657246726f6d3a20494e56414c49445f524543495049454e54455243313135354d696e744275726e2362617463684275726e3a20494e56414c49445f4152524159535f4c454e475448455243313135352362616c616e63654f6642617463683a20494e56414c49445f41525241595f4c454e47544845524331313535237361666542617463685472616e7366657246726f6d3a20494e56414c49445f4f50455241544f52455243313135354d696e744275726e2362617463684d696e743a20494e56414c49445f4152524159535f4c454e47544845524331313535235f63616c6c6f6e45524331313535426174636852656365697665643a20494e56414c49445f4f4e5f524543454956455f4d45535341474545524331313535235f63616c6c6f6e4552433131353552656365697665643a20494e56414c49445f4f4e5f524543454956455f4d4553534147455472616e7366657248656c7065723a3a736166655472616e736665723a207472616e73666572206661696c6564a264697066735822122021a7a684b02c590bc84b35f4e93f50c715f65c1756c4f19edc30fac5bbc4a0f864736f6c63430007040033000000000000000000000000fbf1124c6de0a09f4be4d62c90d95fa7ab9eea2e000000000000000000000000b05c4cf6cf5989ca95273a2667515bd68ac8b029000000000000000000000000000000000000000000000000000000000000000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000fbf1124c6de0a09f4be4d62c90d95fa7ab9eea2e000000000000000000000000b05c4cf6cf5989ca95273a2667515bd68ac8b029000000000000000000000000000000000000000000000000000000000000000a
-----Decoded View---------------
Arg [0] : _tokenAddr (address): 0xfbf1124c6de0a09f4be4d62c90d95fa7ab9eea2e
Arg [1] : _currencyAddr (address): 0xb05c4cf6cf5989ca95273a2667515bd68ac8b029
Arg [2] : _lpFee (uint256): 10
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000fbf1124c6de0a09f4be4d62c90d95fa7ab9eea2e
Arg [1] : 000000000000000000000000b05c4cf6cf5989ca95273a2667515bd68ac8b029
Arg [2] : 000000000000000000000000000000000000000000000000000000000000000a
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.