More Info
Private Name Tags
ContractCreator
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
17733926 | 525 days ago | Contract Creation | 0 ETH |
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": {} }
[{"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
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.