diff --git a/contracts/connectors/curve_gauge.sol b/contracts/connectors/curve_gauge.sol new file mode 100644 index 0000000..7fd1745 --- /dev/null +++ b/contracts/connectors/curve_gauge.sol @@ -0,0 +1,241 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +// import files from common directory +import { Stores } from "../common/stores.sol"; +import { DSMath } from "../common/math.sol"; +import { TokenInterface } from "../common/interfaces.sol"; + +interface IGauge { + function claim_rewards() external; + function deposit(uint256 value) external; + function withdraw(uint256 value) external; + function lp_token() external view returns(address token); + function rewarded_token() external view returns(address token); + function crv_token() external view returns(address token); + function balanceOf(address user) external view returns(uint256 amt); +} + +interface IMintor{ + function mint(address gauge) external; +} + +interface ICurveGaugeMapping { + + struct GaugeData { + address gaugeAddress; + bool rewardToken; + } + + function gaugeMapping(bytes32) external view returns(GaugeData memory); +} + +contract GaugeHelper is DSMath, Stores{ + function getCurveGaugeMappingAddr() internal virtual view returns (address){ + return 0x1C800eF1bBfE3b458969226A96c56B92a069Cc92; + } + + function getCurveMintorAddr() internal virtual view returns (address){ + return 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0; + } + + /** + * @dev Convert String to bytes32. + */ + function stringToBytes32(string memory str) internal pure returns (bytes32 result) { + require(bytes(str).length != 0, "string-empty"); + // solium-disable-next-line security/no-inline-assembly + assembly { + result := mload(add(str, 32)) + } + } +} + +contract CurveGaugeEvent is GaugeHelper { + event LogDeposit( + string indexed gaugePoolName, + uint amount, + uint getId, + uint setId + ); + + event LogWithdraw( + string indexed gaugePoolName, + uint amount, + uint getId, + uint setId + ); + + event LogClaimedReward( + string indexed gaugePoolName, + uint amount, + uint rewardAmt, + uint setId, + uint setIdReward + ); + + function emitLogWithdraw(string memory gaugePoolName, uint _amt, uint getId, uint setId) internal { + emit LogWithdraw(gaugePoolName, _amt, getId, setId); + bytes32 _eventCodeWithdraw = keccak256("LogWithdraw(string,uint256,uint256,uint256)"); + bytes memory _eventParamWithdraw = abi.encode(gaugePoolName, _amt, getId, setId); + emitEvent(_eventCodeWithdraw, _eventParamWithdraw); + } + + function emitLogClaimedReward(string memory gaugePoolName, uint crvAmt, uint rewardAmt, uint setIdCrv, uint setIdReward) internal { + emit LogClaimedReward(gaugePoolName, crvAmt, rewardAmt, setIdCrv, setIdReward); + bytes32 _eventCode = keccak256("LogClaimedReward(string,uint256,uint256,uint256,uint256)"); + bytes memory _eventParam = abi.encode(gaugePoolName, crvAmt, rewardAmt, setIdCrv, setIdReward); + emitEvent(_eventCode, _eventParam); + } +} + +contract CurveGauge is CurveGaugeEvent { + struct Balances{ + uint intialCRVBal; + uint intialRewardBal; + uint finalCRVBal; + uint finalRewardBal; + uint crvRewardAmt; + uint rewardAmt; + } + + /** + * @dev Deposit Cruve LP Token. + * @param gaugePoolName Curve gauge pool name. + * @param amt deposit amount. + * @param getId Get token amount at this ID from `InstaMemory` Contract. + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function deposit( + string calldata gaugePoolName, + uint amt, + uint getId, + uint setId + ) external payable { + uint _amt = getUint(getId, amt); + ICurveGaugeMapping curveGaugeMapping = ICurveGaugeMapping(getCurveGaugeMappingAddr()); + ICurveGaugeMapping.GaugeData memory curveGaugeData = curveGaugeMapping.gaugeMapping( + bytes32(stringToBytes32(gaugePoolName) + )); + require(curveGaugeData.gaugeAddress != address(0), "wrong-gauge-pool-name"); + IGauge gauge = IGauge(curveGaugeData.gaugeAddress); + TokenInterface lp_token = TokenInterface(address(gauge.lp_token())); + + _amt = _amt == uint(-1) ? lp_token.balanceOf(address(this)) : _amt; + lp_token.approve(address(curveGaugeData.gaugeAddress), _amt); + + gauge.deposit(_amt); + + setUint(setId, _amt); + + emit LogDeposit(gaugePoolName, _amt, getId, setId); + bytes32 _eventCode = keccak256("LogDeposit(string,uint256,uint256,uint256)"); + bytes memory _eventParam = abi.encode(gaugePoolName, _amt, getId, setId); + emitEvent(_eventCode, _eventParam); + } + + /** + * @dev Withdraw LP Token and claim both CRV and Reward token. + * @param gaugePoolName gauge pool name. + * @param amt LP token amount. + * @param getId Get LP token amount at this ID from `InstaMemory` Contract. + * @param setId Set LP token amount at this ID in `InstaMemory` Contract. + * @param setIdCrv Set CRV token reward amount at this ID in `InstaMemory` Contract. + * @param setIdReward Set reward amount at this ID in `InstaMemory` Contract. + */ + function withdraw( + string calldata gaugePoolName, + uint amt, + uint getId, + uint setId, + uint setIdCrv, + uint setIdReward + ) external payable { + uint _amt = getUint(getId, amt); + ICurveGaugeMapping curveGaugeMapping = ICurveGaugeMapping(getCurveGaugeMappingAddr()); + ICurveGaugeMapping.GaugeData memory curveGaugeData = curveGaugeMapping.gaugeMapping( + bytes32(stringToBytes32(gaugePoolName)) + ); + require(curveGaugeData.gaugeAddress != address(0), "wrong-gauge-pool-name"); + IGauge gauge = IGauge(curveGaugeData.gaugeAddress); + TokenInterface crv_token = TokenInterface(address(gauge.crv_token())); + TokenInterface rewarded_token; + Balances memory balances; + + _amt = _amt == uint(-1) ? gauge.balanceOf(address(this)) : _amt; + balances.intialCRVBal = crv_token.balanceOf(address(this)); + + if (curveGaugeData.rewardToken) { + rewarded_token = TokenInterface(address(gauge.rewarded_token())); + balances.intialRewardBal = rewarded_token.balanceOf(address(this)); + } + + IMintor(getCurveMintorAddr()).mint(curveGaugeData.gaugeAddress); + gauge.withdraw(_amt); + + balances.finalCRVBal = crv_token.balanceOf(address(this)); + balances.crvRewardAmt = sub(balances.finalCRVBal, balances.intialCRVBal); + + setUint(setId, _amt); + setUint(setIdCrv, balances.crvRewardAmt); + + if (curveGaugeData.rewardToken) { + balances.finalRewardBal = rewarded_token.balanceOf(address(this)); + balances.rewardAmt = sub(balances.finalRewardBal, balances.intialRewardBal); + setUint(setIdReward, balances.rewardAmt); + } + + emitLogWithdraw(gaugePoolName, _amt, getId, setId); + emitLogClaimedReward(gaugePoolName, balances.crvRewardAmt, balances.rewardAmt, setIdCrv, setIdReward); + } + + /** + * @dev Claim CRV Reward with Staked Reward token + * @param gaugePoolName gauge pool name. + * @param setId Set CRV reward amount at this ID in `InstaMemory` Contract. + * @param setIdReward Set token reward amount at this ID in `InstaMemory` Contract. + */ + function claimReward( + string calldata gaugePoolName, + uint setId, + uint setIdReward + ) external payable { + ICurveGaugeMapping curveGaugeMapping = ICurveGaugeMapping(getCurveGaugeMappingAddr()); + ICurveGaugeMapping.GaugeData memory curveGaugeData = curveGaugeMapping.gaugeMapping( + bytes32(stringToBytes32(gaugePoolName)) + ); + require(curveGaugeData.gaugeAddress != address(0), "wrong-gauge-pool-name"); + IMintor mintor = IMintor(getCurveMintorAddr()); + IGauge gauge = IGauge(curveGaugeData.gaugeAddress); + TokenInterface crv_token = TokenInterface(address(gauge.crv_token())); + TokenInterface rewarded_token; + Balances memory balances; + + if (curveGaugeData.rewardToken) { + rewarded_token = TokenInterface(address(gauge.rewarded_token())); + balances.intialRewardBal = rewarded_token.balanceOf(address(this)); + } + + balances.intialCRVBal = crv_token.balanceOf(address(this)); + + mintor.mint(curveGaugeData.gaugeAddress); + + balances.finalCRVBal = crv_token.balanceOf(address(this)); + balances.crvRewardAmt = sub(balances.finalCRVBal, balances.intialCRVBal); + + setUint(setId, balances.crvRewardAmt); + + if(curveGaugeData.rewardToken){ + balances.finalRewardBal = rewarded_token.balanceOf(address(this)); + balances.rewardAmt = sub(balances.finalRewardBal, balances.intialRewardBal); + setUint(setIdReward, balances.rewardAmt); + } + + emitLogClaimedReward(gaugePoolName, balances.crvRewardAmt, balances.rewardAmt, setId, setIdReward); + } +} + +contract ConnectCurveGauge is CurveGauge { + string public name = "Curve-Gauge-v1.0"; +} + diff --git a/contracts/mapping/curve_gauge_mapping.sol b/contracts/mapping/curve_gauge_mapping.sol new file mode 100644 index 0000000..f9c78cf --- /dev/null +++ b/contracts/mapping/curve_gauge_mapping.sol @@ -0,0 +1,109 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +interface ConnectorsInterface { + function chief(address) external view returns (bool); +} + +interface IndexInterface { + function master() external view returns (address); +} + +contract BytesHelper { + /** + * @dev Convert String to bytes32. + */ + function stringToBytes32(string memory str) internal pure returns (bytes32 result) { + require(bytes(str).length != 0, "String-Empty"); + // solium-disable-next-line security/no-inline-assembly + assembly { + result := mload(add(str, 32)) + } + } +} + +contract Helpers is BytesHelper { + address public constant connectors = 0xD6A602C01a023B98Ecfb29Df02FBA380d3B21E0c; + address public constant instaIndex = 0x2971AdFa57b20E5a416aE5a708A8655A9c74f723; + uint public version = 1; + + mapping (bytes32 => GaugeData) public gaugeMapping; + + struct GaugeData { + address gaugeAddress; + bool rewardToken; + } + + event LogAddGaugeMapping( + string gaugeName, + address gaugeAddress, + bool rewardToken + ); + + event LogRemoveGaugeMapping( + string gaugeName, + address gaugeAddress + ); + + modifier isChief virtual { + require( + ConnectorsInterface(connectors).chief(msg.sender) || + IndexInterface(instaIndex).master() == msg.sender, "not-Chief"); + _; + } + + function _addGaugeMapping( + string memory gaugeName, + address gaugeAddress, + bool rewardToken + ) internal { + require(gaugeAddress != address(0), "gaugeAddress-not-vaild"); + require(bytes(gaugeName).length <= 32, "Length-exceeds"); + bytes32 gaugeType = stringToBytes32(gaugeName); + require(gaugeMapping[gaugeType].gaugeAddress == address(0), "gaugePool-already-added"); + + gaugeMapping[gaugeType].gaugeAddress = gaugeAddress; + gaugeMapping[gaugeType].rewardToken = rewardToken; + + emit LogAddGaugeMapping(gaugeName, gaugeAddress, rewardToken); + } + + function addGaugeMappings( + string[] memory gaugeNames, + address[] memory gaugeAddresses, + bool[] memory rewardTokens + ) public isChief { + require(gaugeNames.length == gaugeAddresses.length && gaugeAddresses.length == rewardTokens.length, "length-not-match"); + for (uint32 i; i < gaugeNames.length; i++) { + _addGaugeMapping(gaugeNames[i], gaugeAddresses[i], rewardTokens[i]); + } + } + + function removeGaugeMapping(string memory gaugeName, address gaugeAddress) public isChief { + require(gaugeAddress != address(0), "gaugeAddress-not-vaild"); + bytes32 gaugeType = stringToBytes32(gaugeName); + require(gaugeMapping[gaugeType].gaugeAddress == gaugeAddress, "different-gauge-pool"); + + delete gaugeMapping[gaugeType]; + + emit LogRemoveGaugeMapping( + gaugeName, + gaugeAddress + ); + } +} + +contract CurveGaugeMapping is Helpers { + string constant public name = "Curve-Gauge-Mapping-v1"; + + constructor ( + string[] memory gaugeNames, + address[] memory gaugeAddresses, + bool[] memory rewardTokens + ) public { + require(gaugeNames.length == gaugeAddresses.length && gaugeAddresses.length == rewardTokens.length, "length-not-match"); + for (uint32 i; i < gaugeNames.length; i++) { + _addGaugeMapping(gaugeNames[i], gaugeAddresses[i], rewardTokens[i]); + } + } +} diff --git a/contracts/mapping/staking.sol b/contracts/mapping/staking.sol index e111481..9d9408b 100644 --- a/contracts/mapping/staking.sol +++ b/contracts/mapping/staking.sol @@ -40,6 +40,7 @@ contract BytesHelper { return (string(bytesArray)); } } + contract Helpers is BytesHelper { address public constant connectors = 0xD6A602C01a023B98Ecfb29Df02FBA380d3B21E0c; address public constant instaIndex = 0x2971AdFa57b20E5a416aE5a708A8655A9c74f723; @@ -113,7 +114,6 @@ contract Helpers is BytesHelper { } } - contract InstaStakingMapping is Helpers { string constant public name = "Staking-Mapping-v1"; } diff --git a/contracts/tests/MockCurveGauge.sol b/contracts/tests/MockCurveGauge.sol new file mode 100644 index 0000000..c42896a --- /dev/null +++ b/contracts/tests/MockCurveGauge.sol @@ -0,0 +1,27 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import { ConnectCurveGauge } from "../connectors/curve_gauge.sol"; + +contract MockCurveGauge is ConnectCurveGauge{ + address public curveMintorAddr; + address public curveGaugeMappingAddr; + + constructor(address _curveMintorAddr, address _curveGaugeMappingAddr) public { + curveMintorAddr = _curveMintorAddr; + curveGaugeMappingAddr = _curveGaugeMappingAddr; + } + + function emitEvent(bytes32 eventCode, bytes memory eventData) override internal {} + + function getCurveGaugeMappingAddr() override internal view returns (address) { + return curveGaugeMappingAddr; + } + + function getCurveMintorAddr() override internal view returns (address) { + return curveMintorAddr; + } + + function setUint(uint setId, uint val) override internal {} +} + diff --git a/contracts/tests/MockCurveGaugeMapping.sol b/contracts/tests/MockCurveGaugeMapping.sol new file mode 100644 index 0000000..0aa52ae --- /dev/null +++ b/contracts/tests/MockCurveGaugeMapping.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.6.0; +pragma experimental ABIEncoderV2; + +import { CurveGaugeMapping } from "../mapping/curve_gauge_mapping.sol"; + +contract MockCurveGaugeMapping is CurveGaugeMapping { + constructor( + string[] memory gaugeNames, + address[] memory gaugeAddresses, + bool[] memory rewardTokens + ) public CurveGaugeMapping(gaugeNames, gaugeAddresses, rewardTokens) { + } + modifier isChief override {_;} +} diff --git a/test/CurveGauge.js b/test/CurveGauge.js new file mode 100644 index 0000000..e9ef1c5 --- /dev/null +++ b/test/CurveGauge.js @@ -0,0 +1,157 @@ +const { + BN, // Big Number support + expectEvent, // Assertions for emitted events + expectRevert, // Assertions for transactions that should fail + balance, + ether +} = require('@openzeppelin/test-helpers'); + +const MockContract = artifacts.require("MockContract"); +const MockCurveGauge = artifacts.require('MockCurveGauge'); +const MockCurveGaugeMapping = artifacts.require('MockCurveGaugeMapping'); +const erc20ABI = require("./abi/erc20.js"); +const gaugeABI = require("./abi/curveGauge.json"); + +contract("ConnectCurveGauge", async accounts => { + const [sender, receiver] = accounts; + let mock, mockCurveGauge, mockCurveGaugeMapping; + + before(async function () { + mock = await MockContract.new(); + let names = ["compound", "susd"] + let poolAddress = [mock.address, mock.address] + let rewardArr = [false, true] + mockCurveGaugeMapping = await MockCurveGaugeMapping.new(names, poolAddress, rewardArr); + mockCurveGauge = await MockCurveGauge.new(mock.address, mockCurveGaugeMapping.address); + // lp_token = new web3.eth.Contract(erc20ABI, mock.address); + curveGauge = new web3.eth.Contract(gaugeABI, mock.address) + // mocking lp_token + let lp_token = await curveGauge.methods.lp_token().encodeABI(); + await mock.givenMethodReturnAddress(lp_token, mock.address); + // mocking crv_token + let crv_token = await curveGauge.methods.crv_token().encodeABI(); + await mock.givenMethodReturnAddress(crv_token, mock.address); + // mocking rewarded_token + let rewarded_token = await curveGauge.methods.rewarded_token().encodeABI(); + await mock.givenMethodReturnAddress(rewarded_token, mock.address); + + // await mockCurveGaugeMapping.addGaugeMapping('compound', mock.address, false); + // await mockCurveGaugeMapping.addGaugeMapping('susd', mock.address, true); + }) + + it('can deposit into compound gauge', async function() { + const tx = await mockCurveGauge.deposit( + "compound", + 10000000, + 0, + 0 + ) + expectEvent(tx, "LogDeposit", { + amount: "10000000", + getId: "0", + setId: "0" + }); + }); + + it('can claim reward from compound gauge', async function() { + const tx = await mockCurveGauge.claimReward( + "compound", + 0, + 0 + ) + expectEvent(tx, "LogClaimedReward"); + }); + + it('can withdraw from compound gauge', async function() { + const tx = await mockCurveGauge.withdraw( + "compound", + 10000000, + 0, + 0, + 0, + 0 + ) + expectEvent(tx, "LogClaimedReward"); + expectEvent(tx, "LogWithdraw", { + amount: "10000000", + getId: "0", + setId: "0" + }); + }); + + it('can deposit into susd gauge', async function() { + const tx = await mockCurveGauge.deposit( + "susd", + 10000000, + 0, + 0 + ) + expectEvent(tx, "LogDeposit", { + amount: "10000000", + getId: "0", + setId: "0" + }); + }); + + it('can claim reward from susd gauge', async function() { + const tx = await mockCurveGauge.claimReward( + "susd", + 0, + 0 + ) + expectEvent(tx, "LogClaimedReward"); + }); + + it('can withdraw from susd gauge', async function() { + const tx = await mockCurveGauge.withdraw( + "susd", + 10000000, + 0, + 0, + 0, + 0 + ) + expectEvent(tx, "LogClaimedReward"); + expectEvent(tx, "LogWithdraw", { + amount: "10000000", + getId: "0", + setId: "0" + }); + }); + + it('cannot deposit into unknown gauge', async function() { + const tx = mockCurveGauge.deposit( + "unknown", + 10000000, + 0, + 0 + ) + await expectRevert(tx, "wrong-gauge-pool-name") + }); + + it('cannot claim reward from unknown gauge', async function() { + const tx = mockCurveGauge.claimReward( + "unknown", + 0, + 0 + ) + await expectRevert(tx, "wrong-gauge-pool-name") + }); + + it('cannot withdraw from unknown gauge', async function() { + const tx = mockCurveGauge.withdraw( + "unknown", + 10000000, + 0, + 0, + 0, + 0 + ) + await expectRevert(tx, "wrong-gauge-pool-name") + }); + + it('can add multiple gauge mappings', async function() { + const tx = await mockCurveGaugeMapping.addGaugeMappings(['sbtc'], [mock.address], [true]); + expectEvent(tx, "LogAddGaugeMapping"); + }); +}) diff --git a/test/abi/curveGauge.json b/test/abi/curveGauge.json new file mode 100644 index 0000000..7123d81 --- /dev/null +++ b/test/abi/curveGauge.json @@ -0,0 +1 @@ +[{"name":"Deposit","inputs":[{"type":"address","name":"provider","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false,"type":"event"},{"name":"Withdraw","inputs":[{"type":"address","name":"provider","indexed":true},{"type":"uint256","name":"value","indexed":false}],"anonymous":false,"type":"event"},{"name":"UpdateLiquidityLimit","inputs":[{"type":"address","name":"user","indexed":false},{"type":"uint256","name":"original_balance","indexed":false},{"type":"uint256","name":"original_supply","indexed":false},{"type":"uint256","name":"working_balance","indexed":false},{"type":"uint256","name":"working_supply","indexed":false}],"anonymous":false,"type":"event"},{"outputs":[],"inputs":[{"type":"address","name":"lp_addr"},{"type":"address","name":"_minter"},{"type":"address","name":"_reward_contract"},{"type":"address","name":"_rewarded_token"}],"stateMutability":"nonpayable","type":"constructor"},{"name":"user_checkpoint","outputs":[{"type":"bool","name":""}],"inputs":[{"type":"address","name":"addr"}],"stateMutability":"nonpayable","type":"function","gas":2311984},{"name":"claimable_tokens","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"addr"}],"stateMutability":"nonpayable","type":"function","gas":2231138},{"name":"claimable_reward","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"addr"}],"stateMutability":"view","type":"function","gas":7300},{"name":"kick","outputs":[],"inputs":[{"type":"address","name":"addr"}],"stateMutability":"nonpayable","type":"function","gas":2317383},{"name":"set_approve_deposit","outputs":[],"inputs":[{"type":"address","name":"addr"},{"type":"bool","name":"can_deposit"}],"stateMutability":"nonpayable","type":"function","gas":35826},{"name":"deposit","outputs":[],"inputs":[{"type":"uint256","name":"_value"}],"stateMutability":"nonpayable","type":"function"},{"name":"deposit","outputs":[],"inputs":[{"type":"uint256","name":"_value"},{"type":"address","name":"addr"}],"stateMutability":"nonpayable","type":"function"},{"name":"withdraw","outputs":[],"inputs":[{"type":"uint256","name":"_value"}],"stateMutability":"nonpayable","type":"function"},{"name":"withdraw","outputs":[],"inputs":[{"type":"uint256","name":"_value"},{"type":"bool","name":"claim_rewards"}],"stateMutability":"nonpayable","type":"function"},{"name":"claim_rewards","outputs":[],"inputs":[],"stateMutability":"nonpayable","type":"function"},{"name":"claim_rewards","outputs":[],"inputs":[{"type":"address","name":"addr"}],"stateMutability":"nonpayable","type":"function"},{"name":"integrate_checkpoint","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":2387},{"name":"minter","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1511},{"name":"crv_token","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1541},{"name":"lp_token","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1571},{"name":"controller","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1601},{"name":"voting_escrow","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1631},{"name":"balanceOf","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":1815},{"name":"totalSupply","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1691},{"name":"future_epoch_time","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1721},{"name":"approved_to_deposit","outputs":[{"type":"bool","name":""}],"inputs":[{"type":"address","name":"arg0"},{"type":"address","name":"arg1"}],"stateMutability":"view","type":"function","gas":2059},{"name":"working_balances","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":1935},{"name":"working_supply","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1811},{"name":"period","outputs":[{"type":"int128","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":1841},{"name":"period_timestamp","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"uint256","name":"arg0"}],"stateMutability":"view","type":"function","gas":1980},{"name":"integrate_inv_supply","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"uint256","name":"arg0"}],"stateMutability":"view","type":"function","gas":2010},{"name":"integrate_inv_supply_of","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2085},{"name":"integrate_checkpoint_of","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2115},{"name":"integrate_fraction","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2145},{"name":"inflation_rate","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":2021},{"name":"reward_contract","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":2051},{"name":"rewarded_token","outputs":[{"type":"address","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":2081},{"name":"reward_integral","outputs":[{"type":"uint256","name":""}],"inputs":[],"stateMutability":"view","type":"function","gas":2111},{"name":"reward_integral_for","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2295},{"name":"rewards_for","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2325},{"name":"claimed_rewards_for","outputs":[{"type":"uint256","name":""}],"inputs":[{"type":"address","name":"arg0"}],"stateMutability":"view","type":"function","gas":2355}]