编译器
0.6.12+commit.27d51765
文件 1 的 20:Address.sol
pragma solidity ^0.6.2;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 20:IBPool.sol
pragma solidity ^0.6.12;
interface IBPool {
function isPublicSwap() external view returns (bool);
function isFinalized() external view returns (bool);
function isBound(address t) external view returns (bool);
function getNumTokens() external view returns (uint);
function getCurrentTokens() external view returns (address[] memory tokens);
function getFinalTokens() external view returns (address[] memory tokens);
function getDenormalizedWeight(address token) external view returns (uint);
function getTotalDenormalizedWeight() external view returns (uint);
function getNormalizedWeight(address token) external view returns (uint);
function getBalance(address token) external view returns (uint);
function getSwapFee() external view returns (uint);
function getController() external view returns (address);
function setSwapFee(uint swapFee) external;
function setController(address manager) external;
function setPublicSwap(bool public_) external;
function finalize() external;
function bind(address token, uint balance, uint denorm) external;
function rebind(address token, uint balance, uint denorm) external;
function unbind(address token) external;
function gulp(address token) external;
function getSpotPrice(address tokenIn, address tokenOut) external view returns (uint spotPrice);
function getSpotPriceSansFee(address tokenIn, address tokenOut) external view returns (uint spotPrice);
function joinPool(uint poolAmountOut, uint[] calldata maxAmountsIn) external;
function exitPool(uint poolAmountIn, uint[] calldata minAmountsOut) external;
function swapExactAmountIn(
address tokenIn,
uint tokenAmountIn,
address tokenOut,
uint minAmountOut,
uint maxPrice
) external returns (uint tokenAmountOut, uint spotPriceAfter);
function swapExactAmountOut(
address tokenIn,
uint maxAmountIn,
address tokenOut,
uint tokenAmountOut,
uint maxPrice
) external returns (uint tokenAmountIn, uint spotPriceAfter);
function joinswapExternAmountIn(
address tokenIn,
uint tokenAmountIn,
uint minPoolAmountOut
) external returns (uint poolAmountOut);
function joinswapPoolAmountOut(
address tokenIn,
uint poolAmountOut,
uint maxAmountIn
) external returns (uint tokenAmountIn);
function exitswapPoolAmountIn(
address tokenOut,
uint poolAmountIn,
uint minAmountOut
) external returns (uint tokenAmountOut);
function exitswapExternAmountOut(
address tokenOut,
uint tokenAmountOut,
uint maxPoolAmountIn
) external returns (uint poolAmountIn);
function totalSupply() external view returns (uint);
function balanceOf(address whom) external view returns (uint);
function allowance(address src, address dst) external view returns (uint);
function approve(address dst, uint amt) external returns (bool);
function transfer(address dst, uint amt) external returns (bool);
function transferFrom(
address src, address dst, uint amt
) external returns (bool);
function calcSpotPrice(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint swapFee
) external returns (uint spotPrice);
function calcOutGivenIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint tokenAmountIn,
uint swapFee
) external returns (uint tokenAmountOut);
function calcInGivenOut(
uint tokenBalanceIn,
uint tokenWeightIn,
uint tokenBalanceOut,
uint tokenWeightOut,
uint tokenAmountOut,
uint swapFee
) external returns (uint tokenAmountIn);
function calcPoolOutGivenSingleIn(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint tokenAmountIn,
uint swapFee
) external returns (uint poolAmountOut);
function calcSingleInGivenPoolOut(
uint tokenBalanceIn,
uint tokenWeightIn,
uint poolSupply,
uint totalWeight,
uint poolAmountOut,
uint swapFee
) external returns (uint tokenAmountIn);
function calcSingleOutGivenPoolIn(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint poolAmountIn,
uint swapFee
) external returns (uint tokenAmountOut);
function calcPoolInGivenSingleOut(
uint tokenBalanceOut,
uint tokenWeightOut,
uint poolSupply,
uint totalWeight,
uint tokenAmountOut,
uint swapFee
) external returns (uint poolAmountIn);
}
文件 3 的 20:IDvd.sol
pragma solidity ^0.6.12;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IDvd is IERC20 {
function mint(address account, uint256 amount) external;
function burn(address account, uint256 amount) external;
function increaseShareholderPoint(address account, uint256 amount) external;
function decreaseShareholderPoint(address account, uint256 amount) external;
function shareholderPointOf(address account) external view returns (uint256);
function totalShareholderPoint() external view returns (uint256);
}
文件 4 的 20:IERC20.sol
pragma solidity ^0.6.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 5 的 20:IERC20Snapshot.sol
pragma solidity ^0.6.12;
interface IERC20Snapshot {
function balanceOfAt(address account, uint256 snapshotId) external view returns (uint256);
function totalSupplyAt(uint256 snapshotId) external view returns (uint256);
}
文件 6 的 20:IMasset.sol
pragma solidity ^0.6.12;
import { MassetStructs } from "./MassetStructs.sol";
interface IMasset is MassetStructs {
function collectInterest() external returns (uint256 massetMinted, uint256 newTotalSupply);
function mint(address _basset, uint256 _bassetQuantity)
external returns (uint256 massetMinted);
function mintTo(address _basset, uint256 _bassetQuantity, address _recipient)
external returns (uint256 massetMinted);
function mintMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantity, address _recipient)
external returns (uint256 massetMinted);
function swap( address _input, address _output, uint256 _quantity, address _recipient)
external returns (uint256 output);
function getSwapOutput( address _input, address _output, uint256 _quantity)
external view returns (bool, string memory, uint256 output);
function redeem(address _basset, uint256 _bassetQuantity)
external returns (uint256 massetRedeemed);
function redeemTo(address _basset, uint256 _bassetQuantity, address _recipient)
external returns (uint256 massetRedeemed);
function redeemMulti(address[] calldata _bAssets, uint256[] calldata _bassetQuantities, address _recipient)
external returns (uint256 massetRedeemed);
function redeemMasset(uint256 _mAssetQuantity, address _recipient) external;
function upgradeForgeValidator(address _newForgeValidator) external;
function setSwapFee(uint256 _swapFee) external;
function getBasketManager() external view returns(address);
function forgeValidator() external view returns (address);
function totalSupply() external view returns (uint256);
function swapFee() external view returns (uint256);
}
文件 7 的 20:IPool.sol
pragma solidity ^0.6.12;
interface IPool {
function openFarm() external;
function distributeBonusRewards(uint256 amount) external;
function stake(uint256 amount) external;
function stakeTo(address recipient, uint256 amount) external;
function withdraw(uint256 amount) external;
function withdrawTo(address recipient, uint256 amount) external;
function claimReward() external;
function claimRewardTo(address recipient) external;
}
文件 8 的 20:ISDvd.sol
pragma solidity ^0.6.12;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface ISDvd is IERC20 {
function mint(address account, uint256 amount) external;
function burn(address account, uint256 amount) external;
function setMinter(address account, bool value) external;
function setNoFeeAddress(address account, bool value) external;
function setPairAddress(address _pairAddress) external;
function snapshot() external returns (uint256);
function syncPairTokenTotalSupply() external returns (bool isPairTokenBurned);
}
文件 9 的 20:ITreasury.sol
pragma solidity ^0.6.12;
interface ITreasury {
function release() external;
}
文件 10 的 20:IUniswapV2Factory.sol
pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function migrator() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
function setMigrator(address) external;
}
文件 11 的 20:IUniswapV2Router01.sol
pragma solidity >=0.6.2;
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
external
payable
returns (uint[] memory amounts);
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
}
文件 12 的 20:IUniswapV2Router02.sol
pragma solidity >=0.6.2;
import './IUniswapV2Router01.sol';
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax, uint8 v, bytes32 r, bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
文件 13 的 20:IVault.sol
pragma solidity ^0.6.12;
interface IVault {
function savingsContract() external view returns (address);
function musd() external view returns (address);
function deposit(uint256) external;
function redeem(uint256) external;
function getBalance() external view returns (uint256);
}
文件 14 的 20:IWETH.sol
pragma solidity >=0.5.0;
interface IWETH {
function deposit() external payable;
function transfer(address to, uint value) external returns (bool);
function withdraw(uint) external;
}
文件 15 的 20:LordOfCoin.sol
pragma solidity ^0.6.12;
import '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import '@openzeppelin/contracts/token/ERC20/SafeERC20.sol';
import '@openzeppelin/contracts/math/SafeMath.sol';
import '@openzeppelin/contracts/utils/ReentrancyGuard.sol';
import "./uniswapv2/interfaces/IUniswapV2Factory.sol";
import "./uniswapv2/interfaces/IUniswapV2Router02.sol";
import "./uniswapv2/interfaces/IWETH.sol";
import './interfaces/IERC20Snapshot.sol';
import './interfaces/ITreasury.sol';
import './interfaces/IVault.sol';
import './interfaces/IMasset.sol';
import './interfaces/IDvd.sol';
import './interfaces/ISDvd.sol';
import './interfaces/IPool.sol';
import './interfaces/IBPool.sol';
import './utils/MathUtils.sol';
contract LordOfCoin is ReentrancyGuard {
using SafeMath for uint256;
using MathUtils for uint256;
using SafeERC20 for IERC20;
event Bought(address indexed sender, address indexed recipient, uint256 musdAmount, uint256 dvdReceived);
event Sold(address indexed sender, address indexed recipient, uint256 dvdAmount, uint256 musdReceived);
event SoldToETH(address indexed sender, address indexed recipient, uint256 dvdAmount, uint256 ethReceived);
event DividendClaimed(address indexed recipient, uint256 musdReceived);
event DividendClaimedETH(address indexed recipient, uint256 ethReceived);
event Received(address indexed from, uint256 amount);
uint256 public constant CURVE_TAX_DENOMINATOR = 10;
uint256 public constant BUY_TAX_DENOMINATOR = 20;
uint256 public constant SELL_TAX_DENOMINATOR = 10;
uint256 public constant DIVIDER = 1000000;
address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
IUniswapV2Router02 uniswapRouter;
address weth;
address balancerPool;
address musd;
address public dvd;
address public sdvd;
address public sdvdEthPairAddress;
address public sdvdEthPool;
address public dvdPool;
address public devTreasury;
address public poolTreasury;
address public tradingTreasury;
uint256 public totalDividendClaimed;
uint256 public totalReserve;
address public vault;
bool public isMarketOpen = false;
uint256 public marketOpenTime;
uint256 public snapshotId;
uint256 public snapshotTime;
uint256 public SNAPSHOT_DURATION = 1 weeks;
mapping(uint256 => uint256) private _totalProfitSnapshots;
mapping(uint256 => uint256) private _dividendPayingSDVDSupplySnapshots;
mapping(address => mapping(uint256 => bool)) private _isDividendClaimedSnapshots;
receive() external payable {
emit Received(msg.sender, msg.value);
}
constructor(
address _vault,
address _uniswapRouter,
address _balancerPool,
address _dvd,
address _sdvd,
address _sdvdEthPool,
address _dvdPool,
address _devTreasury,
address _poolTreasury,
address _tradingTreasury,
uint256 _marketOpenTime
) public {
vault = _vault;
musd = IVault(vault).musd();
_approveMax(musd, vault);
uniswapRouter = IUniswapV2Router02(_uniswapRouter);
balancerPool = _balancerPool;
weth = uniswapRouter.WETH();
_approveMax(musd, balancerPool);
_approveMax(weth, balancerPool);
_approveMax(musd, address(this));
dvd = _dvd;
sdvd = _sdvd;
sdvdEthPool = _sdvdEthPool;
dvdPool = _dvdPool;
devTreasury = _devTreasury;
poolTreasury = _poolTreasury;
tradingTreasury = _tradingTreasury;
sdvdEthPairAddress = IUniswapV2Factory(uniswapRouter.factory()).createPair(sdvd, weth);
marketOpenTime = _marketOpenTime;
snapshotTime = _marketOpenTime;
}
modifier marketOpen() {
require(isMarketOpen, 'Market not open');
_;
}
modifier onlyTradingTreasury() {
require(msg.sender == tradingTreasury, 'Only treasury');
_;
}
function depositTradingProfit(uint256 amount) external onlyTradingTreasury {
IVault(vault).deposit(amount);
}
function buy(uint256 musdAmount) external nonReentrant returns (uint256 recipientDVD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
return _buy(msg.sender, msg.sender, musdAmount);
}
function buyTo(address recipient, uint256 musdAmount) external nonReentrant returns (uint256 recipientDVD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
return _buy(msg.sender, recipient, musdAmount);
}
function buyFromETH() payable external nonReentrant returns (uint256 recipientDVD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
return _buy(address(this), msg.sender, _swapETHToMUSD(address(this), msg.value));
}
function sell(uint256 dvdAmount) external nonReentrant marketOpen returns (uint256 returnedMUSD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
return _sell(msg.sender, msg.sender, dvdAmount);
}
function sellTo(address recipient, uint256 dvdAmount) external nonReentrant marketOpen returns (uint256 returnedMUSD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
return _sell(msg.sender, recipient, dvdAmount);
}
function sellToETH(uint256 dvdAmount) external nonReentrant marketOpen returns (uint256 returnedETH, uint256 returnedMUSD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
(returnedMUSD, marketTax, curveTax, taxedDVD) = _sell(msg.sender, address(this), dvdAmount);
returnedETH = _swapMUSDToETH(msg.sender, returnedMUSD);
emit SoldToETH(msg.sender, msg.sender, dvdAmount, returnedETH);
}
function claimDividend() external nonReentrant marketOpen returns (uint256 dividend) {
return _claimDividend(msg.sender, msg.sender);
}
function claimDividendTo(address recipient) external nonReentrant marketOpen returns (uint256 dividend) {
return _claimDividend(msg.sender, recipient);
}
function claimDividendETH() external nonReentrant marketOpen returns (uint256 dividend, uint256 receivedETH) {
dividend = _claimDividend(msg.sender, address(this));
receivedETH = _swapMUSDToETH(msg.sender, dividend);
emit DividendClaimedETH(msg.sender, receivedETH);
}
function checkSnapshot() public {
if (isMarketOpen) {
if (snapshotTime.add(SNAPSHOT_DURATION) <= block.timestamp) {
snapshotTime = block.timestamp;
snapshotId = ISDvd(sdvd).snapshot();
_totalProfitSnapshots[snapshotId] = totalProfit();
_dividendPayingSDVDSupplySnapshots[snapshotId] = dividendPayingSDVDSupply();
}
if (snapshotId > 0 && _totalProfitSnapshots[snapshotId] == 0) {
_totalProfitSnapshots[snapshotId] = totalProfit();
}
}
}
function releaseTreasury() public {
if (isMarketOpen) {
ITreasury(devTreasury).release();
ITreasury(poolTreasury).release();
ITreasury(tradingTreasury).release();
}
}
function claimableDividend(address account) public view returns (uint256 dividend) {
if (snapshotId == 0 || isDividendClaimedAt(account, snapshotId)) {
return 0;
}
uint256 sdvdBalance = IERC20Snapshot(sdvd).balanceOfAt(account, snapshotId);
if (sdvdBalance == 0) {
return 0;
}
dividend = sdvdBalance
.mul(claimableProfitAt(snapshotId))
.div(dividendPayingSDVDSupplyAt(snapshotId));
}
function totalLockedReserve() external view returns (uint256) {
return _calculateReserveFromSupply(dvdBurnedAmount());
}
function claimableProfit() public view returns (uint256) {
return totalProfit().div(2);
}
function claimableProfitAt(uint256 _snapshotId) public view returns (uint256) {
return totalProfitAt(_snapshotId).div(2);
}
function totalProfit() public view returns (uint256) {
uint256 vaultBalance = IVault(vault).getBalance();
if (vaultBalance < totalReserve) {
vaultBalance = totalReserve;
}
return vaultBalance.sub(totalReserve);
}
function totalProfitAt(uint256 _snapshotId) public view returns (uint256) {
return _totalProfitSnapshots[_snapshotId];
}
function isDividendClaimedAt(address account, uint256 _snapshotId) public view returns (bool) {
return _isDividendClaimedSnapshots[account][_snapshotId];
}
function dvdTotalSupply() public view returns (uint256) {
return IERC20(dvd).totalSupply();
}
function dvdBurnedAmount() public view returns (uint256) {
return IERC20(dvd).balanceOf(BURN_ADDRESS);
}
function dvdPrice() external view returns (uint256) {
return dvdTotalSupply().roundedDiv(DIVIDER);
}
function dvdPriceFloor() external view returns (uint256) {
return dvdBurnedAmount().roundedDiv(DIVIDER);
}
function dividendPayingSDVDSupply() public view returns (uint256) {
return IERC20(sdvd).totalSupply()
.sub(IERC20(sdvd).balanceOf(sdvdEthPairAddress))
.sub(IERC20(sdvd).balanceOf(sdvdEthPool))
.sub(IERC20(sdvd).balanceOf(dvdPool))
.sub(IERC20(sdvd).balanceOf(poolTreasury))
.sub(IERC20(sdvd).balanceOf(devTreasury))
.sub(IERC20(sdvd).balanceOf(tradingTreasury));
}
function dividendPayingSDVDSupplyAt(uint256 _snapshotId) public view returns (uint256) {
return _dividendPayingSDVDSupplySnapshots[_snapshotId];
}
function reserveToDVDTaxed(uint256 reserveAmount) external view returns (uint256) {
if (reserveAmount == 0) {
return 0;
}
uint256 tax = reserveAmount.div(CURVE_TAX_DENOMINATOR);
uint256 totalDVD = reserveToDVD(reserveAmount);
uint256 taxedDVD = reserveToDVD(tax);
return totalDVD.sub(taxedDVD);
}
function dvdToReserveTaxed(uint256 tokenAmount) external view returns (uint256) {
if (tokenAmount == 0) {
return 0;
}
uint256 reserveAmount = dvdToReserve(tokenAmount);
uint256 tax = reserveAmount.div(CURVE_TAX_DENOMINATOR);
return reserveAmount.sub(tax);
}
function reserveToDVD(uint256 reserveAmount) public view returns (uint256) {
return _calculateReserveToDVD(reserveAmount, totalReserve, dvdTotalSupply());
}
function dvdToReserve(uint256 tokenAmount) public view returns (uint256) {
return _calculateDVDToReserve(tokenAmount, dvdTotalSupply(), totalReserve);
}
function _checkOpenMarket() internal {
require(marketOpenTime <= block.timestamp, 'Market not open');
if (!isMarketOpen) {
isMarketOpen = true;
}
}
function _buy(address sender, address recipient, uint256 musdAmount) internal returns (uint256 returnedDVD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
_checkOpenMarket();
checkSnapshot();
releaseTreasury();
require(musdAmount > 0, 'Cannot buy 0');
marketTax = musdAmount.div(BUY_TAX_DENOMINATOR);
uint256 inAmount = musdAmount.sub(marketTax);
curveTax = inAmount.div(CURVE_TAX_DENOMINATOR);
uint256 totalDVD = reserveToDVD(inAmount);
taxedDVD = reserveToDVD(curveTax);
returnedDVD = totalDVD.sub(taxedDVD);
IERC20(musd).safeTransferFrom(sender, address(this), musdAmount);
IVault(vault).deposit(musdAmount);
totalReserve = totalReserve.add(inAmount);
IDvd(dvd).mint(BURN_ADDRESS, taxedDVD);
IDvd(dvd).mint(recipient, returnedDVD);
IDvd(dvd).increaseShareholderPoint(recipient, returnedDVD);
emit Bought(sender, recipient, musdAmount, returnedDVD);
}
function _sell(address sender, address recipient, uint256 dvdAmount) internal returns (uint256 returnedMUSD, uint256 marketTax, uint256 curveTax, uint256 taxedDVD) {
checkSnapshot();
releaseTreasury();
require(dvdAmount <= IERC20(dvd).balanceOf(sender), 'Insufficient balance');
require(dvdAmount > 0, 'Cannot sell 0');
require(IDvd(dvd).shareholderPointOf(sender) >= dvdAmount, 'Insufficient shareholder points');
uint256 reserveAmount = dvdToReserve(dvdAmount);
curveTax = reserveAmount.div(CURVE_TAX_DENOMINATOR);
require(curveTax >= 1, 'Insufficient tax');
uint256 net = reserveAmount.sub(curveTax);
taxedDVD = _calculateReserveToDVD(
curveTax,
totalReserve.sub(reserveAmount),
dvdTotalSupply().sub(dvdAmount)
);
marketTax = net.div(SELL_TAX_DENOMINATOR);
returnedMUSD = net.sub(marketTax);
totalReserve = totalReserve.sub(net);
IDvd(dvd).burn(sender, dvdAmount);
IDvd(dvd).mint(BURN_ADDRESS, taxedDVD);
IDvd(dvd).decreaseShareholderPoint(sender, dvdAmount);
IVault(vault).redeem(returnedMUSD);
IERC20(musd).safeTransfer(recipient, returnedMUSD);
emit Sold(sender, recipient, dvdAmount, returnedMUSD);
}
function _claimDividend(address sender, address recipient) internal returns (uint256 dividend) {
checkSnapshot();
releaseTreasury();
dividend = claimableDividend(sender);
require(dividend > 0, 'No dividend');
_isDividendClaimedSnapshots[sender][snapshotId] = true;
IVault(vault).redeem(dividend);
IERC20(musd).safeTransfer(recipient, dividend);
emit DividendClaimed(recipient, dividend);
}
function _swapETHToMUSD(address recipient, uint256 amount) internal returns (uint256 musdAmount) {
IWETH(weth).deposit{ value: amount }();
(musdAmount,) = IBPool(balancerPool).swapExactAmountIn(weth, amount, musd, 0, uint256(-1));
if (recipient != address(this)) {
IERC20(musd).safeTransfer(recipient, musdAmount);
}
}
function _swapMUSDToETH(address recipient, uint256 amount) internal returns (uint256 ethAmount) {
(ethAmount,) = IBPool(balancerPool).swapExactAmountIn(musd, amount, weth, 0, uint256(-1));
IWETH(weth).withdraw(ethAmount);
if (recipient != address(this)) {
payable(recipient).transfer(ethAmount);
}
}
function _approveMax(address tkn, address spender) internal {
uint256 max = uint256(- 1);
IERC20(tkn).safeApprove(spender, max);
}
function _calculateReserveToDVD(
uint256 _reserveDelta,
uint256 _totalReserve,
uint256 _supply
) internal pure returns (uint256 _supplyDelta) {
uint256 _reserve = _totalReserve;
uint256 _newReserve = _reserve.add(_reserveDelta);
uint256 _newSupply = MathUtils.sqrt(
_newReserve
.mul(2)
.mul(DIVIDER)
.mul(1e18)
);
_supplyDelta = _newSupply.sub(_supply);
}
function _calculateDVDToReserve(
uint256 _supplyDelta,
uint256 _supply,
uint256 _totalReserve
) internal pure returns (uint256 _reserveDelta) {
require(_supplyDelta <= _supply, 'Token amount must be less than the supply');
uint256 _newSupply = _supply.sub(_supplyDelta);
uint256 _newReserve = _calculateReserveFromSupply(_newSupply);
_reserveDelta = _totalReserve.sub(_newReserve);
}
function _calculateReserveFromSupply(uint256 _supply) internal pure returns (uint256 _reserve) {
_reserve = _supply
.mul(_supply)
.div(DIVIDER)
.div(2)
.roundedDiv(1e18);
}
}
文件 16 的 20:MassetStructs.sol
pragma solidity ^0.6.12;
interface MassetStructs {
struct Basket {
Basset[] bassets;
uint8 maxBassets;
bool undergoingRecol;
bool failed;
uint256 collateralisationRatio;
}
struct Basset {
address addr;
BassetStatus status;
bool isTransferFeeCharged;
uint256 ratio;
uint256 maxWeight;
uint256 vaultBalance;
}
enum BassetStatus {
Default,
Normal,
BrokenBelowPeg,
BrokenAbovePeg,
Blacklisted,
Liquidating,
Liquidated,
Failed
}
struct BassetDetails {
Basset bAsset;
address integrator;
uint8 index;
}
struct ForgePropsMulti {
bool isValid;
Basset[] bAssets;
address[] integrators;
uint8[] indexes;
}
struct RedeemPropsMulti {
uint256 colRatio;
Basset[] bAssets;
address[] integrators;
uint8[] indexes;
}
}
文件 17 的 20:MathUtils.sol
pragma solidity >=0.5.16 <0.7.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
library MathUtils {
using SafeMath for uint256;
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function roundedDiv(uint256 a, uint256 b) internal pure returns (uint256) {
require(b != 0, 'div by 0');
uint256 halfB = (b.mod(2) == 0) ? (b.div(2)) : (b.div(2).add(1));
return (a.mod(b) >= halfB) ? (a.div(b).add(1)) : (a.div(b));
}
}
文件 18 的 20:ReentrancyGuard.sol
pragma solidity ^0.6.0;
contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 19 的 20:SafeERC20.sol
pragma solidity ^0.6.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
文件 20 的 20:SafeMath.sol
pragma solidity ^0.6.0;
library SafeMath {
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
return c;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
{
"compilationTarget": {
"contracts/LordOfCoin.sol": "LordOfCoin"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"address","name":"_uniswapRouter","type":"address"},{"internalType":"address","name":"_balancerPool","type":"address"},{"internalType":"address","name":"_dvd","type":"address"},{"internalType":"address","name":"_sdvd","type":"address"},{"internalType":"address","name":"_sdvdEthPool","type":"address"},{"internalType":"address","name":"_dvdPool","type":"address"},{"internalType":"address","name":"_devTreasury","type":"address"},{"internalType":"address","name":"_poolTreasury","type":"address"},{"internalType":"address","name":"_tradingTreasury","type":"address"},{"internalType":"uint256","name":"_marketOpenTime","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"musdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dvdReceived","type":"uint256"}],"name":"Bought","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"musdReceived","type":"uint256"}],"name":"DividendClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"}],"name":"DividendClaimedETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"dvdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"musdReceived","type":"uint256"}],"name":"Sold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"dvdAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethReceived","type":"uint256"}],"name":"SoldToETH","type":"event"},{"inputs":[],"name":"BURN_ADDRESS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BUY_TAX_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"CURVE_TAX_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DIVIDER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SELL_TAX_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SNAPSHOT_DURATION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"musdAmount","type":"uint256"}],"name":"buy","outputs":[{"internalType":"uint256","name":"recipientDVD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"buyFromETH","outputs":[{"internalType":"uint256","name":"recipientDVD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"musdAmount","type":"uint256"}],"name":"buyTo","outputs":[{"internalType":"uint256","name":"recipientDVD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"checkSnapshot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimDividend","outputs":[{"internalType":"uint256","name":"dividend","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimDividendETH","outputs":[{"internalType":"uint256","name":"dividend","type":"uint256"},{"internalType":"uint256","name":"receivedETH","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"claimDividendTo","outputs":[{"internalType":"uint256","name":"dividend","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"claimableDividend","outputs":[{"internalType":"uint256","name":"dividend","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimableProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_snapshotId","type":"uint256"}],"name":"claimableProfitAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"depositTradingProfit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"devTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dividendPayingSDVDSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_snapshotId","type":"uint256"}],"name":"dividendPayingSDVDSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvd","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvdBurnedAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvdPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvdPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvdPriceFloor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"dvdToReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"dvdToReserveTaxed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dvdTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"_snapshotId","type":"uint256"}],"name":"isDividendClaimedAt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isMarketOpen","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketOpenTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"releaseTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveAmount","type":"uint256"}],"name":"reserveToDVD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"reserveAmount","type":"uint256"}],"name":"reserveToDVDTaxed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sdvd","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sdvdEthPairAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sdvdEthPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"dvdAmount","type":"uint256"}],"name":"sell","outputs":[{"internalType":"uint256","name":"returnedMUSD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"dvdAmount","type":"uint256"}],"name":"sellTo","outputs":[{"internalType":"uint256","name":"returnedMUSD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"dvdAmount","type":"uint256"}],"name":"sellToETH","outputs":[{"internalType":"uint256","name":"returnedETH","type":"uint256"},{"internalType":"uint256","name":"returnedMUSD","type":"uint256"},{"internalType":"uint256","name":"marketTax","type":"uint256"},{"internalType":"uint256","name":"curveTax","type":"uint256"},{"internalType":"uint256","name":"taxedDVD","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"snapshotId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"snapshotTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalDividendClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLockedReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalProfit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_snapshotId","type":"uint256"}],"name":"totalProfitAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReserve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tradingTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]