// SPDX-License-Identifier: MIXED
// File @boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol@v1.2.1
// License-Identifier: MIT
pragma solidity 0.6.12;
/// @notice A library for performing overflow-/underflow-safe math,
/// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
library BoringMath {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function to128(uint256 a) internal pure returns (uint128 c) {
require(a <= uint128(-1), "BoringMath: uint128 Overflow");
c = uint128(a);
}
function to64(uint256 a) internal pure returns (uint64 c) {
require(a <= uint64(-1), "BoringMath: uint64 Overflow");
c = uint64(a);
}
function to32(uint256 a) internal pure returns (uint32 c) {
require(a <= uint32(-1), "BoringMath: uint32 Overflow");
c = uint32(a);
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
library BoringMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
library BoringMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
library BoringMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
// File @boringcrypto/boring-solidity/contracts/Domain.sol@v1.2.1
// License-Identifier: MIT
// Based on code and smartness by Ross Campbell and Keno
// Uses immutable to store the domain separator to reduce gas usage
// If the chain id changes due to a fork, the forked chain will calculate on the fly.
pragma solidity 0.6.12;
// solhint-disable no-inline-assembly
contract Domain {
bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
// See https://eips.ethereum.org/EIPS/eip-191
string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01";
// solhint-disable var-name-mixedcase
bytes32 private immutable _DOMAIN_SEPARATOR;
uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;
/// @dev Calculate the DOMAIN_SEPARATOR
function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_SEPARATOR_SIGNATURE_HASH,
chainId,
address(this)
)
);
}
constructor() public {
uint256 chainId; assembly {chainId := chainid()}
_DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = chainId);
}
/// @dev Return the DOMAIN_SEPARATOR
// It's named internal to allow making it public from the contract that uses it by creating a simple view function
// with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.
// solhint-disable-next-line func-name-mixedcase
function _domainSeparator() internal view returns (bytes32) {
uint256 chainId; assembly {chainId := chainid()}
return chainId == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(chainId);
}
function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {
digest =
keccak256(
abi.encodePacked(
EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA,
_domainSeparator(),
dataHash
)
);
}
}
// File contracts/interfaces/IERC20.sol
//License-Identifier: MIT
pragma solidity ^0.6.12;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender)
external
view
returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
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
);
/// @notice EIP 2612
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// File contracts/DictatorDAO.sol
//License-Identifier: MIT
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
// DAO code/operator management/dutch auction, etc by BoringCrypto
// Staking in DictatorDAO inspired by Chef Nomi's SushiBar (heavily modified) - MIT license (originally WTFPL)
// TimeLock functionality Copyright 2020 Compound Labs, Inc. - BSD 3-Clause "New" or "Revised" License
contract DictatorDAO is IERC20, Domain {
using BoringMath for uint256;
using BoringMath128 for uint128;
string public symbol;
string public name;
uint8 public constant decimals = 18;
uint256 public override totalSupply;
IERC20 public immutable token;
address public operator;
mapping(address => address) public userVote;
mapping(address => uint256) public votes;
constructor(
string memory sharesSymbol,
string memory sharesName,
IERC20 token_,
address initialOperator
) public {
symbol = sharesSymbol;
name = sharesName;
token = token_;
operator = initialOperator;
}
struct User {
uint128 balance;
uint128 lockedUntil;
}
/// @notice owner > balance mapping.
mapping(address => User) public users;
/// @notice owner > spender > allowance mapping.
mapping(address => mapping(address => uint256)) public override allowance;
/// @notice owner > nonce mapping. Used in `permit`.
mapping(address => uint256) public nonces;
function balanceOf(address user)
public
view
override
returns (uint256 balance)
{
return users[user].balance;
}
function _transfer(
address from,
address to,
uint256 shares
) internal {
User memory fromUser = users[from];
require(block.timestamp >= fromUser.lockedUntil, "Locked");
if (shares != 0) {
require(fromUser.balance >= shares, "Low balance");
if (from != to) {
require(to != address(0), "Zero address"); // Moved down so other failed calls save some gas
User memory toUser = users[to];
address userVoteTo = userVote[to];
address userVoteFrom = userVote[from];
users[from].balance = fromUser.balance - shares.to128(); // Underflow is checked
users[to].balance = toUser.balance + shares.to128(); // Can't overflow because totalSupply would be greater than 2^256-1
// The "from" user's nominee started with at least that user's
// votes, and votes correspond to 1:1 to balances. By the
// "Low balance" check above this will not underflow.
votes[userVoteFrom] -= shares;
// The "to" user's nominee started with at most `totalSupply`
// votes. By the above, they have at least `shares` fewer now.
// It follows that there can be no overflow.
votes[userVoteTo] += shares;
}
}
emit Transfer(from, to, shares);
}
function _useAllowance(address from, uint256 shares) internal {
if (msg.sender == from) {
return;
}
uint256 spenderAllowance = allowance[from][msg.sender];
// If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).
if (spenderAllowance != type(uint256).max) {
require(spenderAllowance >= shares, "Low allowance");
allowance[from][msg.sender] = spenderAllowance - shares; // Underflow is checked
}
}
/// @notice Transfers `shares` tokens from `msg.sender` to `to`.
/// @param to The address to move the tokens.
/// @param shares of the tokens to move.
/// @return (bool) Returns True if succeeded.
function transfer(address to, uint256 shares)
external
override
returns (bool)
{
_transfer(msg.sender, to, shares);
return true;
}
/// @notice Transfers `shares` tokens from `from` to `to`. Caller needs approval for `from`.
/// @param from Address to draw tokens from.
/// @param to The address to move the tokens.
/// @param shares The token shares to move.
/// @return (bool) Returns True if succeeded.
function transferFrom(
address from,
address to,
uint256 shares
) external override returns (bool) {
_useAllowance(from, shares);
_transfer(from, to, shares);
return true;
}
/// @notice Approves `amount` from sender to be spend by `spender`.
/// @param spender Address of the party that can draw from msg.sender's account.
/// @param amount The maximum collective amount that `spender` can draw.
/// @return (bool) Returns True if approved.
function approve(address spender, uint256 amount)
external
override
returns (bool)
{
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 private constant PERMIT_SIGNATURE_HASH =
0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/// @notice Approves `value` from `owner_` to be spend by `spender`.
/// @param owner_ Address of the owner.
/// @param spender The address of the spender that gets approved to draw from `owner_`.
/// @param value The maximum collective amount that `spender` can draw.
/// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).
function permit(
address owner_,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(owner_ != address(0), "Zero owner");
require(block.timestamp < deadline, "Expired");
require(
ecrecover(
_getDigest(
keccak256(
abi.encode(
PERMIT_SIGNATURE_HASH,
owner_,
spender,
value,
nonces[owner_]++,
deadline
)
)
),
v,
r,
s
) == owner_,
"Invalid Sig"
);
allowance[owner_][spender] = value;
emit Approval(owner_, spender, value);
}
// Operator Setting
address public pendingOperator;
uint256 public pendingOperatorTime;
// Condition for safe math: totalSupply < 2^255, so that the doubling fits.
// A sufficient condition is for this to hold for token.totalSupply.
function setOperator(address newOperator) public {
require(newOperator != address(0), "Zero operator");
uint256 netVotes = totalSupply - votes[address(0)];
if (newOperator != pendingOperator) {
require(votes[newOperator] * 2 > netVotes, "Not enough votes");
pendingOperator = newOperator;
pendingOperatorTime = block.timestamp + 7 days;
} else {
if (votes[newOperator] * 2 > netVotes) {
require(block.timestamp >= pendingOperatorTime, "Wait longer");
operator = pendingOperator;
}
// If there aren't enough votes, then the pending operator failed
// to maintain a majority. If there are, then they are now the
// operator. In either situation:
pendingOperator = address(0);
pendingOperatorTime = 0;
}
}
/// math is ok, because amount, totalSupply and shares is always 0 <= amount <= 100.000.000 * 10^18
/// theoretically you can grow the amount/share ratio, but it's not practical and useless
function mint(uint256 amount, address operatorVote) public returns (bool) {
require(msg.sender != address(0), "Zero address");
User memory user = users[msg.sender];
uint256 totalTokens = token.balanceOf(address(this));
uint256 shares =
totalSupply == 0 ? amount : (amount * totalSupply) / totalTokens;
// Did we change our vote? Do this while we know our previous total:
address currentVote = userVote[msg.sender];
uint256 extraVotes = shares;
if (currentVote != operatorVote) {
if (user.balance > 0) {
// Safe, because the user must have added their balance before
votes[currentVote] -= user.balance;
extraVotes += user.balance;
}
userVote[msg.sender] = operatorVote;
}
votes[operatorVote] += extraVotes;
user.balance += shares.to128();
user.lockedUntil = (block.timestamp + 24 hours).to128();
users[msg.sender] = user;
totalSupply += shares;
token.transferFrom(msg.sender, address(this), amount);
emit Transfer(address(0), msg.sender, shares);
return true;
}
// Change your vote. Does not lock tokens.
function vote(address operatorVote) public returns (bool) {
address currentVote = userVote[msg.sender];
if (currentVote != operatorVote) {
User memory user = users[msg.sender];
if (user.balance > 0) {
votes[currentVote] -= user.balance;
votes[operatorVote] += user.balance;
}
userVote[msg.sender] = operatorVote;
}
return true;
}
function _burn(
address from,
address to,
uint256 shares
) internal {
require(to != address(0), "Zero address");
User memory user = users[from];
require(block.timestamp >= user.lockedUntil, "Locked");
uint256 amount =
(shares * token.balanceOf(address(this))) / totalSupply;
users[from].balance = user.balance.sub(shares.to128()); // Must check underflow
totalSupply -= shares;
votes[userVote[from]] -= shares;
token.transfer(to, amount);
emit Transfer(from, address(0), shares);
}
function burn(address to, uint256 shares) public returns (bool) {
_burn(msg.sender, to, shares);
return true;
}
function burnFrom(
address from,
address to,
uint256 shares
) public returns (bool) {
_useAllowance(from, shares);
_burn(from, to, shares);
return true;
}
event QueueTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
bytes data,
uint256 eta
);
event CancelTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
bytes data
);
event ExecuteTransaction(
bytes32 indexed txHash,
address indexed target,
uint256 value,
bytes data
);
uint256 public constant GRACE_PERIOD = 14 days;
uint256 public constant DELAY = 2 days;
mapping(bytes32 => uint256) public queuedTransactions;
function queueTransaction(
address target,
uint256 value,
bytes memory data
) public returns (bytes32) {
require(msg.sender == operator, "Operator only");
require(votes[operator] * 2 > totalSupply, "Not enough votes");
bytes32 txHash = keccak256(abi.encode(target, value, data));
uint256 eta = block.timestamp + DELAY;
queuedTransactions[txHash] = eta;
emit QueueTransaction(txHash, target, value, data, eta);
return txHash;
}
function cancelTransaction(
address target,
uint256 value,
bytes memory data
) public {
require(msg.sender == operator, "Operator only");
bytes32 txHash = keccak256(abi.encode(target, value, data));
queuedTransactions[txHash] = 0;
emit CancelTransaction(txHash, target, value, data);
}
function executeTransaction(
address target,
uint256 value,
bytes memory data
) public payable returns (bytes memory) {
require(msg.sender == operator, "Operator only");
require(votes[operator] * 2 > totalSupply, "Not enough votes");
bytes32 txHash = keccak256(abi.encode(target, value, data));
uint256 eta = queuedTransactions[txHash];
require(block.timestamp >= eta, "Too early");
require(block.timestamp <= eta + GRACE_PERIOD, "Tx stale");
queuedTransactions[txHash] = 0;
// solium-disable-next-line security/no-call-value
(bool success, bytes memory returnData) =
target.call{value: value}(data);
require(success, "Tx reverted :(");
emit ExecuteTransaction(txHash, target, value, data);
return returnData;
}
}
{
"compilationTarget": {
"DAO.sol": "DictatorDAO"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"sharesSymbol","type":"string"},{"internalType":"string","name":"sharesName","type":"string"},{"internalType":"contract IERC20","name":"token_","type":"address"},{"internalType":"address","name":"initialOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"CancelTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ExecuteTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"eta","type":"uint256"}],"name":"QueueTransaction","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DELAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GRACE_PERIOD","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"burnFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"cancelTransaction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"executeTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"operatorVote","type":"address"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"operator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOperatorTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"queueTransaction","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"queuedTransactions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOperator","type":"address"}],"name":"setOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userVote","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"users","outputs":[{"internalType":"uint128","name":"balance","type":"uint128"},{"internalType":"uint128","name":"lockedUntil","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operatorVote","type":"address"}],"name":"vote","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]