More Info
Private Name Tags
ContractCreator
TokenTracker
Multichain Info
No addresses found
Latest 25 from a total of 32 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Approve | 16742193 | 35 days ago | IN | 0 ETH | 0.00000003 | ||||
Approve | 15877757 | 56 days ago | IN | 0 ETH | 0.00000013 | ||||
Approve | 15876836 | 56 days ago | IN | 0 ETH | 0.00000011 | ||||
Approve | 15761095 | 58 days ago | IN | 0 ETH | 0.00000003 | ||||
Approve | 15761090 | 58 days ago | IN | 0 ETH | 0.00000003 | ||||
Approve | 15670529 | 60 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 15576755 | 62 days ago | IN | 0 ETH | 0.00000006 | ||||
Approve | 15465534 | 65 days ago | IN | 0 ETH | 0.00000013 | ||||
Approve | 14657080 | 84 days ago | IN | 0 ETH | 0.00000008 | ||||
Approve | 14251196 | 93 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 14104035 | 97 days ago | IN | 0 ETH | 0.00000006 | ||||
Approve | 13793540 | 104 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 13725722 | 105 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 13589556 | 108 days ago | IN | 0 ETH | 0.00000008 | ||||
Approve | 13428835 | 112 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 13425143 | 112 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 13425097 | 112 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 13275783 | 116 days ago | IN | 0 ETH | 0.00000005 | ||||
Approve | 12900385 | 124 days ago | IN | 0 ETH | 0.00000006 | ||||
Approve | 12838628 | 126 days ago | IN | 0 ETH | 0.0000001 | ||||
Approve | 12655181 | 130 days ago | IN | 0 ETH | 0.00000024 | ||||
Approve | 12635164 | 131 days ago | IN | 0 ETH | 0.00000003 | ||||
Approve | 12364579 | 137 days ago | IN | 0 ETH | 0.00000007 | ||||
Approve | 12317438 | 138 days ago | IN | 0 ETH | 0.00000039 | ||||
Approve | 12281560 | 139 days ago | IN | 0 ETH | 0.0000003 |
Latest 2 internal transactions
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
11655557 | 153 days ago | Contract Creation | 0 ETH | |||
11655557 | 153 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Minimal Proxy Contract for 0xca56b2c3f46c95b301b0c830283c065c2bf9d4bb
Contract Name:
Pair
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 2000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity =0.8.19; import {IERC20Metadata, IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {IPair} from "./interfaces/IPair.sol"; import {IPairCallee} from "./interfaces/IPairCallee.sol"; import {IPairFactory} from "./interfaces/IPairFactory.sol"; import {PairFees} from "./PairFees.sol"; import {BlastERC20RebasingManage} from "../integration/BlastERC20RebasingManage.sol"; // The base pair of pools, either stable or volatile contract Pair is IPair, BlastERC20RebasingManage { string public name; string public symbol; uint8 public constant decimals = 18; // Used to denote stable or volatile pair, not immutable since construction happens in the initialize method for CREATE2 deterministic addresses bool public stable; uint public totalSupply = 0; mapping(address => mapping(address => uint)) public allowance; mapping(address => uint) public balanceOf; bytes32 internal DOMAIN_SEPARATOR; // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); bytes32 internal constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; mapping(address => uint) public nonces; uint internal constant MINIMUM_LIQUIDITY = 10 ** 3; uint256 internal constant MINIMUM_K = 10 ** 10; address public token0; address public token1; address public fees; address public factory; address public communityVault; // Structure to capture time period obervations every 30 minutes, used for local oracles struct Observation { uint timestamp; uint reserve0Cumulative; uint reserve1Cumulative; } // Capture oracle reading every 30 minutes uint constant periodSize = 1800; Observation[] public observations; uint internal decimals0; uint internal decimals1; uint public reserve0; uint public reserve1; uint public blockTimestampLast; uint public reserve0CumulativeLast; uint public reserve1CumulativeLast; // index0 and index1 are used to accumulate fees, this is split out from normal trades to keep the swap "clean" // this further allows LP holders to easily claim fees for tokens they have/staked uint public index0 = 0; uint public index1 = 0; // position assigned to each LP to track their current index0 & index1 vs the global position mapping(address => uint) public supplyIndex0; mapping(address => uint) public supplyIndex1; // tracks the amount of unclaimed, but claimable tokens off of fees for token0 and token1 mapping(address => uint) public claimable0; mapping(address => uint) public claimable1; event Fees(address indexed sender, uint amount0, uint amount1); event Mint(address indexed sender, uint amount0, uint amount1); event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); event Swap(address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to); event Sync(uint reserve0, uint reserve1); event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1); event SetCommunityVault(address indexed communityVault_); event Transfer(address indexed from, address indexed to, uint amount); event Approval(address indexed owner, address indexed spender, uint amount); // simple re-entrancy check uint internal _unlocked; modifier lock() { require(_unlocked == 1); _unlocked = 2; _; _unlocked = 1; } constructor(address blastGovernor_) { __BlastGovernorClaimableSetup_init(blastGovernor_); } function initialize( address _blastGovernor, address _blastPoints, address _blastPointsOperator, address _token0, address _token1, bool _stable, address _communityVault ) external { require(factory == address(0), "Initialized"); factory = msg.sender; __BlastERC20RebasingManage__init(_blastGovernor, _blastPoints, _blastPointsOperator); (token0, token1, stable, communityVault) = (_token0, _token1, _stable, _communityVault); fees = address(new PairFees(_blastGovernor, _blastPoints, _blastPointsOperator, msg.sender, _token0, _token1)); _unlocked = 1; if (_stable) { name = string(abi.encodePacked("StableV1 AMM - ", IERC20Metadata(_token0).symbol(), "/", IERC20Metadata(_token1).symbol())); symbol = string(abi.encodePacked("sAMM-", IERC20Metadata(_token0).symbol(), "/", IERC20Metadata(_token1).symbol())); } else { name = string(abi.encodePacked("VolatileV1 AMM - ", IERC20Metadata(_token0).symbol(), "/", IERC20Metadata(_token1).symbol())); symbol = string(abi.encodePacked("vAMM-", IERC20Metadata(_token0).symbol(), "/", IERC20Metadata(_token1).symbol())); } decimals0 = 10 ** IERC20Metadata(_token0).decimals(); decimals1 = 10 ** IERC20Metadata(_token1).decimals(); observations.push(Observation(block.timestamp, 0, 0)); } function setCommunityVault(address communityVault_) external virtual override { IPairFactory factoryCache = IPairFactory(factory); require(factoryCache.hasRole(factoryCache.PAIRS_ADMINISTRATOR_ROLE(), msg.sender), "ACCESS_DENIED"); communityVault = communityVault_; emit SetCommunityVault(communityVault_); } function _checkAccessForManageBlastERC20Rebasing() internal virtual override { IPairFactory factoryCache = IPairFactory(factory); require( msg.sender == address(factoryCache) || factoryCache.hasRole(factoryCache.PAIRS_ADMINISTRATOR_ROLE(), msg.sender), "ACCESS_DENIED" ); } function observationLength() external view returns (uint) { return observations.length; } function lastObservation() public view returns (Observation memory) { return observations[observations.length - 1]; } function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) { return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1); } function tokens() external view returns (address, address) { return (token0, token1); } function isStable() external view returns (bool) { return stable; } // claim accumulated but unclaimed fees (viewable via claimable0 and claimable1) function claimFees() external returns (uint claimed0, uint claimed1) { _updateFor(msg.sender); claimed0 = claimable0[msg.sender]; claimed1 = claimable1[msg.sender]; if (claimed0 > 0 || claimed1 > 0) { claimable0[msg.sender] = 0; claimable1[msg.sender] = 0; PairFees(fees).claimFeesFor(msg.sender, claimed0, claimed1); emit Claim(msg.sender, msg.sender, claimed0, claimed1); } } // Accrue fees on token0 function _update0(uint amount) internal { // get protocol fee uint256 _protocolFee = 0; address communityVaultCache = communityVault; if (communityVaultCache != address(0)) { uint256 _protocolFeeRate = IPairFactory(factory).getProtocolFee(address(this)); if (_protocolFeeRate > 0) { _protocolFee = (amount * _protocolFeeRate) / 10000; _safeTransfer(token0, communityVaultCache, _protocolFee); amount -= _protocolFee; } } if (amount > 0) { _safeTransfer(token0, fees, amount); uint256 _ratio = (amount * 1e18) / totalSupply; // 1e18 adjustment is removed during claim if (_ratio > 0) { index0 += _ratio; } } emit Fees(msg.sender, amount + _protocolFee, 0); } // Accrue fees on token1 function _update1(uint amount) internal { // get protocol fee uint256 _protocolFee = 0; address communityVaultCache = communityVault; if (communityVaultCache != address(0)) { uint256 _protocolFeeRate = IPairFactory(factory).getProtocolFee(address(this)); if (_protocolFeeRate > 0) { _protocolFee = (amount * _protocolFeeRate) / 10000; _safeTransfer(token1, communityVaultCache, _protocolFee); // transfer the fees out to PairFees amount -= _protocolFee; } } if (amount > 0) { _safeTransfer(token1, fees, amount); uint256 _ratio = (amount * 1e18) / totalSupply; if (_ratio > 0) { index1 += _ratio; } } emit Fees(msg.sender, 0, amount + _protocolFee); } // this function MUST be called on any balance changes, otherwise can be used to infinitely claim fees // Fees are segregated from core funds, so fees can never put liquidity at risk function _updateFor(address recipient) internal { uint _supplied = balanceOf[recipient]; // get LP balance of `recipient` if (_supplied > 0) { uint _supplyIndex0 = supplyIndex0[recipient]; // get last adjusted index0 for recipient uint _supplyIndex1 = supplyIndex1[recipient]; uint _index0 = index0; // get global index0 for accumulated fees uint _index1 = index1; supplyIndex0[recipient] = _index0; // update user current position to global position supplyIndex1[recipient] = _index1; uint _delta0 = _index0 - _supplyIndex0; // see if there is any difference that need to be accrued uint _delta1 = _index1 - _supplyIndex1; if (_delta0 > 0) { uint _share = (_supplied * _delta0) / 1e18; // add accrued difference for each supplied token claimable0[recipient] += _share; } if (_delta1 > 0) { uint _share = (_supplied * _delta1) / 1e18; claimable1[recipient] += _share; } } else { supplyIndex0[recipient] = index0; // new users are set to the default global state supplyIndex1[recipient] = index1; } } function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) { _reserve0 = reserve0; _reserve1 = reserve1; _blockTimestampLast = blockTimestampLast; } // update reserves and, on the first call per block, price accumulators function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1) internal { uint blockTimestamp = block.timestamp; uint timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) { reserve0CumulativeLast += _reserve0 * timeElapsed; reserve1CumulativeLast += _reserve1 * timeElapsed; } Observation memory _point = lastObservation(); timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event if (timeElapsed > periodSize) { observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast)); } reserve0 = balance0; reserve1 = balance1; blockTimestampLast = blockTimestamp; emit Sync(reserve0, reserve1); } // produces the cumulative price using counterfactuals to save gas and avoid a call to sync. function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) { blockTimestamp = block.timestamp; reserve0Cumulative = reserve0CumulativeLast; reserve1Cumulative = reserve1CumulativeLast; // if time has elapsed since the last update on the pair, mock the accumulated price values (uint _reserve0, uint _reserve1, uint _blockTimestampLast) = getReserves(); if (_blockTimestampLast != blockTimestamp) { // subtraction overflow is desired uint timeElapsed = blockTimestamp - _blockTimestampLast; reserve0Cumulative += _reserve0 * timeElapsed; reserve1Cumulative += _reserve1 * timeElapsed; } } // as per `current`, however allows user configured granularity, up to the full window size function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) { uint[] memory _prices = sample(tokenIn, amountIn, granularity, 1); uint priceAverageCumulative; for (uint i = 0; i < _prices.length; i++) { priceAverageCumulative += _prices[i]; } return priceAverageCumulative / granularity; } // returns a memory set of twap prices function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) { return sample(tokenIn, amountIn, points, 1); } function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) { uint[] memory _prices = new uint[](points); uint length = observations.length - 1; uint i = length - (points * window); uint nextIndex = 0; uint index = 0; for (; i < length; i += window) { nextIndex = i + window; uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp; uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed; uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed; _prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); // index < length; length cannot overflow unchecked { index = index + 1; } } return _prices; } // this low-level function should be called by addLiquidity functions in Router.sol, which performs important safety checks // standard uniswap v2 implementation function mint(address to) external lock returns (uint liquidity) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); uint _balance0 = IERC20(token0).balanceOf(address(this)); uint _balance1 = IERC20(token1).balanceOf(address(this)); uint _amount0 = _balance0 - _reserve0; uint _amount1 = _balance1 - _reserve1; uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee if (_totalSupply == 0) { liquidity = Math.sqrt(_amount0 * _amount1) - MINIMUM_LIQUIDITY; _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens if (stable) { require((_amount0 * 1e18) / decimals0 == (_amount1 * 1e18) / decimals1, "Pair: stable deposits must be equal"); require(_k(_amount0, _amount1) > MINIMUM_K, "Pair: stable deposits must be above minimum k"); } } else { liquidity = Math.min((_amount0 * _totalSupply) / _reserve0, (_amount1 * _totalSupply) / _reserve1); } require(liquidity > 0, "ILM"); // Pair: INSUFFICIENT_LIQUIDITY_MINTED _mint(to, liquidity); _update(_balance0, _balance1, _reserve0, _reserve1); emit Mint(msg.sender, _amount0, _amount1); } // this low-level function should be called from a contract which performs important safety checks // standard uniswap v2 implementation function burn(address to) external lock returns (uint amount0, uint amount1) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); (address _token0, address _token1) = (token0, token1); uint _balance0 = IERC20(_token0).balanceOf(address(this)); uint _balance1 = IERC20(_token1).balanceOf(address(this)); uint _liquidity = balanceOf[address(this)]; uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee amount0 = (_liquidity * _balance0) / _totalSupply; // using balances ensures pro-rata distribution amount1 = (_liquidity * _balance1) / _totalSupply; // using balances ensures pro-rata distribution require(amount0 > 0 && amount1 > 0, "ILB"); // Pair: INSUFFICIENT_LIQUIDITY_BURNED _burn(address(this), _liquidity); _safeTransfer(_token0, to, amount0); _safeTransfer(_token1, to, amount1); _balance0 = IERC20(_token0).balanceOf(address(this)); _balance1 = IERC20(_token1).balanceOf(address(this)); _update(_balance0, _balance1, _reserve0, _reserve1); emit Burn(msg.sender, amount0, amount1, to); } // this low-level function should be called from a contract which performs important safety checks function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock { IPairFactory factoryCache = IPairFactory(factory); { require(!factoryCache.isPaused()); address hookTarget = factoryCache.getHookTarget(address(this)); if (hookTarget != address(0)) { IPairCallee(hookTarget).hook(msg.sender, amount0Out, amount1Out, data); } } require(amount0Out > 0 || amount1Out > 0, "IOA"); // Pair: INSUFFICIENT_OUTPUT_AMOUNT (uint _reserve0, uint _reserve1) = (reserve0, reserve1); require(amount0Out < _reserve0 && amount1Out < _reserve1, "IL"); // Pair: INSUFFICIENT_LIQUIDITY uint _balance0; uint _balance1; { // scope for _token{0,1}, avoids stack too deep errors (address _token0, address _token1) = (token0, token1); require(to != _token0 && to != _token1, "IT"); // Pair: INVALID_TO if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens if (data.length > 0) IPairCallee(to).hook(msg.sender, amount0Out, amount1Out, data); // callback, used for flash loans _balance0 = IERC20(_token0).balanceOf(address(this)); _balance1 = IERC20(_token1).balanceOf(address(this)); } uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0; uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0; require(amount0In > 0 || amount1In > 0, "IIA"); // Pair: INSUFFICIENT_INPUT_AMOUNT { // scope for reserve{0,1}Adjusted, avoids stack too deep errors (address _token0, address _token1) = (token0, token1); if (amount0In > 0) _update0((amount0In * factoryCache.getFee(address(this), stable)) / 10000); // accrue fees for token0 and move them out of pool if (amount1In > 0) _update1((amount1In * factoryCache.getFee(address(this), stable)) / 10000); // accrue fees for token1 and move them out of pool _balance0 = IERC20(_token0).balanceOf(address(this)); // since we removed tokens, we need to reconfirm balances, can also simply use previous balance - amountIn/ 10000, but doing balanceOf again as safety check _balance1 = IERC20(_token1).balanceOf(address(this)); // The curve, either x3y+y3x for stable pools, or x*y for volatile pools require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), "K"); // Pair: K } _update(_balance0, _balance1, _reserve0, _reserve1); emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to); } // force balances to match reserves function skim(address to) external lock { (address _token0, address _token1) = (token0, token1); _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)) - (reserve0)); _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)) - (reserve1)); } // force reserves to match balances function sync() external lock { _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1); } function _f(uint256 x0, uint256 y) internal pure returns (uint256) { uint256 _a = (x0 * y) / 1e18; uint256 _b = ((x0 * x0) / 1e18 + (y * y) / 1e18); return (_a * _b) / 1e18; } function _d(uint x0, uint y) internal pure returns (uint) { return (3 * x0 * ((y * y) / 1e18)) / 1e18 + ((((x0 * x0) / 1e18) * x0) / 1e18); } function _get_y(uint x0, uint xy, uint y) internal view returns (uint) { for (uint256 i = 0; i < 255; i++) { uint256 k = _f(x0, y); if (k < xy) { // there are two cases where dy == 0 // case 1: The y is converged and we find the correct answer // case 2: _d(x0, y) is too large compare to (xy - k) and the rounding error // screwed us. // In this case, we need to increase y by 1 uint256 dy = ((xy - k) * 1e18) / _d(x0, y); if (dy == 0) { if (k == xy) { // We found the correct answer. Return y return y; } if (_k(x0, y + 1) > xy) { // If _k(x0, y + 1) > xy, then we are close to the correct answer. // There's no closer answer than y + 1 return y + 1; } dy = 1; } y = y + dy; } else { uint256 dy = ((k - xy) * 1e18) / _d(x0, y); if (dy == 0) { if (k == xy || _f(x0, y - 1) < xy) { // Likewise, if k == xy, we found the correct answer. // If _f(x0, y - 1) < xy, then we are close to the correct answer. // There's no closer answer than "y" // It's worth mentioning that we need to find y where f(x0, y) >= xy // As a result, we can't return y - 1 even it's closer to the correct answer return y; } dy = 1; } y = y - dy; } } revert("!y"); } function getAmountOut(uint amountIn, address tokenIn) external view returns (uint) { (uint _reserve0, uint _reserve1) = (reserve0, reserve1); amountIn -= (amountIn * IPairFactory(factory).getFee(address(this), stable)) / 10000; // remove fee from amount received return _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1); } function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) { if (stable) { uint xy = _k(_reserve0, _reserve1); _reserve0 = (_reserve0 * 1e18) / decimals0; _reserve1 = (_reserve1 * 1e18) / decimals1; (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); amountIn = tokenIn == token0 ? (amountIn * 1e18) / decimals0 : (amountIn * 1e18) / decimals1; uint y = reserveB - _get_y(amountIn + reserveA, xy, reserveB); return (y * (tokenIn == token0 ? decimals1 : decimals0)) / 1e18; } else { (uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0); return (amountIn * reserveB) / (reserveA + amountIn); } } function _k(uint x, uint y) internal view returns (uint) { if (stable) { uint _x = (x * 1e18) / decimals0; uint _y = (y * 1e18) / decimals1; uint _a = (_x * _y) / 1e18; uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18); return (_a * _b) / 1e18; // x3y+y3x >= k } else { return x * y; // xy >= k } } function _mint(address dst, uint amount) internal { _updateFor(dst); // balances must be updated on mint/burn/transfer totalSupply += amount; balanceOf[dst] += amount; emit Transfer(address(0), dst, amount); } function _burn(address dst, uint amount) internal { _updateFor(dst); totalSupply -= amount; balanceOf[dst] -= amount; emit Transfer(dst, address(0), amount); } function approve(address spender, uint amount) external returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external { require(deadline >= block.timestamp, "Pair: EXPIRED"); DOMAIN_SEPARATOR = keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256(bytes("1")), block.chainid, address(this) ) ); bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) ) ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress != address(0) && recoveredAddress == owner, "Pair: INVALID_SIGNATURE"); allowance[owner][spender] = value; emit Approval(owner, spender, value); } function transfer(address dst, uint amount) external returns (bool) { _transferTokens(msg.sender, dst, amount); return true; } function transferFrom(address src, address dst, uint amount) external returns (bool) { address spender = msg.sender; uint spenderAllowance = allowance[src][spender]; if (spender != src && spenderAllowance != type(uint).max) { uint newAllowance = spenderAllowance - amount; allowance[src][spender] = newAllowance; emit Approval(src, spender, newAllowance); } _transferTokens(src, dst, amount); return true; } function _transferTokens(address src, address dst, uint amount) internal { _updateFor(src); // update fee position for src _updateFor(dst); // update fee position for dst balanceOf[src] -= amount; balanceOf[dst] += amount; emit Transfer(src, dst, amount); } function _safeTransfer(address token, address to, uint256 value) internal { require(token.code.length > 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } function _safeApprove(address token, address spender, uint256 value) internal { require(token.code.length > 0); require( (value == 0) || (IERC20(token).allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.approve.selector, spender, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ 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; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. 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); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ 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); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 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; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ 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); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 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; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ 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); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ 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; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ 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); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import {YieldMode, IERC20Rebasing} from "../../integration/interfaces/IERC20Rebasing.sol"; interface IPair { function setCommunityVault(address communityVault_) external; function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1); function claimFees() external returns (uint, uint); function tokens() external view returns (address, address); function token0() external view returns (address); function token1() external view returns (address); function transferFrom(address src, address dst, uint amount) external returns (bool); function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; function burn(address to) external returns (uint amount0, uint amount1); function mint(address to) external returns (uint liquidity); function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast); function getAmountOut(uint, address) external view returns (uint); function name() external view returns (string memory); function symbol() external view returns (string memory); function totalSupply() external view returns (uint); function decimals() external view returns (uint8); function claimable0(address _user) external view returns (uint); function claimable1(address _user) external view returns (uint); function isStable() external view returns (bool); function initialize( address blastGovernor, address blastPoints, address blastPointsOperator, address token0, address token1, bool isStable, address communityVault ) external; function fees() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IPairCallee { function hook(address sender, uint amount0, uint amount1, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IPairFactory { event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint); event SetPaused(bool state); event SetCommunityVaultFactory(address indexed communityVaultFactory); event SetIsPublicPoolCreationMode(bool mode); event SetProtocolFee(uint256 fee); event SetCustomProtocolFee(address indexed pair, uint256 fee); event SetCustomFee(address indexed pair, uint256 fee); event SetFee(bool stable, uint256 fee); /** * @dev Emitted when the rebasing tokens governor address is set. * * @param oldRebasingTokensGovernor The previous address of the rebasing tokens governor. * @param newRebasingTokensGovernor The new address of the rebasing tokens governor. */ event SetRebasingTokensGovernor(address indexed oldRebasingTokensGovernor, address indexed newRebasingTokensGovernor); error IncorrcectFee(); error IncorrectPair(); error IdenticalAddress(); error PairExist(); function implementation() external view returns (address); function PAIRS_ADMINISTRATOR_ROLE() external view returns (bytes32); function FEES_MANAGER_ROLE() external view returns (bytes32); function PAIRS_CREATOR_ROLE() external view returns (bytes32); function hasRole(bytes32 role, address user) external view returns (bool); function allPairsLength() external view returns (uint); function isPair(address pair) external view returns (bool); function allPairs(uint index) external view returns (address); function getPair(address tokenA, address token, bool stable) external view returns (address); function createPair(address tokenA, address tokenB, bool stable) external returns (address pair); function pairs() external view returns (address[] memory); function getFee(address pair_, bool stable_) external view returns (uint256); function getHookTarget(address pair_) external view returns (address); function getProtocolFee(address pair_) external view returns (uint256); function isPaused() external view returns (bool); function isPublicPoolCreationMode() external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.19; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {BlastERC20RebasingManage} from "../integration/BlastERC20RebasingManage.sol"; import {IPairFactory} from "./interfaces/IPairFactory.sol"; // Pair Fees contract is used as a 1:1 pair relationship to split out fees, this ensures that the curve does not need to be modified for LP shares contract PairFees is BlastERC20RebasingManage { address internal immutable pair; // The pair it is bonded to address internal immutable token0; // token0 of pair, saved localy and statically for gas optimization address internal immutable token1; // Token1 of pair, saved localy and statically for gas optimization address internal immutable factory; // The pair factory constructor( address _blastGovernor, address _blastPoints, address _blastPointsOperator, address _factory, address _token0, address _token1 ) { __BlastERC20RebasingManage__init(_blastGovernor, _blastPoints, _blastPointsOperator); pair = msg.sender; token0 = _token0; token1 = _token1; factory = _factory; } function _safeTransfer(address token, address to, uint256 value) internal { require(token.code.length > 0); (bool success, bytes memory data) = token.call(abi.encodeWithSelector(IERC20.transfer.selector, to, value)); require(success && (data.length == 0 || abi.decode(data, (bool)))); } // Allow the pair to transfer fees to users function claimFeesFor(address recipient, uint amount0, uint amount1) external { require(msg.sender == pair); if (amount0 > 0) _safeTransfer(token0, recipient, amount0); if (amount1 > 0) _safeTransfer(token1, recipient, amount1); } function _checkAccessForManageBlastERC20Rebasing() internal virtual override { IPairFactory factoryCache = IPairFactory(factory); require( msg.sender == address(factoryCache) || factoryCache.hasRole(factoryCache.PAIRS_ADMINISTRATOR_ROLE(), msg.sender), "ACCESS_DENIED" ); } }
// SPDX-License-Identifier: MIT pragma solidity =0.8.19; import {YieldMode, IERC20Rebasing, IBlastERC20RebasingManage} from "./interfaces/IBlastERC20RebasingManage.sol"; import {IBlastPoints} from "./interfaces/IBlastPoints.sol"; import {BlastGovernorClaimableSetup} from "./BlastGovernorClaimableSetup.sol"; /** * @title BlastERC20RebasingManage * @dev Abstract contract designed to manage ERC20 rebasing tokens within the Blast ecosystem. * It provides functionalities to configure and claim tokens while ensuring that only authorized * entities can perform these operations. */ abstract contract BlastERC20RebasingManage is IBlastERC20RebasingManage, BlastGovernorClaimableSetup { /** * @dev Initializes the BlastERC20RebasingManage contract. Sets up the initial configuration * for managing ERC20 rebasing tokens within the Blast ecosystem. This includes setting the Blast Governor, * configuring the Blast Points, and assigning the Blast Points operator. * * @param blastGovernor_ The address of the Blast Governor to be used for governance processes. * @param blastPoints_ The address of the Blast Points contract, used for managing points within the ecosystem. * @param blastPointsOperator_ The address of the operator authorized to manage points in the Blast Points contract. * * Requirements: * - `blastGovernor_`, `blastPoints_` and `blastPointsOperator_` must not be the zero address. * * Emits an `AddressZero` error if any of the required addresses are zero. */ function __BlastERC20RebasingManage__init(address blastGovernor_, address blastPoints_, address blastPointsOperator_) internal { if (blastPoints_ == address(0) || blastPointsOperator_ == address(0)) { revert AddressZero(); } __BlastGovernorClaimableSetup_init(blastGovernor_); IBlastPoints(blastPoints_).configurePointsOperator(blastPointsOperator_); } /** * @dev Configures the rebasing parameters of a specified ERC20 rebasing token. * This function can only be called by addresses with the required access permissions. * Implementations of this contract should ensure that the `_checkAccessForManageBlastERC20Rebasing` * function is called to enforce access control. * * @param erc20Rebasing_ The address of the ERC20 rebasing token to configure. * @param mode_ The yield mode to apply to the token, determining how rebasing mechanics are handled. * @return A uint256 value that represents the outcome of the configuration operation, * which could be an updated token supply or another relevant metric, depending on the ERC20 rebasing token implementation. */ function configure(address erc20Rebasing_, YieldMode mode_) external virtual returns (uint256) { _checkAccessForManageBlastERC20Rebasing(); return IERC20Rebasing(erc20Rebasing_).configure(mode_); } /** * @dev Claims rebasing tokens on behalf of the caller and transfers them to a specified recipient. * This function can only be executed by addresses with the necessary access permissions. * * @param erc20Rebasing_ The address of the ERC20 rebasing token from which tokens are claimed. * @param recipient_ The recipient address to receive the claimed tokens. * @param amount_ The amount of tokens to claim. * @return The result of the claim operation, specific to the ERC20 rebasing token implementation. */ function claim(address erc20Rebasing_, address recipient_, uint256 amount_) external virtual returns (uint256) { _checkAccessForManageBlastERC20Rebasing(); return IERC20Rebasing(erc20Rebasing_).claim(recipient_, amount_); } /** * @dev Internal function to check if the message sender has the required permissions to manage ERC20 rebasing tokens. * Reverts the transaction if the sender is not authorized. */ function _checkAccessForManageBlastERC20Rebasing() internal virtual; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity =0.8.19; import {IBlastFull, YieldMode, GasMode} from "./interfaces/IBlastFull.sol"; import {IBlastGovernor} from "./interfaces/IBlastGovernor.sol"; /** * @title Blast Governor Claiamble Setup * @dev Abstract contract for setting up a governor in the Blast ecosystem. * This contract provides an initialization function to configure a governor address * for the Blast protocol, utilizing the `IBlast` interface. */ abstract contract BlastGovernorClaimableSetup { /// @dev Error thrown when an operation involves a zero address where a valid address is required. error AddressZero(); /** * @dev Initializes the governor and claimable configuration for the Blast protocol. * This internal function is meant to be called in the initialization process * of a derived contract that sets up governance. * * @param blastGovernor_ The address of the governor to be configured in the Blast protocol. * Must be a non-zero address. */ function __BlastGovernorClaimableSetup_init(address blastGovernor_) internal { if (blastGovernor_ == address(0)) { revert AddressZero(); } IBlastFull(0x4300000000000000000000000000000000000002).configure(YieldMode.CLAIMABLE, GasMode.CLAIMABLE, blastGovernor_); if (blastGovernor_.code.length > 0) { IBlastGovernor(blastGovernor_).addGasHolder(address(this)); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; import {YieldMode, IERC20Rebasing} from "./IERC20Rebasing.sol"; /** * @title IBlastERC20RebasingManage Interface * @dev Interface for managing ERC20 rebasing tokens within the Blast ecosystem. It provides * the necessary functions to configure and claim tokens, ensuring that only authorized * entities can perform these operations. This interface mandates the implementation of * access control checks to secure the rebasing token management. */ interface IBlastERC20RebasingManage { /** * @dev Configures the rebasing parameters of a specified ERC20 rebasing token. * This function can only be called by addresses with the required access permissions. * Implementations of this contract should ensure that the `_checkAccessForManageBlastERC20Rebasing` * function is called to enforce access control. * * @param erc20Rebasing_ The address of the ERC20 rebasing token to configure. * @param mode_ The yield mode to apply to the token, determining how rebasing mechanics are handled. * @return A uint256 value that represents the outcome of the configuration operation, * which could be an updated token supply or another relevant metric, depending on the ERC20 rebasing token implementation. */ function configure(address erc20Rebasing_, YieldMode mode_) external returns (uint256); /** * @dev Claims rebasing tokens on behalf of the caller and transfers them to a specified recipient. * This function can only be executed by addresses with the necessary access permissions. * * @param erc20Rebasing_ The address of the ERC20 rebasing token from which tokens are claimed. * @param recipient_ The recipient address to receive the claimed tokens. * @param amount_ The amount of tokens to claim. * @return The result of the claim operation, specific to the ERC20 rebasing token implementation. */ function claim(address erc20Rebasing_, address recipient_, uint256 amount_) external returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; /** * @title IBlastFull Interface * @dev Interface for interacting with the Blast protocol, specifically for configuring * governance settings. This interface abstracts the function to set up a governor * within the Blast ecosystem. */ enum GasMode { VOID, CLAIMABLE } enum YieldMode { AUTOMATIC, VOID, CLAIMABLE } interface IBlastFull { // configure function configureContract(address contractAddress, YieldMode _yield, GasMode gasMode, address governor) external; function configure(YieldMode _yield, GasMode gasMode, address governor) external; // base configuration options function configureClaimableYield() external; function configureClaimableYieldOnBehalf(address contractAddress) external; function configureAutomaticYield() external; function configureAutomaticYieldOnBehalf(address contractAddress) external; function configureVoidYield() external; function configureVoidYieldOnBehalf(address contractAddress) external; function configureClaimableGas() external; function configureClaimableGasOnBehalf(address contractAddress) external; function configureVoidGas() external; function configureVoidGasOnBehalf(address contractAddress) external; function configureGovernor(address _governor) external; function configureGovernorOnBehalf(address _newGovernor, address contractAddress) external; // claim yield function claimYield(address contractAddress, address recipientOfYield, uint256 amount) external returns (uint256); function claimAllYield(address contractAddress, address recipientOfYield) external returns (uint256); // claim gas function claimAllGas(address contractAddress, address recipientOfGas) external returns (uint256); // NOTE: can be off by 1 bip function claimGasAtMinClaimRate(address contractAddress, address recipientOfGas, uint256 minClaimRateBips) external returns (uint256); function claimMaxGas(address contractAddress, address recipientOfGas) external returns (uint256); function claimGas( address contractAddress, address recipientOfGas, uint256 gasToClaim, uint256 gasSecondsToConsume ) external returns (uint256); // read functions function readClaimableYield(address contractAddress) external view returns (uint256); function readYieldConfiguration(address contractAddress) external view returns (uint8); function readGasParams( address contractAddress ) external view returns (uint256 etherSeconds, uint256 etherBalance, uint256 lastUpdated, GasMode); function isGovernor(address) external view returns (bool); function governorMap(address) external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity =0.8.19; import {GasMode} from "./IBlastFull.sol"; /** * @title IBlastGovernor * @dev Interface for the BlastGovernor contract. */ interface IBlastGovernor { /** * @dev Structure representing gas parameters. * @param contractAddress Address of the gas holder contract. * @param etherSeconds Accumulated ether seconds. * @param etherBalance Ether balance. * @param lastUpdated Timestamp of the last update. * @param gasMode Current gas mode. */ struct GasParamsResult { address contractAddress; uint256 etherSeconds; uint256 etherBalance; uint256 lastUpdated; GasMode gasMode; } /** * @dev Emitted when a gas holder is added. * @param contractAddress The address of the added gas holder contract. */ event AddGasHolder(address indexed contractAddress); /** * @dev Emitted when gas is claimed. * @param caller The address of the caller who initiated the claim. * @param recipient The address of the recipient who receives the claimed gas. * @param gasHolders The addresses of the gas holders from which gas was claimed. * @param totalClaimedAmount The total amount of gas claimed. */ event ClaimGas(address indexed caller, address indexed recipient, address[] gasHolders, uint256 totalClaimedAmount); /** * @notice Adds a gas holder. * @dev Adds a contract to the list of gas holders. * @param contractAddress_ The address of the gas holder contract. */ function addGasHolder(address contractAddress_) external; /** * @notice Claims all gas for a recipient within the specified range. * @param recipient_ The address of the recipient. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return totalClaimedGas The total amount of gas claimed. */ function claimAllGas(address recipient_, uint256 offset_, uint256 limit_) external returns (uint256 totalClaimedGas); /** * @notice Claims all gas for a recipient from specified gas holders. * @param recipient_ The address of the recipient. * @param holders_ The addresses of the gas holders. * @return totalClaimedGas The total amount of gas claimed. */ function claimAllGasFromSpecifiedGasHolders(address recipient_, address[] memory holders_) external returns (uint256 totalClaimedGas); /** * @notice Claims gas at minimum claim rate for a recipient within the specified range. * @param recipient_ The address of the recipient. * @param minClaimRateBips_ The minimum claim rate in basis points. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return totalClaimedGas The total amount of gas claimed. */ function claimGasAtMinClaimRate( address recipient_, uint256 minClaimRateBips_, uint256 offset_, uint256 limit_ ) external returns (uint256 totalClaimedGas); /** * @notice Claims gas at minimum claim rate for a recipient from specified gas holders. * @param recipient_ The address of the recipient. * @param minClaimRateBips_ The minimum claim rate in basis points. * @param holders_ The addresses of the gas holders. * @return totalClaimedGas The total amount of gas claimed. */ function claimGasAtMinClaimRateFromSpecifiedGasHolders( address recipient_, uint256 minClaimRateBips_, address[] memory holders_ ) external returns (uint256 totalClaimedGas); /** * @notice Claims maximum gas for a recipient within the specified range. * @param recipient_ The address of the recipient. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return totalClaimedGas The total amount of gas claimed. */ function claimMaxGas(address recipient_, uint256 offset_, uint256 limit_) external returns (uint256 totalClaimedGas); /** * @notice Claims maximum gas for a recipient from specified gas holders. * @param recipient_ The address of the recipient. * @param holders_ The addresses of the gas holders. * @return totalClaimedGas The total amount of gas claimed. */ function claimMaxGasFromSpecifiedGasHolders(address recipient_, address[] memory holders_) external returns (uint256 totalClaimedGas); /** * @notice Claims a specific amount of gas for a recipient within the specified range. * @param recipient_ The address of the recipient. * @param gasToClaim_ The amount of gas to claim. * @param gasSecondsToConsume_ The amount of gas seconds to consume. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return totalClaimedGas The total amount of gas claimed. */ function claimGas( address recipient_, uint256 gasToClaim_, uint256 gasSecondsToConsume_, uint256 offset_, uint256 limit_ ) external returns (uint256 totalClaimedGas); /** * @notice Claims a specific amount of gas for a recipient from specified gas holders. * @param recipient_ The address of the recipient. * @param gasToClaim_ The amount of gas to claim. * @param gasSecondsToConsume_ The amount of gas seconds to consume. * @param holders_ The addresses of the gas holders. * @return totalClaimedGas The total amount of gas claimed. */ function claimGasFromSpecifiedGasHolders( address recipient_, uint256 gasToClaim_, uint256 gasSecondsToConsume_, address[] memory holders_ ) external returns (uint256 totalClaimedGas); /** * @notice Reads gas parameters within the specified range. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return gasHoldersParams The gas parameters of the gas holders. */ function readGasParams(uint256 offset_, uint256 limit_) external view returns (GasParamsResult[] memory gasHoldersParams); /** * @notice Reads gas parameters from specified gas holders. * @param holders_ The addresses of the gas holders. * @return gasHoldersParams The gas parameters of the gas holders. */ function readGasParamsFromSpecifiedGasHolders( address[] memory holders_ ) external view returns (GasParamsResult[] memory gasHoldersParams); /** * @notice Checks if a contract is a registered gas holder. * @param contractAddress_ The address of the contract. * @return isRegistered Whether the contract is a registered gas holder. */ function isRegisteredGasHolder(address contractAddress_) external view returns (bool isRegistered); /** * @notice Lists gas holders within the specified range. * @param offset_ The offset to start from. * @param limit_ The maximum number of gas holders to process. * @return gasHolders The addresses of the gas holders. */ function listGasHolders(uint256 offset_, uint256 limit_) external view returns (address[] memory gasHolders); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.0; interface IBlastPoints { function configurePointsOperator(address operator) external; function configurePointsOperatorOnBehalf(address contractAddress, address operator) external; }
// SPDX-License-Identifier: MIT pragma solidity =0.8.19; enum YieldMode { AUTOMATIC, VOID, CLAIMABLE } interface IERC20Rebasing { // changes the yield mode of the caller and update the balance // to reflect the configuration function configure(YieldMode) external returns (uint256); // "claimable" yield mode accounts can call this this claim their yield // to another address function claim(address recipient, uint256 amount) external returns (uint256); // read the claimable amount for an account function getClaimableAmount(address account) external view returns (uint256); /// @notice Query an account's configured yield mode. /// @param account Address to query the configuration. /// @return Configured yield mode. function getConfiguration(address account) external view returns (YieldMode); }
{ "evmVersion": "paris", "viaIR": true, "optimizer": { "enabled": true, "runs": 2000 }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract ABI
API[{"inputs":[{"internalType":"address","name":"blastGovernor_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AddressZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Burn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Fees","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"communityVault_","type":"address"}],"name":"SetCommunityVault","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1In","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount0Out","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1Out","type":"uint256"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"reserve0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserve1","type":"uint256"}],"name":"Sync","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":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blockTimestampLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"burn","outputs":[{"internalType":"uint256","name":"amount0","type":"uint256"},{"internalType":"uint256","name":"amount1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"erc20Rebasing_","type":"address"},{"internalType":"address","name":"recipient_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[{"internalType":"uint256","name":"claimed0","type":"uint256"},{"internalType":"uint256","name":"claimed1","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"claimable1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"communityVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"erc20Rebasing_","type":"address"},{"internalType":"enum YieldMode","name":"mode_","type":"uint8"}],"name":"configure","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"currentCumulativePrices","outputs":[{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"},{"internalType":"uint256","name":"blockTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fees","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"tokenIn","type":"address"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint256","name":"_reserve0","type":"uint256"},{"internalType":"uint256","name":"_reserve1","type":"uint256"},{"internalType":"uint256","name":"_blockTimestampLast","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"index1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_blastGovernor","type":"address"},{"internalType":"address","name":"_blastPoints","type":"address"},{"internalType":"address","name":"_blastPointsOperator","type":"address"},{"internalType":"address","name":"_token0","type":"address"},{"internalType":"address","name":"_token1","type":"address"},{"internalType":"bool","name":"_stable","type":"bool"},{"internalType":"address","name":"_communityVault","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isStable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastObservation","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"}],"internalType":"struct Pair.Observation","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadata","outputs":[{"internalType":"uint256","name":"dec0","type":"uint256"},{"internalType":"uint256","name":"dec1","type":"uint256"},{"internalType":"uint256","name":"r0","type":"uint256"},{"internalType":"uint256","name":"r1","type":"uint256"},{"internalType":"bool","name":"st","type":"bool"},{"internalType":"address","name":"t0","type":"address"},{"internalType":"address","name":"t1","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"observationLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"observations","outputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"reserve0Cumulative","type":"uint256"},{"internalType":"uint256","name":"reserve1Cumulative","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"points","type":"uint256"}],"name":"prices","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"granularity","type":"uint256"}],"name":"quote","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve0CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reserve1CumulativeLast","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"points","type":"uint256"},{"internalType":"uint256","name":"window","type":"uint256"}],"name":"sample","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"communityVault_","type":"address"}],"name":"setCommunityVault","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyIndex0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"supplyIndex1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount0Out","type":"uint256"},{"internalType":"uint256","name":"amount1Out","type":"uint256"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokens","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
Loading...
Loading
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.