Latest 5 from a total of 5 transactions
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x3d602d80 | 18892753 | 791 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0x502a7759809bd673cd39a0055beed44b40eaac98
Contract Name:
INVEscrow
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import "../interfaces/IERC20.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
// @dev Caution: We assume all failed transfers cause reverts and ignore the returned bool.
interface IXINV {
function rewardTreasury() external view returns(address);
function balanceOf(address) external view returns (uint);
function exchangeRateStored() external view returns (uint);
function exchangeRateCurrent() external returns (uint);
function mint(uint mintAmount) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function syncDelegate(address user) external;
function accrualBlockNumber() external view returns (uint);
function getCash() external view returns (uint);
function totalSupply() external view returns (uint);
function rewardPerBlock() external view returns (uint);
}
interface IDbrDistributor {
function stake(uint amount) external;
function unstake(uint amount) external;
function claim(address to) external;
function claimable(address user) external view returns(uint);
}
/**
* @title INV Escrow
* @notice Collateral is stored in unique escrow contracts for every user and every market.
* This escrow allows user to deposit INV collateral directly into the xINV contract, earning APY and allowing them to delegate votes on behalf of the xINV collateral
* @dev Caution: This is a proxy implementation. Follow proxy pattern best practices
*/
contract INVEscrow {
using FixedPointMathLib for uint;
address public market;
IDelegateableERC20 public token;
address public beneficiary;
uint public stakedXINV;
IXINV public immutable xINV;
IDbrDistributor public immutable distributor;
mapping(address => bool) public claimers;
constructor(IXINV _xINV, IDbrDistributor _distributor) {
xINV = _xINV;
distributor = _distributor;
}
/**
* @notice Initialize escrow with a token
* @dev Must be called right after proxy is created.
* @param _token The IERC20 token representing the INV governance token
* @param _beneficiary The beneficiary who may delegate token voting power
*/
function initialize(IDelegateableERC20 _token, address _beneficiary) public {
require(market == address(0), "ALREADY INITIALIZED");
market = msg.sender;
token = _token;
beneficiary = _beneficiary;
_token.delegate(_token.delegates(_beneficiary));
_token.approve(address(xINV), type(uint).max);
xINV.syncDelegate(address(this));
}
/**
* @notice Transfers the associated ERC20 token to a recipient.
* @param recipient The address to receive payment from the escrow
* @param amount The amount of ERC20 token to be transferred.
*/
function pay(address recipient, uint amount) public {
require(msg.sender == market, "ONLY MARKET");
uint invBalance = token.balanceOf(address(this));
if(invBalance < amount) {
uint invNeeded = amount - invBalance;
uint xInvToUnstake = invNeeded * 1 ether / viewExchangeRate();
stakedXINV -= xInvToUnstake;
distributor.unstake(xInvToUnstake);
xINV.redeemUnderlying(invNeeded); // we do not check return value because transfer call will fail if this fails anyway
}
token.transfer(recipient, amount);
}
/**
* @notice Allows the beneficiary to claim DBR tokens
* @dev Requires the caller to be the beneficiary
*/
function claimDBR() public {
require(msg.sender == beneficiary, "ONLY BENEFICIARY");
distributor.claim(msg.sender);
}
/**
* @notice Allows the beneficiary or allowed claimers to claim DBR tokens on behalf of another address
* @param to The address to which the claimed tokens will be sent
* @dev Requires the caller to be the beneficiary or an allowed claimer
*/
function claimDBRTo(address to) public {
require(msg.sender == beneficiary || claimers[msg.sender], "ONLY BENEFICIARY OR ALLOWED CLAIMERS");
distributor.claim(to);
}
/**
* @notice Returns the amount of claimable DBR tokens for the contract
* @return The amount of claimable tokens
*/
function claimable() public view returns (uint) {
return distributor.claimable(address(this));
}
/**
* @notice Sets or unsets an address as an allowed claimer
* @param claimer The address of the claimer to set or unset
* @param allowed A boolean value to determine if the claimer is allowed or not
* @dev Requires the caller to be the beneficiary
*/
function setClaimer(address claimer, bool allowed) public {
require(msg.sender == beneficiary, "ONLY BENEFICIARY");
claimers[claimer] = allowed;
}
/**
* @notice Get the token balance of the escrow
* @return Uint representing the INV token balance of the escrow including the additional INV accrued from xINV
*/
function balance() public view returns (uint) {
uint invBalance = token.balanceOf(address(this));
uint invBalanceInXInv = stakedXINV * viewExchangeRate() / 1 ether;
return invBalance + invBalanceInXInv;
}
/**
* @notice Function called by market on deposit. Will deposit INV into xINV
* @dev This function should remain callable by anyone to handle direct inbound transfers.
*/
function onDeposit() public {
uint invBalance = token.balanceOf(address(this));
if(invBalance > 0) {
uint xinvBal = xINV.balanceOf(address(this));
xINV.mint(invBalance); // we do not check return value because we don't want errors to block this call
stakedXINV += xINV.balanceOf(address(this)) - xinvBal;
distributor.stake(stakedXINV - xinvBal);
}
}
/**
* @notice Delegates voting power of the underlying xINV.
* @param delegatee The address to be delegated voting power
*/
function delegate(address delegatee) public {
require(msg.sender == beneficiary, "ONLY BENEFICIARY");
token.delegate(delegatee);
xINV.syncDelegate(address(this));
}
/**
* @notice View function to calculate exact exchangerate for current block
*/
function viewExchangeRate() internal view returns (uint) {
uint accrualBlockNumberPrior = xINV.accrualBlockNumber();
if (accrualBlockNumberPrior == block.number) return xINV.exchangeRateStored();
uint blockDelta = block.number - accrualBlockNumberPrior;
uint rewardsAccrued = xINV.rewardPerBlock() * blockDelta;
uint treasuryInvBalance = token.balanceOf(xINV.rewardTreasury());
uint treasuryxInvAllowance = token.allowance(xINV.rewardTreasury(), address(xINV));
if( treasuryInvBalance <= rewardsAccrued || treasuryxInvAllowance <= rewardsAccrued) return xINV.exchangeRateStored();
return (xINV.getCash() + rewardsAccrued).divWadDown(xINV.totalSupply());
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,
// 1 is added to round up the division of x * y by the denominator.
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Mod x by y. Note this will return
// 0 instead of reverting if y is zero.
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
/// @solidity memory-safe-assembly
assembly {
// Divide x by y. Note this will return
// 0 instead of reverting if y is zero.
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
/// @solidity memory-safe-assembly
assembly {
// Add 1 to x * y if x % y > 0. Note this will
// return 0 instead of reverting if y is zero.
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}pragma solidity ^0.8.13;
interface IERC20 {
function approve(address,uint) external;
function transfer(address,uint) external returns (bool);
function transferFrom(address,address,uint) external returns (bool);
function balanceOf(address) external view returns (uint);
function allowance(address from, address to) external view returns (uint);
}
interface IDelegateableERC20 is IERC20 {
function delegate(address delegatee) external;
function delegates(address delegator) external view returns (address delegatee);
}{
"remappings": [
"ds-test/=lib/solmate/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solmate/=lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"bytecodeHash": "ipfs"
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "london",
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"contract IXINV","name":"_xINV","type":"address"},{"internalType":"contract IDbrDistributor","name":"_distributor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beneficiary","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimDBR","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"claimDBRTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"distributor","outputs":[{"internalType":"contract IDbrDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IDelegateableERC20","name":"_token","type":"address"},{"internalType":"address","name":"_beneficiary","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"market","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"pay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"claimer","type":"address"},{"internalType":"bool","name":"allowed","type":"bool"}],"name":"setClaimer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stakedXINV","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IDelegateableERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xINV","outputs":[{"internalType":"contract IXINV","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.