// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.21;
import { LockstakeUrn } from "src/LockstakeUrn.sol";
import { Multicall } from "src/Multicall.sol";
interface VoteDelegateFactoryLike {
function created(address) external returns (uint256);
}
interface VoteDelegateLike {
function lock(uint256) external;
function free(uint256) external;
}
interface VatLike {
function ilks(bytes32) external view returns (uint256, uint256, uint256, uint256, uint256);
function urns(bytes32, address) external view returns (uint256, uint256);
function hope(address) external;
function slip(bytes32, address, int256) external;
function frob(bytes32, address, address, address, int256, int256) external;
function grab(bytes32, address, address, address, int256, int256) external;
}
interface UsdsJoinLike {
function vat() external view returns (VatLike);
function usds() external view returns (GemLike);
function join(address, uint256) external;
function exit(address, uint256) external;
}
interface GemLike {
function approve(address, uint256) external;
function transfer(address, uint256) external;
function transferFrom(address, address, uint256) external;
function mint(address, uint256) external;
function burn(address, uint256) external;
}
interface JugLike {
function drip(bytes32) external returns (uint256);
}
interface MkrSkyLike {
function rate() external view returns (uint256);
function mkr() external view returns (GemLike);
function sky() external view returns (GemLike);
function skyToMkr(address, uint256) external;
function mkrToSky(address, uint256) external;
}
contract LockstakeEngine is Multicall {
// --- storage variables ---
mapping(address usr => uint256 allowed) public wards;
mapping(address farm => FarmStatus) public farms;
mapping(address owner => uint256 count) public ownerUrnsCount;
mapping(address owner => mapping(uint256 index => address urn)) public ownerUrns;
mapping(address urn => address owner) public urnOwners;
mapping(address urn => mapping(address usr => uint256 allowed)) public urnCan;
mapping(address urn => address voteDelegate) public urnVoteDelegates;
mapping(address urn => address farm) public urnFarms;
mapping(address urn => uint256 auctionsCount) public urnAuctions;
JugLike public jug;
uint256 public fee;
// --- constants and enums ---
uint256 constant WAD = 10**18;
uint256 constant RAY = 10**27;
enum FarmStatus { UNSUPPORTED, ACTIVE, DELETED }
// --- immutables ---
VoteDelegateFactoryLike immutable public voteDelegateFactory;
VatLike immutable public vat;
UsdsJoinLike immutable public usdsJoin;
GemLike immutable public usds;
bytes32 immutable public ilk;
GemLike immutable public mkr;
GemLike immutable public lsmkr;
MkrSkyLike immutable public mkrSky;
GemLike immutable public sky;
uint256 immutable public mkrSkyRate;
address immutable public urnImplementation;
// --- events ---
event Rely(address indexed usr);
event Deny(address indexed usr);
event File(bytes32 indexed what, address data);
event File(bytes32 indexed what, uint256 data);
event AddFarm(address farm);
event DelFarm(address farm);
event Open(address indexed owner, uint256 indexed index, address urn);
event Hope(address indexed owner, uint256 indexed index, address indexed usr);
event Nope(address indexed owner, uint256 indexed index, address indexed usr);
event SelectVoteDelegate(address indexed owner, uint256 indexed index, address indexed voteDelegate);
event SelectFarm(address indexed owner, uint256 indexed index, address indexed farm, uint16 ref);
event Lock(address indexed owner, uint256 indexed index, uint256 wad, uint16 ref);
event LockSky(address indexed owner, uint256 indexed index, uint256 skyWad, uint16 ref);
event Free(address indexed owner, uint256 indexed index, address to, uint256 wad, uint256 freed);
event FreeSky(address indexed owner, uint256 indexed index, address to, uint256 skyWad, uint256 skyFreed);
event FreeNoFee(address indexed owner, uint256 indexed index, address to, uint256 wad);
event Draw(address indexed owner, uint256 indexed index, address to, uint256 wad);
event Wipe(address indexed owner, uint256 indexed index, uint256 wad);
event GetReward(address indexed owner, uint256 indexed index, address indexed farm, address to, uint256 amt);
event OnKick(address indexed urn, uint256 wad);
event OnTake(address indexed urn, address indexed who, uint256 wad);
event OnRemove(address indexed urn, uint256 sold, uint256 burn, uint256 refund);
// --- modifiers ---
modifier auth {
require(wards[msg.sender] == 1, "LockstakeEngine/not-authorized");
_;
}
// --- constructor ---
constructor(address voteDelegateFactory_, address usdsJoin_, bytes32 ilk_, address mkrSky_, address lsmkr_) {
voteDelegateFactory = VoteDelegateFactoryLike(voteDelegateFactory_);
usdsJoin = UsdsJoinLike(usdsJoin_);
vat = usdsJoin.vat();
usds = usdsJoin.usds();
ilk = ilk_;
mkrSky = MkrSkyLike(mkrSky_);
mkr = mkrSky.mkr();
sky = mkrSky.sky();
mkrSkyRate = mkrSky.rate();
lsmkr = GemLike(lsmkr_);
urnImplementation = address(new LockstakeUrn(address(vat), lsmkr_));
vat.hope(usdsJoin_);
usds.approve(usdsJoin_, type(uint256).max);
sky.approve(address(mkrSky), type(uint256).max);
mkr.approve(address(mkrSky), type(uint256).max);
wards[msg.sender] = 1;
emit Rely(msg.sender);
}
// --- internals ---
function _min(uint256 x, uint256 y) internal pure returns (uint256 z) {
z = x <= y ? x : y;
}
function _divup(uint256 x, uint256 y) internal pure returns (uint256 z) {
// Note: _divup(0,0) will return 0 differing from natural solidity division
unchecked {
z = x != 0 ? ((x - 1) / y) + 1 : 0;
}
}
function _urnAuth(address owner, address urn, address usr) internal view returns (bool ok) {
ok = owner == usr || urnCan[urn][usr] == 1;
}
function _getUrn(address owner, uint256 index) internal view returns (address urn) {
urn = ownerUrns[owner][index];
require(urn != address(0), "LockstakeEngine/invalid-urn");
}
function _getAuthedUrn(address owner, uint256 index) internal view returns (address urn) {
urn = _getUrn(owner, index);
require(_urnAuth(owner, urn, msg.sender), "LockstakeEngine/urn-not-authorized");
}
// See the reference implementation in https://eips.ethereum.org/EIPS/eip-1167
function _initCode() internal view returns (bytes memory code) {
code = new bytes(0x37);
bytes20 impl = bytes20(urnImplementation);
assembly {
mstore(add(code, 0x20), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(code, add(0x20, 0x14)), impl)
mstore(add(code, add(0x20, 0x28)), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
}
}
// --- administration ---
function rely(address usr) external auth {
wards[usr] = 1;
emit Rely(usr);
}
function deny(address usr) external auth {
wards[usr] = 0;
emit Deny(usr);
}
function file(bytes32 what, address data) external auth {
if (what == "jug") {
jug = JugLike(data);
} else revert("LockstakeEngine/file-unrecognized-param");
emit File(what, data);
}
function file(bytes32 what, uint256 data) external auth {
if (what == "fee") {
require(data < WAD, "LockstakeEngine/fee-equal-or-greater-wad");
fee = data;
} else revert("LockstakeEngine/file-unrecognized-param");
emit File(what, data);
}
function addFarm(address farm) external auth {
farms[farm] = FarmStatus.ACTIVE;
emit AddFarm(farm);
}
function delFarm(address farm) external auth {
farms[farm] = FarmStatus.DELETED;
emit DelFarm(farm);
}
// --- getters ---
function isUrnAuth(address owner, uint256 index, address usr) external view returns (bool ok) {
ok = _urnAuth(owner, _getUrn(owner, index), usr);
}
// --- urn management functions ---
function open(uint256 index) external returns (address urn) {
require(index == ownerUrnsCount[msg.sender]++, "LockstakeEngine/wrong-urn-index");
bytes memory initCode = _initCode();
assembly { urn := create(0, add(initCode, 0x20), 0x37) }
LockstakeUrn(urn).init(); // would revert if create had failed
ownerUrns[msg.sender][index] = urn;
urnOwners[urn] = msg.sender;
emit Open(msg.sender, index, urn);
}
function hope(address owner, uint256 index, address usr) external {
address urn = _getAuthedUrn(owner, index);
urnCan[urn][usr] = 1;
emit Hope(owner, index, usr);
}
function nope(address owner, uint256 index, address usr) external {
address urn = _getAuthedUrn(owner, index);
urnCan[urn][usr] = 0;
emit Nope(owner, index, usr);
}
// --- delegation/staking functions ---
function selectVoteDelegate(address owner, uint256 index, address voteDelegate) external {
address urn = _getAuthedUrn(owner, index);
require(urnAuctions[urn] == 0, "LockstakeEngine/urn-in-auction");
require(voteDelegate == address(0) || voteDelegateFactory.created(voteDelegate) == 1, "LockstakeEngine/not-valid-vote-delegate");
address prevVoteDelegate = urnVoteDelegates[urn];
require(prevVoteDelegate != voteDelegate, "LockstakeEngine/same-vote-delegate");
(uint256 ink, uint256 art) = vat.urns(ilk, urn);
if (art > 0 && voteDelegate != address(0)) {
(,, uint256 spot,,) = vat.ilks(ilk);
require(ink * spot >= art * jug.drip(ilk), "LockstakeEngine/urn-unsafe");
}
_selectVoteDelegate(urn, ink, prevVoteDelegate, voteDelegate);
emit SelectVoteDelegate(owner, index, voteDelegate);
}
function _selectVoteDelegate(address urn, uint256 wad, address prevVoteDelegate, address voteDelegate) internal {
if (wad > 0) {
if (prevVoteDelegate != address(0)) {
VoteDelegateLike(prevVoteDelegate).free(wad);
}
if (voteDelegate != address(0)) {
mkr.approve(voteDelegate, wad);
VoteDelegateLike(voteDelegate).lock(wad);
}
}
urnVoteDelegates[urn] = voteDelegate;
}
function selectFarm(address owner, uint256 index, address farm, uint16 ref) external {
address urn = _getAuthedUrn(owner, index);
require(urnAuctions[urn] == 0, "LockstakeEngine/urn-in-auction");
require(farm == address(0) || farms[farm] == FarmStatus.ACTIVE, "LockstakeEngine/farm-unsupported-or-deleted");
address prevFarm = urnFarms[urn];
require(prevFarm != farm, "LockstakeEngine/same-farm");
(uint256 ink,) = vat.urns(ilk, urn);
_selectFarm(urn, ink, prevFarm, farm, ref);
emit SelectFarm(owner, index, farm, ref);
}
function _selectFarm(address urn, uint256 wad, address prevFarm, address farm, uint16 ref) internal {
if (wad > 0) {
if (prevFarm != address(0)) {
LockstakeUrn(urn).withdraw(prevFarm, wad);
}
if (farm != address(0)) {
LockstakeUrn(urn).stake(farm, wad, ref);
}
}
urnFarms[urn] = farm;
}
function lock(address owner, uint256 index, uint256 wad, uint16 ref) external {
address urn = _getUrn(owner, index);
mkr.transferFrom(msg.sender, address(this), wad);
_lock(urn, wad, ref);
emit Lock(owner, index, wad, ref);
}
function lockSky(address owner, uint256 index, uint256 skyWad, uint16 ref) external {
address urn = _getUrn(owner, index);
sky.transferFrom(msg.sender, address(this), skyWad);
mkrSky.skyToMkr(address(this), skyWad);
_lock(urn, skyWad / mkrSkyRate, ref);
emit LockSky(owner, index, skyWad, ref);
}
function _lock(address urn, uint256 wad, uint16 ref) internal {
require(wad <= uint256(type(int256).max), "LockstakeEngine/overflow");
address voteDelegate = urnVoteDelegates[urn];
if (voteDelegate != address(0)) {
mkr.approve(voteDelegate, wad);
VoteDelegateLike(voteDelegate).lock(wad);
}
vat.slip(ilk, urn, int256(wad));
vat.frob(ilk, urn, urn, address(0), int256(wad), 0);
lsmkr.mint(urn, wad);
address urnFarm = urnFarms[urn];
if (urnFarm != address(0)) {
require(farms[urnFarm] == FarmStatus.ACTIVE, "LockstakeEngine/farm-deleted");
LockstakeUrn(urn).stake(urnFarm, wad, ref);
}
}
function free(address owner, uint256 index, address to, uint256 wad) external returns (uint256 freed) {
address urn = _getAuthedUrn(owner, index);
freed = _free(urn, wad, fee);
mkr.transfer(to, freed);
emit Free(owner, index, to, wad, freed);
}
function freeSky(address owner, uint256 index, address to, uint256 skyWad) external returns (uint256 skyFreed) {
address urn = _getAuthedUrn(owner, index);
uint256 wad = skyWad / mkrSkyRate;
uint256 freed = _free(urn, wad, fee);
skyFreed = freed * mkrSkyRate;
mkrSky.mkrToSky(to, freed);
emit FreeSky(owner, index, to, skyWad, skyFreed);
}
function freeNoFee(address owner, uint256 index, address to, uint256 wad) external auth {
address urn = _getAuthedUrn(owner, index);
_free(urn, wad, 0);
mkr.transfer(to, wad);
emit FreeNoFee(owner, index, to, wad);
}
function _free(address urn, uint256 wad, uint256 fee_) internal returns (uint256 freed) {
require(wad <= uint256(type(int256).max), "LockstakeEngine/overflow");
address urnFarm = urnFarms[urn];
if (urnFarm != address(0)) {
LockstakeUrn(urn).withdraw(urnFarm, wad);
}
lsmkr.burn(urn, wad);
vat.frob(ilk, urn, urn, address(0), -int256(wad), 0);
vat.slip(ilk, urn, -int256(wad));
address voteDelegate = urnVoteDelegates[urn];
if (voteDelegate != address(0)) {
VoteDelegateLike(voteDelegate).free(wad);
}
uint256 burn = wad * fee_ / WAD;
if (burn > 0) {
mkr.burn(address(this), burn);
}
unchecked { freed = wad - burn; } // burn <= wad always
}
// --- loan functions ---
function draw(address owner, uint256 index, address to, uint256 wad) external {
address urn = _getAuthedUrn(owner, index);
uint256 rate = jug.drip(ilk);
uint256 dart = _divup(wad * RAY, rate);
require(dart <= uint256(type(int256).max), "LockstakeEngine/overflow");
vat.frob(ilk, urn, address(0), address(this), 0, int256(dart));
usdsJoin.exit(to, wad);
emit Draw(owner, index, to, wad);
}
function wipe(address owner, uint256 index, uint256 wad) external {
address urn = _getUrn(owner, index);
usds.transferFrom(msg.sender, address(this), wad);
usdsJoin.join(address(this), wad);
(, uint256 rate,,,) = vat.ilks(ilk);
uint256 dart = wad * RAY / rate;
require(dart <= uint256(type(int256).max), "LockstakeEngine/overflow");
vat.frob(ilk, urn, address(0), address(this), 0, -int256(dart));
emit Wipe(owner, index, wad);
}
function wipeAll(address owner, uint256 index) external returns (uint256 wad) {
address urn = _getUrn(owner, index);
(, uint256 art) = vat.urns(ilk, urn);
require(art <= uint256(type(int256).max), "LockstakeEngine/overflow");
(, uint256 rate,,,) = vat.ilks(ilk);
wad = _divup(art * rate, RAY);
usds.transferFrom(msg.sender, address(this), wad);
usdsJoin.join(address(this), wad);
vat.frob(ilk, urn, address(0), address(this), 0, -int256(art));
emit Wipe(owner, index, wad);
}
// --- staking rewards function ---
function getReward(address owner, uint256 index, address farm, address to) external returns (uint256 amt) {
address urn = _getAuthedUrn(owner, index);
require(farms[farm] > FarmStatus.UNSUPPORTED, "LockstakeEngine/farm-unsupported");
amt = LockstakeUrn(urn).getReward(farm, to);
emit GetReward(owner, index, farm, to, amt);
}
// --- liquidation callback functions ---
function onKick(address urn, uint256 wad) external auth {
// Urn confiscation happens in Dog contract where ilk vat.gem is sent to the LockstakeClipper
(uint256 ink,) = vat.urns(ilk, urn);
uint256 inkBeforeKick = ink + wad;
_selectVoteDelegate(urn, inkBeforeKick, urnVoteDelegates[urn], address(0));
_selectFarm(urn, inkBeforeKick, urnFarms[urn], address(0), 0);
lsmkr.burn(urn, wad);
urnAuctions[urn]++;
emit OnKick(urn, wad);
}
function onTake(address urn, address who, uint256 wad) external auth {
mkr.transfer(who, wad); // Free MKR to the auction buyer
emit OnTake(urn, who, wad);
}
function onRemove(address urn, uint256 sold, uint256 left) external auth {
uint256 burn;
uint256 refund;
if (left > 0) {
uint256 fee_ = fee;
burn = _min(sold * fee_ / (WAD - fee_), left);
mkr.burn(address(this), burn);
unchecked { refund = left - burn; }
if (refund > 0) {
// The following is ensured by the dog and clip but we still prefer to be explicit
require(refund <= uint256(type(int256).max), "LockstakeEngine/overflow");
vat.slip(ilk, urn, int256(refund));
vat.grab(ilk, urn, urn, address(0), int256(refund), 0);
lsmkr.mint(urn, refund);
}
}
urnAuctions[urn]--;
emit OnRemove(urn, sold, burn, refund);
}
}
// SPDX-FileCopyrightText: © 2023 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
pragma solidity ^0.8.21;
interface VatLike {
function hope(address) external;
}
interface GemLike {
function balanceOf(address) external view returns (uint256);
function approve(address, uint256) external;
function transfer(address, uint256) external;
}
interface StakingRewardsLike {
function rewardsToken() external view returns (GemLike);
function stake(uint256, uint16) external;
function withdraw(uint256) external;
function getReward() external;
}
contract LockstakeUrn {
// --- immutables ---
address immutable public engine;
GemLike immutable public lsmkr;
VatLike immutable public vat;
// --- modifiers ---
modifier isEngine {
require(msg.sender == engine, "LockstakeUrn/not-engine");
_;
}
// --- constructor & init ---
constructor(address vat_, address lsmkr_) {
engine = msg.sender;
vat = VatLike(vat_);
lsmkr = GemLike(lsmkr_);
}
function init() external isEngine {
vat.hope(msg.sender);
lsmkr.approve(msg.sender, type(uint256).max);
}
// --- staking functions ---
function stake(address farm, uint256 wad, uint16 ref) external isEngine {
lsmkr.approve(farm, wad);
StakingRewardsLike(farm).stake(wad, ref);
}
function withdraw(address farm, uint256 wad) external isEngine {
StakingRewardsLike(farm).withdraw(wad);
}
function getReward(address farm, address to) external isEngine returns (uint256 amt) {
StakingRewardsLike(farm).getReward();
GemLike rewardsToken = StakingRewardsLike(farm).rewardsToken();
amt = rewardsToken.balanceOf(address(this));
rewardsToken.transfer(to, amt);
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on https://github.com/Uniswap/v3-periphery/blob/697c2474757ea89fec12a4e6db16a574fe259610/contracts/base/Multicall.sol
pragma solidity ^0.8.21;
// Enables calling multiple methods in a single call to the contract
abstract contract Multicall {
function multicall(bytes[] calldata data) external returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory result) = address(this).delegatecall(data[i]);
if (!success) {
if (result.length == 0) revert("multicall failed");
assembly ("memory-safe") {
revert(add(32, result), mload(result))
}
}
results[i] = result;
}
}
}
{
"compilationTarget": {
"src/LockstakeEngine.sol": "LockstakeEngine"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":dss-interfaces/=lib/token-tests/lib/dss-test/lib/dss-interfaces/src/",
":dss-test/=lib/token-tests/lib/dss-test/src/",
":forge-std/=lib/token-tests/lib/dss-test/lib/forge-std/src/",
":token-tests/=lib/token-tests/src/"
]
}
[{"inputs":[{"internalType":"address","name":"voteDelegateFactory_","type":"address"},{"internalType":"address","name":"usdsJoin_","type":"address"},{"internalType":"bytes32","name":"ilk_","type":"bytes32"},{"internalType":"address","name":"mkrSky_","type":"address"},{"internalType":"address","name":"lsmkr_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"farm","type":"address"}],"name":"AddFarm","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"farm","type":"address"}],"name":"DelFarm","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"usr","type":"address"}],"name":"Deny","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Draw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"what","type":"bytes32"},{"indexed":false,"internalType":"address","name":"data","type":"address"}],"name":"File","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"what","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"data","type":"uint256"}],"name":"File","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"freed","type":"uint256"}],"name":"Free","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"FreeNoFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"skyWad","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"skyFreed","type":"uint256"}],"name":"FreeSky","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"address","name":"farm","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amt","type":"uint256"}],"name":"GetReward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"address","name":"usr","type":"address"}],"name":"Hope","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"ref","type":"uint16"}],"name":"Lock","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"skyWad","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"ref","type":"uint16"}],"name":"LockSky","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"address","name":"usr","type":"address"}],"name":"Nope","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"urn","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"OnKick","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"urn","type":"address"},{"indexed":false,"internalType":"uint256","name":"sold","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"refund","type":"uint256"}],"name":"OnRemove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"urn","type":"address"},{"indexed":true,"internalType":"address","name":"who","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"OnTake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"address","name":"urn","type":"address"}],"name":"Open","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"usr","type":"address"}],"name":"Rely","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"address","name":"farm","type":"address"},{"indexed":false,"internalType":"uint16","name":"ref","type":"uint16"}],"name":"SelectFarm","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":true,"internalType":"address","name":"voteDelegate","type":"address"}],"name":"SelectVoteDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Wipe","type":"event"},{"inputs":[{"internalType":"address","name":"farm","type":"address"}],"name":"addFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"farm","type":"address"}],"name":"delFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"deny","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"draw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"farm","type":"address"}],"name":"farms","outputs":[{"internalType":"enum LockstakeEngine.FarmStatus","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"uint256","name":"data","type":"uint256"}],"name":"file","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"what","type":"bytes32"},{"internalType":"address","name":"data","type":"address"}],"name":"file","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"free","outputs":[{"internalType":"uint256","name":"freed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"freeNoFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"skyWad","type":"uint256"}],"name":"freeSky","outputs":[{"internalType":"uint256","name":"skyFreed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"farm","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"getReward","outputs":[{"internalType":"uint256","name":"amt","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"usr","type":"address"}],"name":"hope","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ilk","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"usr","type":"address"}],"name":"isUrnAuth","outputs":[{"internalType":"bool","name":"ok","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"jug","outputs":[{"internalType":"contract JugLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"wad","type":"uint256"},{"internalType":"uint16","name":"ref","type":"uint16"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"skyWad","type":"uint256"},{"internalType":"uint16","name":"ref","type":"uint16"}],"name":"lockSky","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lsmkr","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mkr","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mkrSky","outputs":[{"internalType":"contract MkrSkyLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mkrSkyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"results","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"usr","type":"address"}],"name":"nope","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"onKick","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"},{"internalType":"uint256","name":"sold","type":"uint256"},{"internalType":"uint256","name":"left","type":"uint256"}],"name":"onRemove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"},{"internalType":"address","name":"who","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"onTake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"open","outputs":[{"internalType":"address","name":"urn","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"ownerUrns","outputs":[{"internalType":"address","name":"urn","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"ownerUrnsCount","outputs":[{"internalType":"uint256","name":"count","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"rely","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"farm","type":"address"},{"internalType":"uint16","name":"ref","type":"uint16"}],"name":"selectFarm","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"address","name":"voteDelegate","type":"address"}],"name":"selectVoteDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sky","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"}],"name":"urnAuctions","outputs":[{"internalType":"uint256","name":"auctionsCount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"},{"internalType":"address","name":"usr","type":"address"}],"name":"urnCan","outputs":[{"internalType":"uint256","name":"allowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"}],"name":"urnFarms","outputs":[{"internalType":"address","name":"farm","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"urnImplementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"}],"name":"urnOwners","outputs":[{"internalType":"address","name":"owner","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"urn","type":"address"}],"name":"urnVoteDelegates","outputs":[{"internalType":"address","name":"voteDelegate","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usds","outputs":[{"internalType":"contract GemLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"usdsJoin","outputs":[{"internalType":"contract UsdsJoinLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vat","outputs":[{"internalType":"contract VatLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"voteDelegateFactory","outputs":[{"internalType":"contract VoteDelegateFactoryLike","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"usr","type":"address"}],"name":"wards","outputs":[{"internalType":"uint256","name":"allowed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"wipe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"wipeAll","outputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]