编译器
0.8.18+commit.87f61d96
文件 1 的 35:AccessControl.sol
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
文件 2 的 35:AddressUpgradeable.sol
pragma solidity ^0.8.1;
library AddressUpgradeable {
function isContract(address account) internal view returns (bool) {
return account.code.length > 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 functionCallWithValue(target, data, 0, "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");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 3 的 35:AdminAgent.sol
pragma solidity ^0.8.18;
import { Context } from "../lib/utils/Context.sol";
contract AdminAgent is Context {
mapping(address => bool) private _adminAgents;
constructor(address[] memory adminAgents_) {
for (uint i = 0; i < adminAgents_.length; i++) {
require(adminAgents_[i] != address(0), "Invalid address");
_adminAgents[adminAgents_[i]] = true;
}
}
modifier onlyAdminAgents() {
require(_adminAgents[_msgSender()], "Unauthorized");
_;
}
}
文件 4 的 35:AdminGovernanceAgent.sol
pragma solidity ^0.8.18;
import { Context } from "../lib/utils/Context.sol";
contract AdminGovernanceAgent is Context {
mapping(address => bool) private _adminGovAgents;
constructor(address[] memory adminGovAgents_) {
for (uint i = 0; i < adminGovAgents_.length; i++) {
require(adminGovAgents_[i] != address(0), "Invalid address");
_adminGovAgents[adminGovAgents_[i]] = true;
}
}
modifier onlyAdminGovAgents() {
require(_adminGovAgents[_msgSender()], "Unauthorized");
_;
}
}
文件 5 的 35:BackendAgent.sol
pragma solidity ^0.8.18;
import { Context } from "../lib/utils/Context.sol";
contract BackendAgent is Context {
mapping(address => bool) private _backendAdminAgents;
mapping(address => bool) private _backendAgents;
event SetBackendAgent(address agent);
event RevokeBackendAgent(address agent);
modifier onlyBackendAdminAgents() {
require(_backendAdminAgents[_msgSender()], "Unauthorized");
_;
}
modifier onlyBackendAgents() {
require(_backendAgents[_msgSender()], "Unauthorized");
_;
}
function _setBackendAgents(address[] memory backendAgents) internal {
for (uint i = 0; i < backendAgents.length; i++) {
require(backendAgents[i] != address(0), "Invalid address");
_backendAgents[backendAgents[i]] = true;
}
}
function _setBackendAdminAgents(address[] memory backendAdminAgents) internal {
for (uint i = 0; i < backendAdminAgents.length; i++) {
require(backendAdminAgents[i] != address(0), "Invalid address");
_backendAdminAgents[backendAdminAgents[i]] = true;
}
}
function setBackendAgent(address _agent) external onlyBackendAdminAgents {
require(_agent != address(0), "Invalid address");
_backendAgents[_agent] = true;
emit SetBackendAgent(_agent);
}
function revokeBackendAgent(address _agent) external onlyBackendAdminAgents {
_backendAgents[_agent] = false;
emit RevokeBackendAgent(_agent);
}
}
文件 6 的 35:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 7 的 35:ContextUpgradeable.sol
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
uint256[50] private __gap;
}
文件 8 的 35:ERC165.sol
pragma solidity ^0.8.0;
import "./IERC165.sol";
abstract contract ERC165 is IERC165 {
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
文件 9 的 35:ERC1967UpgradeUpgradeable.sol
pragma solidity ^0.8.2;
import "../beacon/IBeaconUpgradeable.sol";
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../../utils/AddressUpgradeable.sol";
import "../../utils/StorageSlotUpgradeable.sol";
import "../utils/Initializable.sol";
abstract contract ERC1967UpgradeUpgradeable is Initializable {
function __ERC1967Upgrade_init() internal onlyInitializing {
}
function __ERC1967Upgrade_init_unchained() internal onlyInitializing {
}
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
event Upgraded(address indexed implementation);
function _getImplementation() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
function _setImplementation(address newImplementation) private {
require(AddressUpgradeable.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlotUpgradeable.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
function _upgradeToAndCall(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
_functionDelegateCall(newImplementation, data);
}
}
function _upgradeToAndCallUUPS(
address newImplementation,
bytes memory data,
bool forceCall
) internal {
if (StorageSlotUpgradeable.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822ProxiableUpgradeable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
event AdminChanged(address previousAdmin, address newAdmin);
function _getAdmin() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value;
}
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlotUpgradeable.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
event BeaconUpgraded(address indexed beacon);
function _getBeacon() internal view returns (address) {
return StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value;
}
function _setBeacon(address newBeacon) private {
require(AddressUpgradeable.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
AddressUpgradeable.isContract(IBeaconUpgradeable(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlotUpgradeable.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
function _upgradeBeaconToAndCall(
address newBeacon,
bytes memory data,
bool forceCall
) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
_functionDelegateCall(IBeaconUpgradeable(newBeacon).implementation(), data);
}
}
function _functionDelegateCall(address target, bytes memory data) private returns (bytes memory) {
require(AddressUpgradeable.isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return AddressUpgradeable.verifyCallResult(success, returndata, "Address: low-level delegate call failed");
}
uint256[50] private __gap;
}
文件 10 的 35:ERC20.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
function decimals() public view virtual override returns (uint8) {
return 18;
}
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
文件 11 的 35:Governable.sol
pragma solidity ^0.8.18;
import { Context } from "../lib/utils/Context.sol";
import { Registrar } from "../Registrar.sol";
contract Governable is Context {
address internal _governanceAddress;
constructor() {}
modifier onlyGovernance() {
require(_governanceAddress == _msgSender(), "Unauthorized");
_;
}
function _updateGovernable(Registrar registrar) internal {
_governanceAddress = registrar.getVETHGovernance();
}
function getGovernanceAddress() external view returns (address) {
return _governanceAddress;
}
}
文件 12 的 35:IAccessControl.sol
pragma solidity ^0.8.0;
interface IAccessControl {
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
function hasRole(bytes32 role, address account) external view returns (bool);
function getRoleAdmin(bytes32 role) external view returns (bytes32);
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function renounceRole(bytes32 role, address account) external;
}
文件 13 的 35:IBeaconUpgradeable.sol
pragma solidity ^0.8.0;
interface IBeaconUpgradeable {
function implementation() external view returns (address);
}
文件 14 的 35:IERC165.sol
pragma solidity ^0.8.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 15 的 35:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, 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 from, address to, uint256 amount) external returns (bool);
}
文件 16 的 35:IERC20Metadata.sol
pragma solidity ^0.8.0;
import "../IERC20.sol";
interface IERC20Metadata is IERC20 {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
}
文件 17 的 35:Initializable.sol
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
abstract contract Initializable {
uint8 private _initialized;
bool private _initializing;
event Initialized(uint8 version);
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
}
文件 18 的 35:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1, "Math: mulDiv overflow");
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
文件 19 的 35:OwnableUpgradeable.sol
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
uint256[49] private __gap;
}
文件 20 的 35:Registrar.sol
pragma solidity 0.8.18;
import { AdminAgent } from "./access/AdminAgent.sol";
import { IRegistrarClient } from "./RegistrarClient.sol";
import { VYToken } from "./token/VYToken.sol";
contract Registrar is AdminAgent {
bytes32 private constant ECOSYSTEM_ID = keccak256(bytes("VY_ETH"));
address[] private _contracts;
address[] private _prevContracts;
bool private _finalized;
event SetContracts(address[] addresses);
event SetContractByIndex(uint8 index, address contractAddressTo);
event Finalize(address registrarAddress);
enum Contract {
VYToken,
VETHYieldRateTreasury,
VETHP2P,
VETHRevenueCycleTreasury,
VETHGovernance,
VETHReverseStakingTreasury
}
constructor(address[] memory adminAgents) AdminAgent(adminAgents) {
_prevContracts = new address[](_numbersOfContracts());
}
modifier onlyUnfinalized() {
require(_finalized == false, "Registrar already finalized");
_;
}
modifier onlyValidContractIndex(uint256 index) {
require(index < _numbersOfContracts(), "Invalid index");
_;
}
function getEcosystemId() external pure virtual returns (bytes32) {
return ECOSYSTEM_ID;
}
function getContracts() external view returns (address[] memory) {
return _contracts;
}
function getContractByIndex(
uint256 index
) external view onlyValidContractIndex(index) returns (address) {
return _contracts[index];
}
function getPrevContractByIndex(
uint256 index
) external view onlyValidContractIndex(index) returns (address) {
return _prevContracts[index];
}
function setContracts(address[] calldata _addresses) external onlyAdminAgents onlyUnfinalized {
require(_validContractsLength(_addresses.length), "Invalid number of addresses");
for (uint i = 0; i < _contracts.length; i++) {
if (_addresses[i] != _contracts[i]) {
_prevContracts[i] = _contracts[i];
}
}
_contracts = _addresses;
emit SetContracts(_addresses);
}
function setContractByIndex(uint8 _index, address _address) external onlyAdminAgents onlyUnfinalized {
if (_address != _contracts[_index]) {
_prevContracts[_index] = _contracts[_index];
}
_contracts[_index] = _address;
emit SetContractByIndex(_index, _address);
}
function updateAllClients() external onlyAdminAgents onlyUnfinalized {
VYToken(this.getVYToken()).setMinter();
IRegistrarClient(this.getVETHP2P()).updateAddresses();
IRegistrarClient(this.getVETHRevenueCycleTreasury()).updateAddresses();
IRegistrarClient(this.getVETHReverseStakingTreasury()).updateAddresses();
IRegistrarClient(this.getVETHYieldRateTreasury()).updateAddresses();
IRegistrarClient(this.getVETHGovernance()).updateAddresses();
}
function getVYToken() external view returns (address) {
return _contracts[uint(Contract.VYToken)];
}
function getVETHYieldRateTreasury() external view returns (address) {
return _contracts[uint(Contract.VETHYieldRateTreasury)];
}
function getVETHP2P() external view returns (address) {
return _contracts[uint(Contract.VETHP2P)];
}
function getVETHRevenueCycleTreasury() external view returns (address) {
return _contracts[uint(Contract.VETHRevenueCycleTreasury)];
}
function getVETHGovernance() external view returns (address) {
return _contracts[uint(Contract.VETHGovernance)];
}
function getVETHReverseStakingTreasury() external view returns (address) {
return _contracts[uint(Contract.VETHReverseStakingTreasury)];
}
function finalize() external onlyAdminAgents onlyUnfinalized {
_finalized = true;
emit Finalize(address(this));
}
function isFinalized() external view returns (bool) {
return _finalized;
}
function _numbersOfContracts() private pure returns (uint256) {
return uint(Contract.VETHReverseStakingTreasury) + 1;
}
function _validContractsLength(uint256 contractsLength) private pure returns (bool) {
return contractsLength == _numbersOfContracts();
}
}
文件 21 的 35:RegistrarClient.sol
pragma solidity ^0.8.18;
import { Context } from "./lib/utils/Context.sol";
import { Registrar } from "./Registrar.sol";
interface IRegistrarClient {
function updateAddresses() external;
}
abstract contract RegistrarClient is Context, IRegistrarClient {
Registrar internal _registrar;
constructor(address registrarAddress) {
require(registrarAddress != address(0), "Invalid address");
_registrar = Registrar(registrarAddress);
}
modifier onlyRegistrar() {
require(_msgSender() == address(_registrar), "Unauthorized, registrar only");
_;
}
function getRegistrar() external view returns(address) {
return address(_registrar);
}
function updateAddresses() external override virtual;
}
文件 22 的 35:RegistrarMigrator.sol
pragma solidity ^0.8.18;
import { Registrar } from "./Registrar.sol";
import { AdminAgent } from "./access/AdminAgent.sol";
import { VYToken } from "./token/VYToken.sol";
abstract contract RegistrarMigrator is AdminAgent {
Registrar private _registrar;
uint256 private _contractIndex;
constructor(
address registrarAddress,
uint256 contractIndex,
address[] memory adminAgents
) AdminAgent(adminAgents) {
require(registrarAddress != address(0), "Invalid address");
_registrar = Registrar(registrarAddress);
_contractIndex = contractIndex;
}
modifier onlyUnfinalized() {
require(_registrar.isFinalized() == false, "Registrar already finalized");
_;
}
function registrarMigrateTokens() external onlyAdminAgents onlyUnfinalized {
VYToken vyToken = VYToken(_registrar.getVYToken());
vyToken.registrarMigrateTokens(_registrar.getEcosystemId(), _contractIndex);
}
}
文件 23 的 35:Router.sol
pragma solidity 0.8.18;
import { OwnableUpgradeable } from "./lib/openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import { Initializable } from "./lib/openzeppelin-upgradeable/proxy/utils/Initializable.sol";
import { UUPSUpgradeable } from "./lib/openzeppelin-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract Router is Initializable, OwnableUpgradeable, UUPSUpgradeable {
address private _primaryStakeholder;
event Route(address indexed receiver, uint256 amount);
constructor() {
_disableInitializers();
}
function initialize(address primaryStakeholder_) public initializer {
__Ownable_init();
__UUPSUpgradeable_init();
_primaryStakeholder = primaryStakeholder_;
}
function route() external payable {
_routePrimaryStakeholder(msg.value);
}
function _routePrimaryStakeholder(uint256 amount) private {
(bool sent,) = _primaryStakeholder.call{value: amount}("");
require(sent, "Failed to send Ether");
emit Route(_primaryStakeholder, amount);
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
receive() external payable {}
}
文件 24 的 35:SignedMath.sol
pragma solidity ^0.8.0;
library SignedMath {
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
function average(int256 a, int256 b) internal pure returns (int256) {
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
function abs(int256 n) internal pure returns (uint256) {
unchecked {
return uint256(n >= 0 ? n : -n);
}
}
}
文件 25 的 35:StorageSlotUpgradeable.sol
pragma solidity ^0.8.0;
library StorageSlotUpgradeable {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly {
r.slot := slot
}
}
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly {
r.slot := slot
}
}
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly {
r.slot := slot
}
}
}
文件 26 的 35:Strings.sol
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}
文件 27 的 35:UUPSUpgradeable.sol
pragma solidity ^0.8.0;
import "../../interfaces/draft-IERC1822Upgradeable.sol";
import "../ERC1967/ERC1967UpgradeUpgradeable.sol";
import "./Initializable.sol";
abstract contract UUPSUpgradeable is Initializable, IERC1822ProxiableUpgradeable, ERC1967UpgradeUpgradeable {
function __UUPSUpgradeable_init() internal onlyInitializing {
}
function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
}
address private immutable __self = address(this);
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
modifier notDelegated() {
require(address(this) == __self, "UUPSUpgradeable: must not be called through delegatecall");
_;
}
function proxiableUUID() external view virtual override notDelegated returns (bytes32) {
return _IMPLEMENTATION_SLOT;
}
function upgradeTo(address newImplementation) external virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, new bytes(0), false);
}
function upgradeToAndCall(address newImplementation, bytes memory data) external payable virtual onlyProxy {
_authorizeUpgrade(newImplementation);
_upgradeToAndCallUUPS(newImplementation, data, true);
}
function _authorizeUpgrade(address newImplementation) internal virtual;
uint256[50] private __gap;
}
文件 28 的 35:VETHGovernance.sol
pragma solidity 0.8.18;
import { AdminAgent } from "../access/AdminAgent.sol";
import { VETHYieldRateTreasury } from "../treasury/VETHYieldRateTreasury.sol";
import { VYToken } from "../token/VYToken.sol";
import { VETHRevenueCycleTreasury } from "../exchange/VETHRevenueCycleTreasury.sol";
import { VETHReverseStakingTreasury } from "../VETHReverseStakingTreasury.sol";
import { RegistrarClient } from "../RegistrarClient.sol";
contract VETHGovernance is AdminAgent, RegistrarClient {
enum VoteOptions {
YES,
NO
}
enum ProposalType {
Migration,
Registrar
}
struct MigrationProposal {
address yieldRateTreasuryDestination;
address revenueCycleTreasuryDestination;
address reverseStakingTreasuryDestination;
}
struct RegistrarProposal {
address registrar;
}
struct Proposal {
ProposalType proposalType;
uint256 endsAt;
bool approved;
MigrationProposal migration;
RegistrarProposal registrar;
}
event StartMigrationProposal(
uint256 proposalId,
address yieldRateTreasuryDestination,
address revenueCycleTreasuryDestination,
address reverseStakingTreasuryDestination,
uint256 endsAt
);
event StartRegistrarProposal(uint256 proposalId, address registrar, uint256 endsAt);
VYToken private _vyToken;
VETHYieldRateTreasury private _vethYRT;
VETHRevenueCycleTreasury private _vethRevenueCycleTreasury;
VETHReverseStakingTreasury private _vethReverseStakingTreasury;
mapping(uint256 => mapping(address => uint256)) private _deposits;
mapping(uint256 => mapping(VoteOptions => uint256)) private _voteCount;
mapping(uint256 => Proposal) private _proposals;
uint256 public votingPeriod;
uint256 private _proposalNonce = 0;
event Vote(address account, VoteOptions voteOption, uint256 quantity);
constructor(
address registrarAddress,
uint256 votingPeriod_,
address[] memory adminAgents
) AdminAgent(adminAgents) RegistrarClient(registrarAddress) {
votingPeriod = votingPeriod_;
}
modifier hasMigrationAddresses() {
require(address(_vethYRT) != address(0), "ETH Treasury address not set");
require(address(_vethRevenueCycleTreasury) != address(0), "VETHRevenueCycleTreasury address not set");
require(address(_vethReverseStakingTreasury) != address(0), "VETHReverseStakingTreasury address not set");
_;
}
modifier hasProposal() {
require(_proposals[_proposalNonce].endsAt > 0, "No proposal");
_;
}
modifier hasProposalById(uint256 proposalId) {
require(_proposals[proposalId].endsAt > 0, "No proposal");
_;
}
function getCurrentProposal() external view returns (Proposal memory) {
return _proposals[_proposalNonce];
}
function getProposalById(uint256 proposalId) external view returns (Proposal memory) {
return _proposals[proposalId];
}
function getCurrentProposalId() external view returns (uint256) {
return _proposalNonce;
}
function getCurrentYesVotes() external view returns (uint256) {
return _voteCount[_proposalNonce][VoteOptions.YES];
}
function getCurrentNoVotes() external view returns (uint256) {
return _voteCount[_proposalNonce][VoteOptions.NO];
}
function getYesVotesById(uint256 proposalId) external view returns (uint256) {
return _voteCount[proposalId][VoteOptions.YES];
}
function getNoVotesById(uint256 proposalId) external view returns (uint256) {
return _voteCount[proposalId][VoteOptions.NO];
}
function getCurrentDepositedVY(address voter) external view returns (uint256) {
return _deposits[_proposalNonce][voter];
}
function getDepositedVYById(uint256 proposalId, address voter) external view returns (uint256) {
return _deposits[proposalId][voter];
}
function hasCurrentProposalEnded() public view hasProposal returns (bool) {
return block.timestamp > _proposals[_proposalNonce].endsAt;
}
function hasProposalEndedById(uint256 proposalId) external view hasProposalById(proposalId) returns (bool) {
return block.timestamp > _proposals[proposalId].endsAt;
}
function voteYes(uint256 quantity) external {
_vote(VoteOptions.YES, quantity);
}
function voteNo(uint256 quantity) external {
_vote(VoteOptions.NO, quantity);
}
function _vote(VoteOptions voteOption, uint256 quantity) private hasProposal {
require(block.timestamp < _proposals[_proposalNonce].endsAt, "Proposal already ended");
require(_deposits[_proposalNonce][_msgSender()] == 0, "Already voted");
require(_vyToken.allowance(_msgSender(), address(this)) >= quantity, "Insufficient VY allowance");
require(_vyToken.balanceOf(_msgSender()) >= quantity, "Insufficient VY balance");
_deposits[_proposalNonce][_msgSender()] += quantity;
_voteCount[_proposalNonce][voteOption] += quantity;
_vyToken.transferFrom(_msgSender(), address(this), quantity);
emit Vote(_msgSender(), voteOption, quantity);
}
function startMigrationProposal(
address yieldRateTreasuryDestination,
address revenueCycleTreasuryDestination,
address reverseStakingTreasuryDestination
) external onlyAdminAgents {
require(
yieldRateTreasuryDestination != address(0) &&
revenueCycleTreasuryDestination != address(0) &&
reverseStakingTreasuryDestination != address(0),
"Invalid address"
);
require(block.timestamp > _proposals[_proposalNonce].endsAt || _proposalNonce == 0, "Proposal still ongoing");
uint256 endsAt = block.timestamp + votingPeriod;
_proposals[++_proposalNonce] = Proposal(
ProposalType.Migration,
endsAt,
false,
MigrationProposal(yieldRateTreasuryDestination, revenueCycleTreasuryDestination, reverseStakingTreasuryDestination),
RegistrarProposal(address(0))
);
emit StartMigrationProposal(
_proposalNonce,
yieldRateTreasuryDestination,
revenueCycleTreasuryDestination,
reverseStakingTreasuryDestination,
endsAt
);
}
function executeMigrationProposal() external hasMigrationAddresses hasProposal onlyAdminAgents {
require(hasCurrentProposalEnded(), "Proposal still ongoing");
require(_proposals[_proposalNonce].proposalType == ProposalType.Migration, "Invalid proposal");
require(_voteCount[_proposalNonce][VoteOptions.YES] >= _voteCount[_proposalNonce][VoteOptions.NO], "Proposal not passed");
_proposals[_proposalNonce].approved = true;
_vethYRT.setMigration(_proposals[_proposalNonce].migration.yieldRateTreasuryDestination);
_vethRevenueCycleTreasury.setMigration(_proposals[_proposalNonce].migration.revenueCycleTreasuryDestination);
_vethReverseStakingTreasury.setMigration(_proposals[_proposalNonce].migration.reverseStakingTreasuryDestination);
}
function startRegistrarProposal(address registrar) external onlyAdminAgents {
require(registrar != address(0), "Invalid address");
require(block.timestamp > _proposals[_proposalNonce].endsAt || _proposalNonce == 0, "Proposal still ongoing");
uint256 endsAt = block.timestamp + votingPeriod;
_proposals[++_proposalNonce] = Proposal(
ProposalType.Registrar,
endsAt,
false,
MigrationProposal(address(0), address(0), address(0)),
RegistrarProposal(registrar)
);
emit StartRegistrarProposal(
_proposalNonce,
registrar,
endsAt
);
}
function executeRegistrarProposal() external hasProposal onlyAdminAgents {
require(hasCurrentProposalEnded(), "Proposal still ongoing");
require(_proposals[_proposalNonce].proposalType == ProposalType.Registrar, "Invalid proposal");
require(_voteCount[_proposalNonce][VoteOptions.YES] >= _voteCount[_proposalNonce][VoteOptions.NO], "Proposal not passed");
_proposals[_proposalNonce].approved = true;
_vyToken.setRegistrar(_registrar.getEcosystemId(), _proposalNonce);
}
function withdrawDepositedVY() external {
_withdraw(_proposalNonce);
}
function withdrawDepositedVYById(uint256 proposalId) external {
_withdraw(proposalId);
}
function withdrawAllDepositedVY() external hasProposal {
require(hasCurrentProposalEnded(), "Proposal still ongoing");
bool nothingToWithdraw = true;
for (uint proposalId = 1; proposalId <= _proposalNonce; proposalId++) {
if (_deposits[proposalId][_msgSender()] > 0) {
nothingToWithdraw = false;
_withdraw(proposalId);
}
}
require(!nothingToWithdraw, "Nothing to withdraw");
}
function _withdraw(uint256 proposalId) private hasProposalById(proposalId) {
require(block.timestamp > _proposals[proposalId].endsAt, "Proposal still ongoing");
require(_deposits[proposalId][_msgSender()] > 0, "Nothing to withdraw");
uint256 quantity = _deposits[proposalId][_msgSender()];
_deposits[proposalId][_msgSender()] = 0;
_vyToken.transfer(_msgSender(), quantity);
}
function updateAddresses() external override onlyRegistrar {
_vyToken = VYToken(_registrar.getVYToken());
_vethYRT = VETHYieldRateTreasury(payable(_registrar.getVETHYieldRateTreasury()));
_vethRevenueCycleTreasury = VETHRevenueCycleTreasury(_registrar.getVETHRevenueCycleTreasury());
_vethReverseStakingTreasury = VETHReverseStakingTreasury(payable(_registrar.getVETHReverseStakingTreasury()));
}
}
文件 29 的 35:VETHP2P.sol
pragma solidity 0.8.18;
import { VETHRevenueCycleTreasury } from "./VETHRevenueCycleTreasury.sol";
import { ERC20 } from "../lib/token/ERC20/ERC20.sol";
import { VYToken } from "../token/VYToken.sol";
import { BackendAgent } from "../access/BackendAgent.sol";
import { RegistrarClient } from "../RegistrarClient.sol";
import { Router } from "../Router.sol";
contract VETHP2P is BackendAgent, RegistrarClient {
uint256 private constant MULTIPLIER = 10**18;
struct TradeOfferCalcInfo {
uint256 amountOut;
uint256 takerReceives;
uint256 takerFee;
uint256 makerReceives;
uint256 makerFee;
}
uint256 public constant EXPIRES_IN = 30 days;
uint256 public constant MINIMUM_AUTOCLOSE_IN_ETH = 500000000000000;
uint256 public constant ETH_FEE = 20000000000000000;
uint256 public constant VY_FEE = 20000000000000000;
Router private _ethComptroller;
VETHRevenueCycleTreasury private _vethRevenueCycleTreasury;
uint256 private _nonce = 1;
enum TradingPairs {
VY_ETH,
ETH_VY
}
struct Offer {
uint256 id;
TradingPairs tradingPair;
uint256 quantity;
uint256 price;
uint256 expiresAt;
bool isOpen;
}
struct TradingPair {
address makerAssetAddress;
address takerAssetAddress;
address makerTreasuryAddress;
address takerTreasuryAddress;
uint256 makerFeeRate;
uint256 takerFeeRate;
}
mapping(address => mapping(uint256 => Offer)) private _offers;
mapping(TradingPairs => TradingPair) private _tradingPairs;
event CreateOffer(uint256 id, address seller, TradingPairs tradingPair, uint256 quantity, uint256 price, uint256 expiresAt, uint256 timestamp);
event TradeOffer(uint256 id, address buyer, uint256 sellerQuantity, uint256 buyerQuantity, uint256 unfilledQuantity, uint256 timestamp);
event CloseOffer(uint256 id, uint256 timestamp);
constructor(
address registrarAddress,
address ethComptrollerAddress_,
address[] memory backendAdminAgents,
address[] memory backendAgents
) RegistrarClient(registrarAddress) {
require(ethComptrollerAddress_ != address(0), "Invalid address");
_ethComptroller = Router(payable(ethComptrollerAddress_));
_setBackendAdminAgents(backendAdminAgents);
_setBackendAgents(backendAgents);
}
modifier onlyValidCreateOffer(TradingPairs tradingPair, uint256 quantity, uint256 price) {
require(_pairExist(tradingPair), "Invalid pair");
require(quantity > 0, "Invalid quantity");
require(price > 0, "Invalid price");
if (tradingPair == TradingPairs.ETH_VY) {
require(msg.value == quantity, "Invalid ETH amount sent");
} else {
require(msg.value == 0, "Invalid ETH amount sent");
}
_;
}
modifier onlyValidTradeOffer(uint256 id, address seller, uint256 quantity) {
require(_isOfferActive(id, seller), "Invalid offer");
require(quantity > 0, "Invalid quantity");
_;
}
modifier onlyOpenOffer(uint256 id, address seller) {
require(_offers[seller][id].isOpen, "Offer must be open in order to close");
_;
}
function getNonce() external view returns (uint256) {
return _nonce;
}
function getOffer(uint256 id, address seller) external view returns (Offer memory) {
return _offers[seller][id];
}
function createOffer(TradingPairs tradingPair, uint256 quantity, uint256 price)
external
payable
onlyValidCreateOffer(tradingPair, quantity, price)
{
_createOffer(tradingPair, quantity, price);
}
function createOffer(TradingPairs tradingPair, uint256 quantity, uint256 price, uint8 v, bytes32 r, bytes32 s)
external
payable
onlyValidCreateOffer(tradingPair, quantity, price)
{
require(_tradingPairs[tradingPair].makerAssetAddress == _tradingPairs[TradingPairs.VY_ETH].makerAssetAddress, "Must be [VY_ETH]");
VYToken makerAsset = VYToken(_tradingPairs[tradingPair].makerAssetAddress);
makerAsset.permit(_msgSender(), address(this), quantity, v, r, s);
_createOffer(tradingPair, quantity, price);
}
function _createOffer(TradingPairs tradingPair, uint256 quantity, uint256 price) private {
uint256 yieldRate = _vethRevenueCycleTreasury.getYieldRate();
if (tradingPair == TradingPairs.ETH_VY) {
require(price <= yieldRate, "Price must be <= yieldRate");
} else if (tradingPair == TradingPairs.VY_ETH) {
require((MULTIPLIER * MULTIPLIER / price) <= yieldRate, "Price reciprocal must be <= yieldRate");
} else {
revert("Unsupported pair");
}
uint256 expiresAt = block.timestamp + EXPIRES_IN;
uint256 id = _nonce++;
_offers[_msgSender()][id] = Offer(id, tradingPair, quantity, price, expiresAt, true);
if (tradingPair == TradingPairs.VY_ETH) {
ERC20 token = _getSpendingTokenAndCheck(_tradingPairs[tradingPair].makerAssetAddress, quantity);
token.transferFrom(_msgSender(), address(this), quantity);
}
emit CreateOffer(id, _msgSender(), tradingPair, quantity, price, expiresAt, block.timestamp);
}
function tradeOffer(uint256 id, address seller, uint256 quantity)
external
payable
onlyValidTradeOffer(id, seller, quantity)
returns (TradeOfferCalcInfo memory)
{
_validateTradeOfferETHAmount(id, seller, quantity);
return _tradeOffer(id, seller, quantity);
}
function tradeOffer(uint256 id, address seller, uint256 quantity, uint8 v, bytes32 r, bytes32 s)
external
payable
onlyValidTradeOffer(id, seller, quantity)
returns (TradeOfferCalcInfo memory)
{
_validateTradeOfferETHAmount(id, seller, quantity);
TradingPair memory tradingPair = _tradingPairs[_offers[seller][id].tradingPair];
require(tradingPair.takerAssetAddress == _tradingPairs[TradingPairs.ETH_VY].takerAssetAddress, "Must be [ETH_VY]");
VYToken takerAsset = VYToken(tradingPair.takerAssetAddress);
takerAsset.permit(_msgSender(), address(this), quantity, v, r, s);
return _tradeOffer(id, seller, quantity);
}
function estimateTradeOffer(uint256 id, address seller, uint256 quantity) external view onlyValidTradeOffer(id, seller, quantity) returns (TradeOfferCalcInfo memory) {
TradingPair memory tradingPair = _tradingPairs[_offers[seller][id].tradingPair];
uint256 maxInput = _offers[seller][id].quantity * _offers[seller][id].price / MULTIPLIER;
require(quantity <= maxInput, "Not enough to sell");
return _calcTradeOffer(tradingPair, quantity, _offers[seller][id].price);
}
function _tradeOffer(uint256 id, address seller, uint256 quantity) private returns (TradeOfferCalcInfo memory) {
TradingPair memory tradingPair = _tradingPairs[_offers[seller][id].tradingPair];
uint256 maxInput = _offers[seller][id].quantity * _offers[seller][id].price / MULTIPLIER;
require(quantity <= maxInput, "Not enough to sell");
TradeOfferCalcInfo memory calc = _calcTradeOffer(tradingPair, quantity, _offers[seller][id].price);
require(_offers[seller][id].quantity >= calc.amountOut, "Bad calculations");
_offers[seller][id].quantity -= calc.amountOut;
if (tradingPair.takerAssetAddress == address(0)) {
ERC20 makerAsset = ERC20(tradingPair.makerAssetAddress);
_transfer(seller, calc.makerReceives);
_ethComptroller.route{ value: calc.makerFee }();
makerAsset.transfer(_msgSender(), calc.takerReceives);
makerAsset.transfer(tradingPair.makerTreasuryAddress, calc.takerFee);
} else {
ERC20 takerAsset = _getSpendingTokenAndCheck(tradingPair.takerAssetAddress, quantity);
takerAsset.transferFrom(_msgSender(), address(this), calc.makerReceives);
takerAsset.transfer(seller, calc.makerReceives);
takerAsset.transferFrom(_msgSender(), tradingPair.takerTreasuryAddress, calc.makerFee);
_transfer(_msgSender(), calc.takerReceives);
_ethComptroller.route{ value: calc.takerFee }();
}
bool makerCloseout = (tradingPair.makerAssetAddress == address(0) && _offers[seller][id].quantity < MINIMUM_AUTOCLOSE_IN_ETH);
bool takerCloseout = (tradingPair.takerAssetAddress == address(0) && _offers[seller][id].quantity * _offers[seller][id].price / MULTIPLIER < MINIMUM_AUTOCLOSE_IN_ETH);
if (makerCloseout || takerCloseout) {
_closeOffer(id, seller);
}
emit TradeOffer(id, _msgSender(), calc.amountOut, quantity, _offers[seller][id].quantity, block.timestamp);
return calc;
}
function closeOffer(uint256 id) external onlyOpenOffer(id, _msgSender()) {
_closeOffer(id, _msgSender());
}
function closeOffer(address seller, uint256 id) external onlyOpenOffer(id, seller) onlyBackendAgents {
_closeOffer(id, seller);
}
function _pairExist(TradingPairs tradingPair) private view returns (bool) {
return _tradingPairs[tradingPair].makerAssetAddress != address(0) || _tradingPairs[tradingPair].takerAssetAddress != address(0);
}
function _isOfferActive(uint256 id, address seller) private view returns (bool) {
return _offers[seller][id].isOpen && _offers[seller][id].expiresAt > block.timestamp;
}
function _getSpendingTokenAndCheck(address assetAddress, uint256 quantity) private view returns (ERC20) {
ERC20 token = ERC20(assetAddress);
require(token.allowance(_msgSender(), address(this)) >= quantity, "Insufficient allowance");
require(token.balanceOf(_msgSender()) >= quantity, "Insufficient balance");
return token;
}
function _calcTradeOffer(TradingPair memory tradingPair, uint256 quantity, uint256 price) private pure returns (TradeOfferCalcInfo memory) {
uint256 buyQuantity = quantity * MULTIPLIER / price;
TradeOfferCalcInfo memory calc;
calc.amountOut = buyQuantity;
calc.makerFee = quantity * tradingPair.makerFeeRate / MULTIPLIER;
calc.takerFee = buyQuantity * tradingPair.takerFeeRate / MULTIPLIER;
calc.makerReceives = quantity - calc.makerFee;
calc.takerReceives = buyQuantity - calc.takerFee;
return calc;
}
function _closeOffer(uint256 id, address seller) private {
uint256 remainingQuantity = _offers[seller][id].quantity;
_offers[seller][id].isOpen = false;
if (remainingQuantity > 0) {
_offers[seller][id].quantity = 0;
address makerAssetAddress = _tradingPairs[_offers[seller][id].tradingPair].makerAssetAddress;
if (makerAssetAddress == address(0)) {
_transfer(seller, remainingQuantity);
} else {
ERC20 token = ERC20(makerAssetAddress);
token.transfer(seller, remainingQuantity);
}
}
emit CloseOffer(id, block.timestamp);
}
function updateAddresses() external override onlyRegistrar {
_vethRevenueCycleTreasury = VETHRevenueCycleTreasury(_registrar.getVETHRevenueCycleTreasury());
_initTradingPairs();
}
function _initTradingPairs() internal {
address vethRevenueCycleTreasury = _registrar.getVETHRevenueCycleTreasury();
address vyToken = _registrar.getVYToken();
_tradingPairs[TradingPairs.VY_ETH] = TradingPair(vyToken, address(0), vethRevenueCycleTreasury, address(_ethComptroller), VY_FEE, ETH_FEE);
_tradingPairs[TradingPairs.ETH_VY] = TradingPair(address(0), vyToken, address(_ethComptroller), vethRevenueCycleTreasury, ETH_FEE, VY_FEE);
}
function _transfer(address recipient, uint256 amount) private {
(bool sent,) = recipient.call{value: amount}("");
require(sent, "Failed to send Ether");
}
function _validateTradeOfferETHAmount(uint256 id, address seller, uint256 quantity) private {
if (_offers[seller][id].tradingPair == TradingPairs.VY_ETH) {
require(msg.value == quantity, "Invalid ETH amount sent");
} else {
require(msg.value == 0, "Invalid ETH amount sent");
}
}
}
文件 30 的 35:VETHRevenueCycleTreasury.sol
pragma solidity 0.8.18;
import { BackendAgent } from "../access/BackendAgent.sol";
import { VYToken } from "../token/VYToken.sol";
import { RegistrarClient } from "../RegistrarClient.sol";
import { RegistrarMigrator } from "../RegistrarMigrator.sol";
import { AdminGovernanceAgent } from "../access/AdminGovernanceAgent.sol";
import { Governable } from "../governance/Governable.sol";
import { VETHYieldRateTreasury } from "../treasury/VETHYieldRateTreasury.sol";
import { VYRevenueCycleCirculationTracker } from "./VYRevenueCycleCirculationTracker.sol";
import { Registrar } from "../Registrar.sol";
import { Router } from "../Router.sol";
contract VETHRevenueCycleTreasury is BackendAgent, RegistrarClient, RegistrarMigrator, AdminGovernanceAgent, Governable, VYRevenueCycleCirculationTracker {
uint256 private constant MULTIPLIER = 10**18;
uint256 public constant ETH_FEE = 20000000000000000;
uint256 public constant VY_FEE = 20000000000000000;
uint256 public constant CREATE_PRICE_FACTOR = 2000000000000000000;
uint256 public constant YIELD_RATE_FACTOR = 1030000000000000000;
VYToken internal _vyToken;
VETHYieldRateTreasury private _vethYRT;
Router private _ethComptroller;
address private _migration;
uint256 private _nonce = 0;
uint256 private _vyAllocatedInOffer = 0;
uint256 internal _initialYieldRate = 0;
struct Offer {
uint256 id;
uint256 quantity;
uint256 price;
bool isOpen;
}
mapping(uint256 => Offer) private _offers;
event CreateOffer(uint256 id, uint256 quantity, uint256 price, uint256 timestamp);
event TradeOffer(uint256 id, address buyer, uint256 sellerQuantity, uint256 buyerQuantity, uint256 unfilledQuantity, uint256 timestamp);
event CloseOffer(uint256 id, uint256 timestamp);
constructor(
address registrarAddress,
address ethComptrollerAddress_,
address[] memory adminAgents,
address[] memory backendAdminAgents,
address[] memory backendAgents,
address[] memory adminGovAgents,
uint256 initialYieldRate_,
uint256 initialCirculation
) RegistrarClient(registrarAddress)
RegistrarMigrator(registrarAddress, uint(Registrar.Contract.VETHRevenueCycleTreasury), adminAgents)
AdminGovernanceAgent(adminGovAgents)
VYRevenueCycleCirculationTracker(initialCirculation) {
require(ethComptrollerAddress_ != address(0), "Invalid address");
_ethComptroller = Router(payable(ethComptrollerAddress_));
_setBackendAdminAgents(backendAdminAgents);
_setBackendAgents(backendAgents);
_initialYieldRate = initialYieldRate_;
}
function getNonce() external view returns (uint256) {
return _nonce;
}
function getVYAllocatedInOffer() external view returns (uint256) {
return _vyAllocatedInOffer;
}
function getOffer(uint256 id) external view returns (Offer memory) {
return _offers[id];
}
function getMigration() external view returns (address) {
return _migration;
}
function getInitialYieldRate() external view returns (uint256) {
return _initialYieldRate;
}
function getYieldRate() external view returns (uint256) {
return _getYieldRate();
}
function getVETHCirculation() public view returns (uint256) {
return _getRevenueCycleCirculation();
}
function setMigration(address destination) external onlyGovernance {
_migration = destination;
}
function transferMigration(uint256 amount) external onlyAdminGovAgents {
require(_migration != address(0), "Migration not set");
require(_vyToken.balanceOf(address(this)) >= amount, "Insufficient balance");
_vyToken.transfer(_migration, amount);
}
function createOffer(uint256 quantity) external onlyBackendAgents {
require(quantity > 0, "Invalid quantity");
uint256 yieldRate = _getYieldRate();
require(yieldRate > 0, "Yield rate must be greater than zero");
uint256 price = CREATE_PRICE_FACTOR * MULTIPLIER / yieldRate;
Offer memory offer = _offers[_nonce];
if (offer.isOpen) {
_closeOffer(_nonce);
}
uint256 _vyBalance = _vyToken.balanceOf(address(this));
uint256 _desiredTotalVY = _vyAllocatedInOffer + quantity;
uint256 id = ++_nonce;
_offers[id] = Offer(id, quantity, price, true);
_vyAllocatedInOffer += quantity;
if (_desiredTotalVY > _vyBalance) {
uint256 amountToMint = _desiredTotalVY - _vyBalance;
_vyToken.mint(amountToMint);
}
emit CreateOffer(id, quantity, price, block.timestamp);
}
function tradeOffer(uint256 id) external payable {
require(msg.value > 0, "Invalid quantity");
require(_isOfferActive(id), "Invalid offer");
uint256 price = _offers[id].price;
uint256 maxInput = _offers[id].quantity * price / MULTIPLIER;
require(msg.value <= maxInput, "Not enough to sell");
uint256 buyQuantity = msg.value * MULTIPLIER / price;
require(_vyToken.balanceOf(address(this)) >= buyQuantity, "Not enough to sell");
uint256 yieldRate = _getYieldRate();
if (yieldRate > 0) {
uint256 limitYieldRate = YIELD_RATE_FACTOR * MULTIPLIER / yieldRate;
require(price >= limitYieldRate, "Price must be >= limitYieldRate");
}
require(_offers[id].quantity >= buyQuantity, "Bad calculations");
_offers[id].quantity -= buyQuantity;
_vyAllocatedInOffer -= buyQuantity;
uint256 makerFee = msg.value * VY_FEE / MULTIPLIER;
uint256 takerFee = buyQuantity * ETH_FEE / MULTIPLIER;
uint256 makerReceives = msg.value - makerFee;
uint256 takerReceives = buyQuantity - takerFee;
_transfer(address(_vethYRT), makerReceives);
_ethComptroller.route{ value: makerFee }();
_vyToken.transfer(_msgSender(), takerReceives);
emit TradeOffer(id, _msgSender(), buyQuantity, msg.value, _offers[id].quantity, block.timestamp);
}
function closeOffer(uint256 id) external onlyBackendAgents {
require(_isOfferActive(id), "Invalid offer");
_closeOffer(id);
}
function updateAddresses() external override onlyRegistrar {
_vyToken = VYToken(_registrar.getVYToken());
_vethYRT = VETHYieldRateTreasury(payable(_registrar.getVETHYieldRateTreasury()));
_updateGovernable(_registrar);
_updateVYCirculationHelper(_registrar);
}
function _isOfferActive(uint256 id) private view returns (bool) {
return _offers[id].isOpen;
}
function _getYieldRate() private view returns (uint256) {
uint256 circulation = getVETHCirculation();
uint256 treasuryValue = _vethYRT.getYieldRateTreasuryValue();
if (treasuryValue == 0) {
return 0;
}
if (circulation > 0) {
return MULTIPLIER * circulation / treasuryValue;
} else {
return _initialYieldRate;
}
}
function _transfer(address recipient, uint256 amount) private {
(bool sent,) = recipient.call{value: amount}("");
require(sent, "Failed to send Ether");
}
function _closeOffer(uint256 id) private {
_vyAllocatedInOffer -= _offers[id].quantity;
_offers[id].isOpen = false;
_offers[id].quantity = 0;
emit CloseOffer(id, block.timestamp);
}
}
文件 31 的 35:VETHReverseStakingTreasury.sol
pragma solidity 0.8.18;
import { BackendAgent } from "./access/BackendAgent.sol";
import { VYToken } from "./token/VYToken.sol";
import { VETHP2P } from "./exchange/VETHP2P.sol";
import { VETHRevenueCycleTreasury } from "./exchange/VETHRevenueCycleTreasury.sol";
import { VETHYieldRateTreasury } from "./treasury/VETHYieldRateTreasury.sol";
import { RegistrarClient } from "./RegistrarClient.sol";
import { AdminGovernanceAgent } from "./access/AdminGovernanceAgent.sol";
import { Governable } from "./governance/Governable.sol";
import { RegistrarMigrator } from "./RegistrarMigrator.sol";
import { Registrar } from "./Registrar.sol";
import { Router } from "./Router.sol";
contract VETHReverseStakingTreasury is BackendAgent, RegistrarClient, RegistrarMigrator, AdminGovernanceAgent, Governable {
uint256 private constant MINIMUM_REVERSE_STAKE_AUTOCLOSE = 100000000;
uint256 private constant MULTIPLIER = 10**18;
uint256 private constant DAY_IN_SECONDS = 86400;
bytes private constant ROUTE_SELECTOR = abi.encode(bytes4(keccak256("route()")));
struct TradeOfferVars {
uint256 maxInput;
uint256 ethFee;
uint256 vyFee;
uint256 vyOut;
}
struct CalcOfferRepayment {
uint256 effectiveETHPaidOff;
uint256 excessETH;
uint256 excessStakedVY;
uint256 vyToBurn;
bool isPaidOff;
}
struct RestakeVars {
uint256 newReverseStakeVY;
uint256 newReverseStakeClaimedYieldETH;
uint256 newReverseStakeId;
uint256 startAt;
uint256 endAt;
uint256 vyToBurn;
uint256 processingFeeETH;
uint256 yieldPayout;
uint256[] reverseStakeIds;
uint256 vyYieldRate;
}
struct MigrateReverseStakeVars {
address borrowerAddress;
uint256 stakedVY;
uint256 originalClaimedYieldETH;
uint256 currentClaimedYieldETH;
uint256 yieldRate;
uint256 startAt;
uint256 endAt;
uint256 lastPaidAt;
uint256 previousReverseStakeId;
uint256 termId;
}
uint256 public constant ETH_FEE = 20000000000000000;
uint256 public constant VY_FEE = 20000000000000000;
uint256 public constant OFFER_PRICE_YR_RATIO = 1020409000000000000;
uint256 public constant OFFER_NET_STAKE_RATIO = 980000000000000000;
uint256 public constant EXPIRES_IN = 30 days;
uint256 public constant MINIMUM_OFFER_AUTOCLOSE_IN_ETH = 500000000000000;
uint256 public constant MAX_RESTAKE_REVERSE_STAKES = 20;
enum DataTypes {
VY,
PERCENTAGE
}
VETHP2P private _vethP2P;
VETHRevenueCycleTreasury private _vethRevenueCycleTreasury;
VYToken private _vyToken;
VETHYieldRateTreasury private _vethYRT;
Router private _ethComptroller;
address private _migration;
uint256 private _reverseStakeTermsNonce = 0;
uint256 private _reverseStakesNonce = 0;
uint256 private _totalClaimedYieldETH = 0;
uint256 private _maxReverseStakes = 20;
bool private _reverseStakeExtendable = true;
struct ReverseStakeTerm {
uint256 dailyBurnRate;
uint256 durationInDays;
uint256 minimumReverseStakeETH;
uint256 processingFeePercentage;
uint256 extensionMinimumRemainingStake;
DataTypes extensionMinimumRemainingStakeType;
uint256 restakeMinimumPayout;
}
struct ReverseStake {
uint256 termId;
uint256 stakedVY;
uint256 originalClaimedYieldETH;
uint256 currentClaimedYieldETH;
uint256 yieldRate;
uint256 startAt;
uint256 endAt;
uint256 lastPaidAt;
}
struct Offer {
uint256 unfilledQuantity;
uint256 price;
uint256 maxClaimedYieldETH;
uint256 maxQuantity;
uint256 expiresAt;
bool isOpen;
}
mapping(uint256 => ReverseStakeTerm) private _reverseStakeTerms;
mapping(address => mapping(uint256 => ReverseStake)) private _reverseStakes;
mapping(address => uint256) private _openReverseStakes;
mapping(address => mapping(uint256 => Offer)) private _offers;
event CreateReverseStakeTerm(
uint256 termId,
uint256 dailyBurnRate,
uint256 durationInDays,
uint256 minimumReverseStakeETH,
uint256 processingFeePercentage,
uint256 extensionMinimumRemainingStake,
DataTypes extensionMinimumRemainingStakeType,
uint256 restakeMinimumPayout);
event CreateReverseStake(
address borrower,
uint256 reverseStakeId,
uint256 termId,
uint256 stakedVY,
uint256 originalClaimedYieldETH,
uint256 currentClaimedYieldETH,
uint256 yieldRate,
uint256 startAt,
uint256 endAt);
event ReturnETHToUnstake(
address borrower,
uint256 reverseStakeId,
uint256 ethAmount,
uint256 currentClaimedYieldETH,
uint256 stakedVY,
uint256 stakedVYReturned,
uint256 burnRatePaid,
uint256 paidAt
);
event MigrateReverseStake(
address borrower,
uint256 reverseStakeId,
uint256 termId,
uint256 stakedVY,
uint256 originalClaimedYieldETH,
uint256 currentClaimedYieldETH,
uint256 yieldRate,
uint256 startAt,
uint256 endAt,
uint256 previousReverseStakeId
);
event ExtendReverseStake(address borrower, uint256 reverseStakeId, uint256 endAt, uint256 burnRatePaid);
event CloseReverseStake(address borrower, uint256 reverseStakeId, uint256 stakeTransferred);
event CreateOffer(address borrower, uint256 reverseStakeId, uint256 quantity, uint256 price, uint256 expiresAt, uint256 timestamp);
event TradeOffer(
address borrower,
uint256 reverseStakeId,
address buyer,
uint256 sellerQuantity,
uint256 buyerQuantity,
uint256 unfilledQuantity,
uint256 excessETH,
uint256 timestamp
);
event CloseOffer(address borrower, uint256 reverseStakeId, uint256 timestamp);
event Restake(
address borrower,
uint256 reverseStakeId,
uint256 termId,
uint256 stakedVY,
uint256 originalClaimedYieldETH,
uint256 currentClaimedYieldETH,
uint256 yieldRate,
uint256 startAt,
uint256 endAt,
uint256 yieldPayout,
uint256[] previousReverseStakeIds,
uint256 burnRatePaid
);
constructor(
address registrarAddress,
address ethComptrollerAddress_,
address[] memory adminGovAgents,
address[] memory backendAdminAgents,
address[] memory backendAgents,
address[] memory adminAgents
) RegistrarClient(registrarAddress)
RegistrarMigrator(registrarAddress, uint(Registrar.Contract.VETHReverseStakingTreasury), adminAgents)
AdminGovernanceAgent(adminGovAgents) {
require(ethComptrollerAddress_ != address(0), "Invalid address");
_ethComptroller = Router(payable(ethComptrollerAddress_));
_setBackendAdminAgents(backendAdminAgents);
_setBackendAgents(backendAgents);
}
modifier onlyActiveReverseStake(address borrower, uint256 reverseStakeId) {
_checkValidReverseStake(_reverseStakes[borrower][reverseStakeId].startAt > 0);
_checkActiveReverseStake(isReverseStakeActive(borrower, reverseStakeId));
_;
}
modifier onlyActiveOffer(address borrower, uint256 reverseStakeId) {
require(_offers[borrower][reverseStakeId].isOpen && _offers[borrower][reverseStakeId].expiresAt > block.timestamp, "Invalid offer");
_;
}
modifier onlyOpenOffer(uint256 id, address borrower) {
require(_offers[borrower][id].isOpen, "Offer must be open in order to close");
_;
}
function setupInitialReverseStakeTerm(
uint256 dailyBurnRate,
uint256 durationInDays,
uint256 minimumReverseStakeETH,
uint256 processingFeePercentage,
uint256 extensionMinimumRemainingStake,
DataTypes extensionMinimumRemainingStakeType,
uint256 restakeMinimumPayout
) external onlyBackendAdminAgents {
require(_reverseStakeTermsNonce == 0, "Reverse stake terms already set up");
_createNewReverseStakeTerm(
dailyBurnRate,
durationInDays,
minimumReverseStakeETH,
processingFeePercentage,
extensionMinimumRemainingStake,
extensionMinimumRemainingStakeType,
restakeMinimumPayout
);
}
function createNewReverseStakeTerm(
uint256 dailyBurnRate,
uint256 durationInDays,
uint256 minimumReverseStakeETH,
uint256 processingFeePercentage,
uint256 extensionMinimumRemainingStake,
DataTypes extensionMinimumRemainingStakeType,
uint256 restakeMinimumPayout
) external onlyBackendAdminAgents {
_createNewReverseStakeTerm(
dailyBurnRate,
durationInDays,
minimumReverseStakeETH,
processingFeePercentage,
extensionMinimumRemainingStake,
extensionMinimumRemainingStakeType,
restakeMinimumPayout
);
}
function getTotalClaimedYield() external view returns (uint256) {
return _totalClaimedYieldETH;
}
function getReverseStake(address borrower, uint256 reverseStakeId) external view returns (ReverseStake memory) {
return _reverseStakes[borrower][reverseStakeId];
}
function isReverseStakeActive(address borrower, uint256 reverseStakeId) public view returns (bool) {
return !_isReverseStakeExpired(borrower, reverseStakeId) && _reverseStakes[borrower][reverseStakeId].currentClaimedYieldETH > 0;
}
function getReverseStakeTerm(uint256 termId) external view returns (ReverseStakeTerm memory) {
return _reverseStakeTerms[termId];
}
function getCurrentReverseStakeTerm() external view returns (ReverseStakeTerm memory) {
return _reverseStakeTerms[_reverseStakeTermsNonce];
}
function getCurrentReverseStakeTermId() external view returns (uint256) {
return _reverseStakeTermsNonce;
}
function ethToBurn(address borrower, uint256 reverseStakeId) external view returns (uint256) {
return _ethToBurn(borrower, reverseStakeId);
}
function vyToBurn(address borrower, uint256 reverseStakeId) external view returns (uint256) {
return _vyToBurn(borrower, reverseStakeId);
}
function getStakedVYForReverseStakeETH(uint256 ethAmount) external view returns (uint256) {
return _getStakedVYForReverseStakeETH(ethAmount);
}
function getMaxReverseStakes() external view returns (uint256) {
return _maxReverseStakes;
}
function getReverseStakesNonce() external view returns (uint256) {
return _reverseStakesNonce;
}
function setMaxReverseStakes(uint256 maxReverseStakes_) external onlyBackendAdminAgents {
_maxReverseStakes = maxReverseStakes_;
}
function isReverseStakeExtendable() external view returns (bool) {
return _reverseStakeExtendable;
}
function toggleReverseStakeExtension(bool enabled) external onlyAdminGovAgents {
_reverseStakeExtendable = enabled;
}
function getMigration() external view returns (address) {
return _migration;
}
function setMigration(address destination) external onlyGovernance {
_migration = destination;
}
function transferMigration(uint256 amount) external onlyAdminGovAgents {
require(_migration != address(0), "Migration not set");
_checkSufficientBalance(_vyToken.balanceOf(address(this)) >= amount);
_transferVY(_migration, amount);
}
function createReverseStake(uint256 termId, uint256 ethAmount, uint256 vyAmount) external {
require(_vyToken.allowance(_msgSender(), address(this)) >= vyAmount, "Insufficient allowance");
require(_vyToken.balanceOf(_msgSender()) >= vyAmount, "Insufficient balance");
uint256 minStake = _createReverseStakePrerequisite(termId, ethAmount, vyAmount);
_createReverseStake(ethAmount, minStake);
}
function createReverseStake(uint256 termId, uint256 ethAmount, uint256 vyAmount, uint8 v, bytes32 r, bytes32 s) external {
uint256 minStake = _createReverseStakePrerequisite(termId, ethAmount, vyAmount);
_vyToken.permit(_msgSender(), address(this), vyAmount, v, r, s);
_createReverseStake(ethAmount, minStake);
}
function _createReverseStake(uint256 ethAmount, uint256 stakedVY) private {
ReverseStakeTerm memory reverseStakeTerm = _reverseStakeTerms[_reverseStakeTermsNonce];
require(ethAmount >= reverseStakeTerm.minimumReverseStakeETH, "Minimum reverse stake ETH not met");
uint256 reverseStakeId = ++_reverseStakesNonce;
uint256 ethComptrollerReceives = ethAmount * reverseStakeTerm.processingFeePercentage / MULTIPLIER;
uint256 borrowerReceives = ethAmount - ethComptrollerReceives;
uint256 startAt = block.timestamp;
uint256 endAt = startAt + reverseStakeTerm.durationInDays * DAY_IN_SECONDS;
uint256 vyYieldRate = _vethRevenueCycleTreasury.getYieldRate();
_reverseStakes[_msgSender()][reverseStakeId] = ReverseStake(_reverseStakeTermsNonce, stakedVY, ethAmount, ethAmount, vyYieldRate, startAt, endAt, 0);
_openReverseStakes[_msgSender()]++;
_totalClaimedYieldETH += ethAmount;
_vyToken.transferFrom(_msgSender(), address(this), stakedVY);
_vethYRT.reverseStakingTransfer(_msgSender(), borrowerReceives);
_vethYRT.reverseStakingRoute(address(_ethComptroller), ethComptrollerReceives, ROUTE_SELECTOR);
emit CreateReverseStake(_msgSender(), reverseStakeId, _reverseStakeTermsNonce, stakedVY, ethAmount, ethAmount, vyYieldRate, startAt, endAt);
}
function returnETHToUnstake(uint256 reverseStakeId) external payable onlyActiveReverseStake(_msgSender(), reverseStakeId) {
require(msg.value > 0, "Zero ETH amount sent");
_checkActiveOffer(_offers[_msgSender()][reverseStakeId].isOpen);
ReverseStake storage reverseStake = _reverseStakes[_msgSender()][reverseStakeId];
uint256 vyToBurn_ = _vyToBurn(_msgSender(), reverseStakeId);
require(reverseStake.stakedVY >= vyToBurn_, "Not enough staked VY to burn");
reverseStake.stakedVY -= vyToBurn_;
uint256 excessETH = 0;
uint256 stakedVYReturned = 0;
uint256 ethAmount = msg.value;
if (ethAmount > reverseStake.currentClaimedYieldETH) {
excessETH = ethAmount - reverseStake.currentClaimedYieldETH;
ethAmount = reverseStake.currentClaimedYieldETH;
}
if (reverseStake.currentClaimedYieldETH == ethAmount) {
stakedVYReturned = reverseStake.stakedVY;
_decrementOpenReverseStakesAndCloseOffer(_msgSender(), reverseStakeId, 0);
} else {
stakedVYReturned = reverseStake.stakedVY * ethAmount / reverseStake.currentClaimedYieldETH;
}
reverseStake.currentClaimedYieldETH -= ethAmount;
reverseStake.stakedVY -= stakedVYReturned;
reverseStake.lastPaidAt = block.timestamp;
_totalClaimedYieldETH -= ethAmount;
_transferToRevenueCycleTreasury(vyToBurn_);
_transferVY(_msgSender(), stakedVYReturned);
_transfer(address(_vethYRT), ethAmount);
if (excessETH > 0) {
_transfer(_msgSender(), excessETH);
}
emit ReturnETHToUnstake(
_msgSender(),
reverseStakeId,
ethAmount,
reverseStake.currentClaimedYieldETH,
reverseStake.stakedVY,
stakedVYReturned,
vyToBurn_,
reverseStake.lastPaidAt
);
}
function extendReverseStake(uint256 reverseStakeId) external payable onlyActiveReverseStake(_msgSender(), reverseStakeId) {
require(_reverseStakeExtendable, "Extend reverse stakes disabled");
ReverseStake storage reverseStake = _reverseStakes[_msgSender()][reverseStakeId];
ReverseStakeTerm memory reverseStakeTerm = _reverseStakeTerms[reverseStake.termId];
uint256 vyToBurn_ = _vyToBurn(_msgSender(), reverseStakeId);
require(reverseStake.stakedVY >= vyToBurn_, "Not enough staked VY to burn");
reverseStake.stakedVY -= vyToBurn_;
uint256 originalStakedVY = reverseStake.originalClaimedYieldETH * reverseStake.yieldRate / MULTIPLIER;
require(reverseStake.stakedVY >= _getRemainingStakedVYExtensionLimit(reverseStake.termId, originalStakedVY), "Staked VY too low to extend");
uint256 processingFee = reverseStake.currentClaimedYieldETH * reverseStakeTerm.processingFeePercentage / MULTIPLIER;
require(msg.value == processingFee, "Invalid ETH amount sent");
reverseStake.lastPaidAt = block.timestamp;
reverseStake.endAt = block.timestamp + reverseStakeTerm.durationInDays * DAY_IN_SECONDS;
_ethComptroller.route{ value: processingFee }();
_transferToRevenueCycleTreasury(vyToBurn_);
emit ExtendReverseStake(_msgSender(), reverseStakeId, reverseStake.endAt, vyToBurn_);
}
function restake(uint256[] memory reverseStakeIds) external {
address borrower = _msgSender();
require(reverseStakeIds.length > 0 && reverseStakeIds.length <= MAX_RESTAKE_REVERSE_STAKES, "Invalid number of reverseStakes");
uint256 firstReverseStakeTermId;
uint256 totalCurrentClaimedYieldETH;
RestakeVars memory reverseStakeData = RestakeVars(0, 0, 0, 0, 0, 0, 0, 0, new uint256[](reverseStakeIds.length), 0);
for (uint i = 0; i < reverseStakeIds.length; i++) {
uint256 reverseStakeId = reverseStakeIds[i];
reverseStakeData.reverseStakeIds[i] = reverseStakeId;
_checkValidReverseStake(_reverseStakes[borrower][reverseStakeId].startAt > 0);
_checkActiveReverseStake(isReverseStakeActive(borrower, reverseStakeId));
_checkActiveOffer(_offers[borrower][reverseStakeId].isOpen);
ReverseStake storage reverseStake = _reverseStakes[borrower][reverseStakeId];
if (i == 0) {
firstReverseStakeTermId = reverseStake.termId;
} else {
require(reverseStake.termId == firstReverseStakeTermId, "Reverse stakes must have same reverse stake term");
}
uint256 vyToBurn_ = _vyToBurn(borrower, reverseStakeId);
reverseStakeData.vyToBurn += vyToBurn_;
require(reverseStake.stakedVY >= vyToBurn_, "Not enough staked VY to burn");
uint256 newReverseStakeVY = reverseStake.stakedVY - vyToBurn_;
reverseStakeData.newReverseStakeVY += newReverseStakeVY;
totalCurrentClaimedYieldETH += reverseStake.currentClaimedYieldETH;
reverseStake.stakedVY = 0;
reverseStake.currentClaimedYieldETH = 0;
if (_openReverseStakes[borrower] > 0) {
_openReverseStakes[borrower]--;
}
}
reverseStakeData.vyYieldRate = _vethRevenueCycleTreasury.getYieldRate();
reverseStakeData.newReverseStakeClaimedYieldETH = reverseStakeData.newReverseStakeVY * MULTIPLIER / reverseStakeData.vyYieldRate;
reverseStakeData.newReverseStakeId = ++_reverseStakesNonce;
reverseStakeData.startAt = block.timestamp;
reverseStakeData.endAt = reverseStakeData.startAt + _reverseStakeTerms[firstReverseStakeTermId].durationInDays * DAY_IN_SECONDS;
_reverseStakes[borrower][reverseStakeData.newReverseStakeId] = ReverseStake(
firstReverseStakeTermId,
reverseStakeData.newReverseStakeVY,
reverseStakeData.newReverseStakeClaimedYieldETH,
reverseStakeData.newReverseStakeClaimedYieldETH,
reverseStakeData.vyYieldRate,
reverseStakeData.startAt,
reverseStakeData.endAt,
0);
_openReverseStakes[borrower]++;
require(reverseStakeData.newReverseStakeClaimedYieldETH >= totalCurrentClaimedYieldETH, "Restaked reverseStakes must increase in value");
reverseStakeData.yieldPayout = reverseStakeData.newReverseStakeClaimedYieldETH - totalCurrentClaimedYieldETH;
_totalClaimedYieldETH += reverseStakeData.yieldPayout;
reverseStakeData.processingFeeETH = reverseStakeData.yieldPayout * _reverseStakeTerms[firstReverseStakeTermId].processingFeePercentage / MULTIPLIER;
require(reverseStakeData.yieldPayout >= _reverseStakeTerms[firstReverseStakeTermId].restakeMinimumPayout, "Minimum yield payout not met");
reverseStakeData.yieldPayout -= reverseStakeData.processingFeeETH;
_vethYRT.reverseStakingTransfer(borrower, reverseStakeData.yieldPayout);
_vethYRT.reverseStakingRoute(address(_ethComptroller), reverseStakeData.processingFeeETH, ROUTE_SELECTOR);
_transferToRevenueCycleTreasury(reverseStakeData.vyToBurn);
emit Restake(
borrower,
reverseStakeData.newReverseStakeId,
_reverseStakeTermsNonce,
reverseStakeData.newReverseStakeVY,
reverseStakeData.newReverseStakeClaimedYieldETH,
reverseStakeData.newReverseStakeClaimedYieldETH,
reverseStakeData.vyYieldRate,
reverseStakeData.startAt,
reverseStakeData.endAt,
reverseStakeData.yieldPayout,
reverseStakeData.reverseStakeIds,
reverseStakeData.vyToBurn
);
}
function closeReverseStake(address borrower, uint256 reverseStakeId) external onlyBackendAgents {
ReverseStake storage reverseStake = _reverseStakes[borrower][reverseStakeId];
_checkValidReverseStake(reverseStake.startAt > 0);
require(!isReverseStakeActive(borrower, reverseStakeId), "ReverseStake is still active");
require(reverseStake.stakedVY > 0 && reverseStake.currentClaimedYieldETH > 0, "ReverseStake is already closed");
uint256 stakedVY = reverseStake.stakedVY;
uint256 currentClaimedYieldETH = reverseStake.currentClaimedYieldETH;
_decrementOpenReverseStakesAndCloseOffer(borrower, reverseStakeId, stakedVY);
reverseStake.stakedVY = 0;
reverseStake.currentClaimedYieldETH = 0;
_totalClaimedYieldETH -= currentClaimedYieldETH;
_transferToRevenueCycleTreasury(stakedVY);
}
function createOffer(uint256 reverseStakeId, uint256 quantity, uint256 price) external onlyActiveReverseStake(_msgSender(), reverseStakeId) {
_checkValidQuantity(quantity);
require(!_offers[_msgSender()][reverseStakeId].isOpen, "Limit one offer per reverseStake");
ReverseStake memory reverseStake = _reverseStakes[_msgSender()][reverseStakeId];
uint256 vyToBurn_ = _vyToBurn(_msgSender(), reverseStakeId);
require(reverseStake.stakedVY >= vyToBurn_, "Not enough staked VY");
uint256 maximumQuantity = (reverseStake.stakedVY - vyToBurn_) * OFFER_NET_STAKE_RATIO / MULTIPLIER;
require(quantity <= maximumQuantity, "Quantity exceeds limit");
uint256 minPrice = reverseStake.currentClaimedYieldETH * OFFER_PRICE_YR_RATIO / maximumQuantity;
_checkMinPrice(price >= minPrice);
uint256 adjustedYieldRate = _vethRevenueCycleTreasury.getYieldRate() * MULTIPLIER / OFFER_PRICE_YR_RATIO;
require((MULTIPLIER * MULTIPLIER / price) <= adjustedYieldRate, "Price too low");
uint256 expiresAt = block.timestamp + EXPIRES_IN;
require(reverseStake.endAt > expiresAt, "ReverseStake is about to expire");
_offers[_msgSender()][reverseStakeId] = Offer(quantity, price, reverseStake.currentClaimedYieldETH, maximumQuantity, expiresAt, true);
emit CreateOffer(_msgSender(), reverseStakeId, quantity, price, expiresAt, block.timestamp);
}
function tradeOffer(address borrower, uint256 reverseStakeId) external payable onlyActiveOffer(borrower, reverseStakeId) {
_checkValidQuantity(msg.value);
Offer storage offer = _offers[borrower][reverseStakeId];
ReverseStake storage reverseStake = _reverseStakes[borrower][reverseStakeId];
TradeOfferVars memory info;
info.maxInput = offer.unfilledQuantity * offer.price / MULTIPLIER;
_checkEnoughAmountToSell(msg.value <= info.maxInput);
info.ethFee = msg.value * ETH_FEE / MULTIPLIER;
info.vyFee = msg.value * VY_FEE / offer.price;
info.vyOut = msg.value * MULTIPLIER / offer.price;
CalcOfferRepayment memory calc = _payReverseStakeVY(
borrower,
reverseStakeId,
info.vyOut,
offer.maxClaimedYieldETH,
offer.maxQuantity,
msg.value - info.ethFee
);
if (!calc.isPaidOff) {
if (info.vyOut > offer.unfilledQuantity) {
info.vyOut = offer.unfilledQuantity;
}
offer.unfilledQuantity -= info.vyOut;
bool takerCloseout = (offer.unfilledQuantity * offer.price / MULTIPLIER) < MINIMUM_OFFER_AUTOCLOSE_IN_ETH;
if (takerCloseout) {
_closeOffer(borrower, reverseStakeId);
}
}
_totalClaimedYieldETH -= calc.effectiveETHPaidOff;
_transferToRevenueCycleTreasury(info.vyFee + calc.vyToBurn);
_transferVY(_msgSender(), info.vyOut - info.vyFee);
_ethComptroller.route{ value: info.ethFee }();
_transfer(address(_vethYRT), msg.value - calc.excessETH - info.ethFee);
if (calc.excessETH > 0) {
_transfer(borrower, calc.excessETH);
}
if (calc.excessStakedVY > 0) {
_transferVY(borrower, calc.excessStakedVY);
}
emit TradeOffer(borrower, reverseStakeId, _msgSender(), info.vyOut, msg.value, offer.unfilledQuantity, calc.excessETH, reverseStake.lastPaidAt);
emit ReturnETHToUnstake(
borrower,
reverseStakeId,
calc.effectiveETHPaidOff,
reverseStake.currentClaimedYieldETH,
reverseStake.stakedVY,
0,
calc.vyToBurn,
reverseStake.lastPaidAt
);
}
function tradeStakedVY(uint256 reverseStakeId, uint256 offerId, address seller, uint256 amountVY) external onlyActiveReverseStake(_msgSender(), reverseStakeId) {
_tradeStakedVYPrerequisite(reverseStakeId, amountVY);
ReverseStake storage reverseStake = _reverseStakes[_msgSender()][reverseStakeId];
VETHP2P.Offer memory offer = _vethP2P.getOffer(offerId, seller);
require(offer.isOpen == true && offer.quantity > 0, "Offer is closed or has zero quantity");
uint256 vyToBurn_ = _vyToBurn(_msgSender(), reverseStakeId);
uint256 maxPrice = reverseStake.currentClaimedYieldETH * OFFER_PRICE_YR_RATIO / (reverseStake.stakedVY - vyToBurn_);
maxPrice = MULTIPLIER * MULTIPLIER / maxPrice;
_checkMinPrice(offer.price <= maxPrice);
_vyToken.approve(address(_vethP2P), amountVY);
VETHP2P.TradeOfferCalcInfo memory calc = _vethP2P.estimateTradeOffer(offerId, seller, amountVY);
CalcOfferRepayment memory reverseStakeCalcs = _payReverseStakeVY(
_msgSender(),
reverseStakeId,
amountVY,
reverseStake.currentClaimedYieldETH,
reverseStake.stakedVY - vyToBurn_,
calc.amountOut - calc.takerFee
);
_totalClaimedYieldETH -= reverseStakeCalcs.effectiveETHPaidOff;
VETHP2P.TradeOfferCalcInfo memory realCalc = _vethP2P.tradeOffer(offerId, seller, amountVY);
require(calc.amountOut == realCalc.amountOut, "amountOut does not match");
_transferToRevenueCycleTreasury(reverseStakeCalcs.vyToBurn);
_transfer(address(_vethYRT), realCalc.amountOut - reverseStakeCalcs.excessETH - realCalc.takerFee);
if (reverseStakeCalcs.excessETH > 0) {
_transfer(_msgSender(), reverseStakeCalcs.excessETH);
}
if (reverseStakeCalcs.excessStakedVY > 0) {
_transferVY(_msgSender(), reverseStakeCalcs.excessStakedVY);
}
emit ReturnETHToUnstake(
_msgSender(),
reverseStakeId,
reverseStakeCalcs.effectiveETHPaidOff,
reverseStake.currentClaimedYieldETH,
reverseStake.stakedVY,
0,
reverseStakeCalcs.vyToBurn,
reverseStake.lastPaidAt
);
}
function closeOffer(uint256 reverseStakeId) external onlyOpenOffer(reverseStakeId, _msgSender()) {
_closeOffer(_msgSender(), reverseStakeId);
}
function closeOffer(address borrower, uint256 reverseStakeId) external onlyOpenOffer(reverseStakeId, borrower) onlyBackendAgents {
_closeOffer(borrower, reverseStakeId);
}
function getOffer(address borrower, uint256 reverseStakeId) external view returns (Offer memory) {
return _offers[borrower][reverseStakeId];
}
function updateAddresses() external override onlyRegistrar {
_vethP2P = VETHP2P(_registrar.getVETHP2P());
_vethRevenueCycleTreasury = VETHRevenueCycleTreasury(_registrar.getVETHRevenueCycleTreasury());
_vyToken = VYToken(_registrar.getVYToken());
_vethYRT = VETHYieldRateTreasury(payable(_registrar.getVETHYieldRateTreasury()));
_updateGovernable(_registrar);
}
function _migrateReverseStake(
address borrowerAddress,
uint256 stakedVY,
uint256 originalClaimedYieldETH,
uint256 currentClaimedYieldETH,
uint256 yieldRate,
uint256 startAt,
uint256 endAt,
uint256 lastPaidAt,
uint256 previousReverseStakeId,
uint256 termId
) private {
require(startAt > 0, "Previous reverseStake invalid");
uint256 reverseStakeId = ++_reverseStakesNonce;
_reverseStakes[borrowerAddress][reverseStakeId] = ReverseStake(termId, stakedVY, originalClaimedYieldETH, currentClaimedYieldETH, yieldRate, startAt, endAt, lastPaidAt);
_openReverseStakes[borrowerAddress]++;
_totalClaimedYieldETH += currentClaimedYieldETH;
emit MigrateReverseStake(borrowerAddress, reverseStakeId, termId, stakedVY, originalClaimedYieldETH, currentClaimedYieldETH, yieldRate, startAt, endAt, previousReverseStakeId);
}
function migrateReverseStakes(
MigrateReverseStakeVars[] calldata reverseStakeDataArray
) external onlyBackendAgents onlyUnfinalized {
for (uint i = 0; i < reverseStakeDataArray.length; i++) {
_migrateReverseStake(
reverseStakeDataArray[i].borrowerAddress,
reverseStakeDataArray[i].stakedVY,
reverseStakeDataArray[i].originalClaimedYieldETH,
reverseStakeDataArray[i].currentClaimedYieldETH,
reverseStakeDataArray[i].yieldRate,
reverseStakeDataArray[i].startAt,
reverseStakeDataArray[i].endAt,
reverseStakeDataArray[i].lastPaidAt,
reverseStakeDataArray[i].previousReverseStakeId,
reverseStakeDataArray[i].termId
);
}
}
function _createNewReverseStakeTerm(
uint256 dailyBurnRate,
uint256 durationInDays,
uint256 minimumReverseStakeETH,
uint256 processingFeePercentage,
uint256 extensionMinimumRemainingStake,
DataTypes extensionMinimumRemainingStakeType,
uint256 restakeMinimumPayout
) private {
require(extensionMinimumRemainingStakeType == DataTypes.PERCENTAGE || extensionMinimumRemainingStakeType == DataTypes.VY, "Invalid type");
_reverseStakeTerms[++_reverseStakeTermsNonce] = ReverseStakeTerm(
dailyBurnRate,
durationInDays,
minimumReverseStakeETH,
processingFeePercentage,
extensionMinimumRemainingStake,
extensionMinimumRemainingStakeType,
restakeMinimumPayout
);
emit CreateReverseStakeTerm(
_reverseStakeTermsNonce,
dailyBurnRate,
durationInDays,
minimumReverseStakeETH,
processingFeePercentage,
extensionMinimumRemainingStake,
extensionMinimumRemainingStakeType,
restakeMinimumPayout
);
}
function _getStakedVYForReverseStakeETH(uint256 ethAmount) private view returns (uint256) {
uint256 vyYieldRate = _vethRevenueCycleTreasury.getYieldRate();
return vyYieldRate * ethAmount / MULTIPLIER;
}
function _isReverseStakeExpired(address borrower, uint256 reverseStakeId) private view returns (bool) {
return _reverseStakes[borrower][reverseStakeId].endAt < block.timestamp;
}
function _daysElapsed(uint256 startAt, uint256 lastPaidAt) private view returns (uint256) {
uint256 currentTime = block.timestamp;
if (lastPaidAt > 0) {
uint256 daysTotal = (currentTime - startAt) / DAY_IN_SECONDS;
uint256 daysPaid = (lastPaidAt - startAt) / DAY_IN_SECONDS;
return daysTotal - daysPaid;
} else {
return (currentTime - startAt) / DAY_IN_SECONDS;
}
}
function _getRemainingStakedVYExtensionLimit(uint256 termId, uint256 originalStakedVY) private view returns (uint256) {
ReverseStakeTerm memory reverseStakeTerm = _reverseStakeTerms[termId];
if (reverseStakeTerm.extensionMinimumRemainingStakeType == DataTypes.VY) {
return reverseStakeTerm.extensionMinimumRemainingStake;
} else if (reverseStakeTerm.extensionMinimumRemainingStakeType == DataTypes.PERCENTAGE) {
return originalStakedVY * reverseStakeTerm.extensionMinimumRemainingStake / MULTIPLIER;
} else {
return 0;
}
}
function _ethToBurn(address borrower, uint256 reverseStakeId) private view returns (uint256) {
ReverseStake memory reverseStake = _reverseStakes[borrower][reverseStakeId];
ReverseStakeTerm memory reverseStakeTerm = _reverseStakeTerms[reverseStake.termId];
uint256 daysElapsed = _daysElapsed(reverseStake.startAt, reverseStake.lastPaidAt);
return reverseStake.currentClaimedYieldETH * reverseStakeTerm.dailyBurnRate * daysElapsed / MULTIPLIER;
}
function _vyToBurn(address borrower, uint256 reverseStakeId) private view returns (uint256) {
uint256 ethToBurn_ = _ethToBurn(borrower, reverseStakeId);
uint256 vyYieldRate = _vethRevenueCycleTreasury.getYieldRate();
return ethToBurn_ * vyYieldRate / MULTIPLIER;
}
function _payReverseStakeVY(address borrower, uint256 reverseStakeId, uint256 vyToTrade, uint256 maxClaimedYieldETH, uint256 maxVY, uint256 amountETH) private returns (CalcOfferRepayment memory) {
ReverseStake storage reverseStake = _reverseStakes[borrower][reverseStakeId];
CalcOfferRepayment memory calc;
calc.effectiveETHPaidOff = vyToTrade * maxClaimedYieldETH / maxVY;
if (amountETH > calc.effectiveETHPaidOff) {
calc.excessETH = amountETH - calc.effectiveETHPaidOff;
}
calc.vyToBurn = _vyToBurn(borrower, reverseStakeId);
require(reverseStake.stakedVY >= vyToTrade + calc.vyToBurn, "Not enough staked VY");
reverseStake.stakedVY -= vyToTrade + calc.vyToBurn;
if (calc.effectiveETHPaidOff > reverseStake.currentClaimedYieldETH) {
calc.effectiveETHPaidOff = reverseStake.currentClaimedYieldETH;
}
if (reverseStake.currentClaimedYieldETH > calc.effectiveETHPaidOff &&
(reverseStake.currentClaimedYieldETH - calc.effectiveETHPaidOff <= MINIMUM_REVERSE_STAKE_AUTOCLOSE)) {
calc.effectiveETHPaidOff = reverseStake.currentClaimedYieldETH;
}
if (calc.effectiveETHPaidOff == reverseStake.currentClaimedYieldETH) {
calc.isPaidOff = true;
_decrementOpenReverseStakesAndCloseOffer(borrower, reverseStakeId, 0);
if (reverseStake.stakedVY > 0) {
calc.excessStakedVY = reverseStake.stakedVY;
reverseStake.stakedVY = 0;
}
}
reverseStake.currentClaimedYieldETH -= calc.effectiveETHPaidOff;
reverseStake.lastPaidAt = block.timestamp;
return calc;
}
function _createReverseStakePrerequisite(uint256 termId, uint256 ethAmount, uint256 vyAmount) private view returns (uint256) {
require(termId == _reverseStakeTermsNonce, "Invalid reverse stake term specified");
require(_openReverseStakes[_msgSender()] < _maxReverseStakes, "Maximum reverse stakes reached");
uint256 minStake = _getStakedVYForReverseStakeETH(ethAmount);
require(vyAmount >= minStake, "vyAmount too low based on yield rate");
return minStake;
}
function _tradeStakedVYPrerequisite(uint256 reverseStakeId, uint256 amountVY) private view {
_checkValidQuantity(amountVY);
Offer memory offer = _offers[_msgSender()][reverseStakeId];
_checkActiveOffer(offer.isOpen);
ReverseStake memory reverseStake = _reverseStakes[_msgSender()][reverseStakeId];
uint256 vyToBurn_ = _vyToBurn(_msgSender(), reverseStakeId);
require(reverseStake.stakedVY >= vyToBurn_, "Not enough staked VY");
uint256 remainingStake = reverseStake.stakedVY - vyToBurn_;
_checkEnoughAmountToSell(amountVY <= remainingStake);
}
function _transferToRevenueCycleTreasury(uint256 amount) private {
_transferVY(address(_vethRevenueCycleTreasury), amount);
}
function _decrementOpenReverseStakesAndCloseOffer(address borrower, uint256 reverseStakeId, uint256 stakeTransferred) internal {
if (_openReverseStakes[borrower] > 0) {
_openReverseStakes[borrower]--;
}
if (_offers[borrower][reverseStakeId].isOpen) {
_closeOffer(borrower, reverseStakeId);
}
emit CloseReverseStake(borrower, reverseStakeId, stakeTransferred);
}
function _closeOffer(address borrower, uint256 reverseStakeId) internal {
delete _offers[borrower][reverseStakeId];
emit CloseOffer(borrower, reverseStakeId, block.timestamp);
}
function _transferVY(address recipient, uint256 amount) private {
if (amount > 0) {
_vyToken.transfer(recipient, amount);
}
}
function _checkActiveOffer(bool isOpen) private pure {
require(!isOpen, "Active offer found");
}
function _checkMinPrice(bool minPriceMet) private pure {
require(minPriceMet, "Minimum price not met");
}
function _checkValidQuantity(uint256 amount) private pure {
require(amount > 0, "Invalid quantity");
}
function _checkEnoughAmountToSell(bool isEnough) private pure {
require(isEnough, "Not enough to sell");
}
function _checkSufficientBalance(bool isufficient) private pure {
require(isufficient, "Insufficient balance");
}
function _checkValidReverseStake(bool isValid) private pure {
require(isValid, "Invalid reverseStake");
}
function _checkActiveReverseStake(bool isActive) private pure {
require(isActive, "ReverseStake is not active");
}
function _transfer(address recipient, uint256 amount) private {
(bool sent,) = recipient.call{value: amount}("");
require(sent, "Failed to send Ether");
}
receive() external payable {}
}
文件 32 的 35:VETHYieldRateTreasury.sol
pragma solidity 0.8.18;
import { AdminGovernanceAgent } from "../access/AdminGovernanceAgent.sol";
import { Governable } from "../governance/Governable.sol";
import { VETHReverseStakingTreasury } from "../VETHReverseStakingTreasury.sol";
import { RegistrarClient } from "../RegistrarClient.sol";
contract VETHYieldRateTreasury is AdminGovernanceAgent, Governable, RegistrarClient {
address private _migration;
VETHReverseStakingTreasury private _vethReverseStakingTreasury;
event ReverseStakingTransfer(address recipient, uint256 amount);
constructor(
address registrarAddress,
address[] memory adminGovAgents
) AdminGovernanceAgent(adminGovAgents)
RegistrarClient(registrarAddress) {
}
modifier onlyReverseStakingTreasury() {
require(address(_vethReverseStakingTreasury) == _msgSender(), "Unauthorized");
_;
}
function getYieldRateTreasuryValue() external view returns (uint256) {
return address(this).balance + _vethReverseStakingTreasury.getTotalClaimedYield();
}
function getMigration() external view returns (address) {
return _migration;
}
function setMigration(address destination) external onlyGovernance {
_migration = destination;
}
function transferMigration(uint256 amount) external onlyAdminGovAgents {
require(_migration != address(0), "Migration not set");
_transfer(_migration, amount, "");
}
function reverseStakingTransfer(address recipient, uint256 amount) external onlyReverseStakingTreasury {
_transfer(recipient, amount, "");
emit ReverseStakingTransfer(recipient, amount);
}
function reverseStakingRoute(address recipient, uint256 amount, bytes memory selector) external onlyReverseStakingTreasury {
_transfer(recipient, amount, selector);
emit ReverseStakingTransfer(recipient, amount);
}
function updateAddresses() external override onlyRegistrar {
_vethReverseStakingTreasury = VETHReverseStakingTreasury(payable(_registrar.getVETHReverseStakingTreasury()));
_updateGovernable(_registrar);
}
function _transfer(address recipient, uint256 amount, bytes memory payload) private {
require(address(this).balance >= amount, "Insufficient balance");
(bool sent,) = recipient.call{value: amount}(payload);
require(sent, "Failed to send Ether");
}
receive() external payable {}
}
文件 33 的 35:VYRevenueCycleCirculationTracker.sol
pragma solidity 0.8.18;
import { Registrar } from "../Registrar.sol";
import { Context } from "../lib/utils/Context.sol";
contract VYRevenueCycleCirculationTracker is Context {
uint256 private _revenueCycleCirculation;
address private _vyTokenAddress;
constructor(uint256 initialCirculation) {
_revenueCycleCirculation = initialCirculation;
}
modifier onlyVYToken() {
require(_msgSender() == _vyTokenAddress, "Caller must be VYToken");
_;
}
function increaseRevenueCycleCirculation(uint256 amount) external onlyVYToken {
_revenueCycleCirculation += amount;
}
function decreaseRevenueCycleCirculation(uint256 amount) external onlyVYToken {
if (amount > _revenueCycleCirculation) {
_revenueCycleCirculation = 0;
} else {
_revenueCycleCirculation -= amount;
}
}
function _updateVYCirculationHelper(Registrar registrar) internal {
_vyTokenAddress = registrar.getVYToken();
}
function _getRevenueCycleCirculation() internal view returns (uint256) {
return _revenueCycleCirculation;
}
}
文件 34 的 35:VYToken.sol
pragma solidity 0.8.18;
import { AccessControl } from "../lib/access/AccessControl.sol";
import { ERC20 } from "../lib/token/ERC20/ERC20.sol";
import { AdminAgent } from "../access/AdminAgent.sol";
import { BackendAgent } from "../access/BackendAgent.sol";
import { VYRevenueCycleCirculationTracker } from "../exchange/VYRevenueCycleCirculationTracker.sol";
import { Registrar } from "../Registrar.sol";
import { VETHGovernance } from "../governance/VETHGovernance.sol";
contract VYToken is ERC20, AdminAgent, BackendAgent, AccessControl {
uint256 private constant MULTIPLIER = 10**18;
bytes32 private constant EIP712DOMAINTYPE_HASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;
bytes32 private constant NAME_HASH = 0xc8992ef634b020d3849cb749bb94cf703a7071d02872a417a811fadacc5fdcbb;
bytes32 private constant VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;
bytes32 private constant TXTYPE_HASH = 0x085abc97e2d328b3816b8248b9e8aa0e35bb8f414343c830d2d375b0d9b3c98f;
bytes32 public DOMAIN_SEPARATOR;
mapping(address => uint) public nonces;
bytes32 public constant MAIN_ECOSYSTEM_ID = keccak256(bytes("VY_ETH"));
bytes32 private constant OWNER_ROLE = keccak256("OWNER_ROLE");
bytes32 private constant WHITELISTER_ROLE = keccak256("WHITELISTER_ROLE");
uint256 public constant MAX_SUPPLY = 7000000000000000000000000000;
mapping(address => bool) private _agents;
mapping(address => bool) private _minters;
mapping(bytes32 => address) private _registrars;
uint256 private _vyCirculation = 0;
uint256 private _transferFee = 0;
event AgentWhitelisted(address recipient);
event AgentWhitelistRevoked(address recipient);
event SetRegistrar(address registrar, bytes32 ecosystemId);
constructor(
string memory name,
string memory symbol,
address registrarAddress,
address[] memory adminAgents,
address[] memory backendAdminAgents,
address[] memory backendAgents,
uint256 transferFee_,
uint256 initialCirculation
) ERC20(name, symbol) AdminAgent(adminAgents) {
_setRoleAdmin(WHITELISTER_ROLE, OWNER_ROLE);
_setRoleAdmin(OWNER_ROLE, OWNER_ROLE);
_setupRole(OWNER_ROLE, _msgSender());
_setBackendAdminAgents(backendAdminAgents);
_setBackendAgents(backendAgents);
_setRegistrar(registrarAddress);
DOMAIN_SEPARATOR = keccak256(
abi.encode(
EIP712DOMAINTYPE_HASH,
NAME_HASH,
VERSION_HASH,
block.chainid,
address(this)
)
);
_transferFee = transferFee_;
_vyCirculation = initialCirculation;
}
function getVYCirculation() external view returns (uint256) {
return _vyCirculation;
}
function getRegistrarById(bytes32 id) external view returns(address) {
return _registrars[id];
}
function isMinter(address _address) external view returns (bool) {
return _minters[_address];
}
function transfer(address recipient, uint256 amount) public override returns (bool) {
uint256 fee = _calculateTransferFee(_msgSender(), recipient, amount);
if (fee != 0) {
address mainRevenueCycleTreasury = _getMainEcosystemRegistrar().getVETHRevenueCycleTreasury();
_updateCirculationAndSupply(_msgSender(), mainRevenueCycleTreasury, fee);
super.transfer(recipient, amount - fee);
return super.transfer(mainRevenueCycleTreasury, fee);
}
_updateCirculationAndSupply(_msgSender(), recipient, amount);
return super.transfer(recipient, amount);
}
function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
uint256 fee = _calculateTransferFee(sender, recipient, amount);
if (fee != 0) {
address mainRevenueCycleTreasury = _getMainEcosystemRegistrar().getVETHRevenueCycleTreasury();
_updateCirculationAndSupply(sender, mainRevenueCycleTreasury, fee);
super.transferFrom(sender, recipient, amount - fee);
return super.transferFrom(sender, mainRevenueCycleTreasury, fee);
}
_updateCirculationAndSupply(sender, recipient, amount);
return super.transferFrom(sender, recipient, amount);
}
function getTransferFee() external view returns (uint256) {
return _transferFee;
}
function setTransferFee(uint256 fee) external onlyAdminAgents {
_transferFee = fee;
}
function setRegistrar(bytes32 originEcosystemId, uint proposalNonce) external {
address registrarAddress = _registrars[originEcosystemId];
require(registrarAddress != address(0), "Invalid originEcosystemId");
Registrar registrar = Registrar(registrarAddress);
VETHGovernance governance = VETHGovernance(registrar.getVETHGovernance());
require(_msgSender() == address(governance), "Caller must be VETHGovernance");
VETHGovernance.Proposal memory proposal = governance.getProposalById(proposalNonce);
require(proposal.approved == true && proposal.proposalType == VETHGovernance.ProposalType.Registrar, "Invalid proposal");
_setRegistrar(proposal.registrar.registrar);
_setMinter(Registrar(proposal.registrar.registrar));
}
function registrarMigrateTokens(bytes32 registrarId, uint256 contractIndex) external {
address registrarAddress = _registrars[registrarId];
require(registrarAddress != address(0), "Invalid registar id");
Registrar registrar = Registrar(registrarAddress);
_requireRegistrarIsUnfinalized(registrar);
address prevContract = registrar.getPrevContractByIndex(contractIndex);
address newContract = registrar.getContractByIndex(contractIndex);
require(_msgSender() == prevContract, "Caller must be prevContract");
require(newContract != address(0), "newContract is the zero address");
super.transfer(newContract, balanceOf(prevContract));
}
function _setRegistrar(address registrar) private {
require(registrar != address(0), "Invalid address");
bytes32 ecosystemId = Registrar(registrar).getEcosystemId();
_registrars[ecosystemId] = registrar;
emit SetRegistrar(registrar, ecosystemId);
}
function mint(uint256 amount) public returns (bool) {
require(_minters[_msgSender()], "Caller is not an allowed minter");
require(totalSupply() + amount <= MAX_SUPPLY, "Exceeds max supply");
super._mint(_msgSender(), amount);
return true;
}
function setMinter() external {
Registrar registrar = Registrar(_msgSender());
require(_registrars[registrar.getEcosystemId()] == _msgSender(), "Invalid registar");
_requireRegistrarIsUnfinalized(registrar);
_setMinter(registrar);
}
function _setMinter(Registrar registrar) private {
address prevRevenueCycleTreasury = registrar.getPrevContractByIndex(uint(Registrar.Contract.VETHRevenueCycleTreasury));
_minters[prevRevenueCycleTreasury] = false;
address revenueCycleTreasury = registrar.getVETHRevenueCycleTreasury();
_minters[revenueCycleTreasury] = true;
}
function airdropTokens(address[] calldata _addresses, uint[] calldata _amounts) external onlyBackendAgents {
require(_addresses.length == _amounts.length, "Argument array length mismatch");
_requireRegistrarIsUnfinalized(_getMainEcosystemRegistrar());
for (uint i = 0; i < _addresses.length; i++) {
super._mint(_addresses[i], _amounts[i]);
}
}
function grantOwnerRole(address _address) external onlyRole(OWNER_ROLE) {
grantRole(OWNER_ROLE, _address);
}
function grantWhitelisterRole(address _address) external onlyRole(OWNER_ROLE) {
grantRole(WHITELISTER_ROLE, _address);
}
function revokeOwnerRole(address _address) external onlyRole(OWNER_ROLE) {
revokeRole(OWNER_ROLE, _address);
}
function revokeWhitelisterRole(address _address) external onlyRole(OWNER_ROLE) {
revokeRole(WHITELISTER_ROLE, _address);
}
function isWhitelistedAgent(address _address) external view returns (bool) {
return _agents[_address];
}
function whitelistAgent(address _address) external onlyRole(WHITELISTER_ROLE) {
require(_agents[_address] == false, "Already whitelisted");
_agents[_address] = true;
emit AgentWhitelisted(_address);
}
function revokeWhitelistedAgent(address _address) external onlyRole(WHITELISTER_ROLE) {
require(_agents[_address] == true, "Not whitelisted");
delete _agents[_address];
emit AgentWhitelistRevoked(_address);
}
function permit(address owner, address spender, uint256 amount, uint8 v, bytes32 r, bytes32 s) external {
bytes32 txInputHash = keccak256(abi.encode(TXTYPE_HASH, owner, spender, amount, nonces[owner]));
bytes32 totalHash = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, txInputHash));
address recoveredAddress = ecrecover(totalHash, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "VYToken: INVALID_SIGNATURE");
nonces[owner] = nonces[owner] + 1;
_approve(owner, spender, amount);
}
function _getMainEcosystemRegistrar() private view returns (Registrar) {
address registrarAddress = _registrars[MAIN_ECOSYSTEM_ID];
return Registrar(registrarAddress);
}
function _updateCirculationAndSupply(address from, address to, uint256 amount) private {
if (_minters[to]) {
_decreaseCirculationAndSupply(amount, to);
} else if (_minters[from]) {
_increaseCirculationAndSupply(amount, from);
}
}
function _increaseCirculationAndSupply(uint256 amount, address minter) internal {
_vyCirculation += amount;
VYRevenueCycleCirculationTracker(minter).increaseRevenueCycleCirculation(amount);
}
function _decreaseCirculationAndSupply(uint256 amount, address minter) internal {
if (amount > _vyCirculation) {
_vyCirculation = 0;
} else {
_vyCirculation -= amount;
}
VYRevenueCycleCirculationTracker(minter).decreaseRevenueCycleCirculation(amount);
}
function _calculateTransferFee(address from, address to, uint256 amount) private view returns (uint256) {
if (!_agents[from] && !_agents[to]) {
uint256 transferFee = amount * _transferFee / MULTIPLIER;
return transferFee;
}
return 0;
}
function _requireRegistrarIsUnfinalized(Registrar registrar) private view {
require(!registrar.isFinalized(), "Registrar already finalized");
}
}
文件 35 的 35:draft-IERC1822Upgradeable.sol
pragma solidity ^0.8.0;
interface IERC1822ProxiableUpgradeable {
function proxiableUUID() external view returns (bytes32);
}
{
"compilationTarget": {
"contracts/token/VYToken.sol": "VYToken"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"address","name":"registrarAddress","type":"address"},{"internalType":"address[]","name":"adminAgents","type":"address[]"},{"internalType":"address[]","name":"backendAdminAgents","type":"address[]"},{"internalType":"address[]","name":"backendAgents","type":"address[]"},{"internalType":"uint256","name":"transferFee_","type":"uint256"},{"internalType":"uint256","name":"initialCirculation","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"AgentWhitelistRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"}],"name":"AgentWhitelisted","type":"event"},{"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":false,"internalType":"address","name":"agent","type":"address"}],"name":"RevokeBackendAgent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"agent","type":"address"}],"name":"SetBackendAgent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"registrar","type":"address"},{"indexed":false,"internalType":"bytes32","name":"ecosystemId","type":"bytes32"}],"name":"SetRegistrar","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":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAIN_ECOSYSTEM_ID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_addresses","type":"address[]"},{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"name":"airdropTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","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":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"id","type":"bytes32"}],"name":"getRegistrarById","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTransferFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVYCirculation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"grantOwnerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"grantWhitelisterRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isWhitelistedAgent","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"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":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","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":"bytes32","name":"registrarId","type":"bytes32"},{"internalType":"uint256","name":"contractIndex","type":"uint256"}],"name":"registrarMigrateTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_agent","type":"address"}],"name":"revokeBackendAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"revokeOwnerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"revokeWhitelistedAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"revokeWhitelisterRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_agent","type":"address"}],"name":"setBackendAgent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setMinter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"originEcosystemId","type":"bytes32"},{"internalType":"uint256","name":"proposalNonce","type":"uint256"}],"name":"setRegistrar","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setTransferFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"whitelistAgent","outputs":[],"stateMutability":"nonpayable","type":"function"}]