Oko contract explorer
Contract

Code for 0xa5dc…d207 (LiquidityGaugeV6)

Since block 19409361

Verified contract

  1. # pragma version 0.3.10
  2. # pragma optimize gas
  3. # pragma evm-version shanghai
  4. """
  5. @title LiquidityGaugeV6
  6. @author Curve.Fi
  7. @license Copyright (c) Curve.Fi, 2020-2023 - all rights reserved
  8. @notice Implementation contract for use with Curve Factory
  9. @dev Differs from v5.0.0 in that it uses create_from_blueprint to deploy Gauges
  10. """
  11. from vyper.interfaces import ERC20
  12. implements: ERC20
  13. interface CRV20:
  14. def future_epoch_time_write() -> uint256: nonpayable
  15. def rate() -> uint256: view
  16. interface Controller:
  17. def checkpoint_gauge(addr: address): nonpayable
  18. def gauge_relative_weight(addr: address, time: uint256) -> uint256: view
  19. interface ERC20Extended:
  20. def symbol() -> String[32]: view
  21. interface ERC1271:
  22. def isValidSignature(_hash: bytes32, _signature: Bytes[65]) -> bytes32: view
  23. interface Factory:
  24. def admin() -> address: view
  25. interface Minter:
  26. def minted(user: address, gauge: address) -> uint256: view
  27. interface VotingEscrow:
  28. def user_point_epoch(addr: address) -> uint256: view
  29. def user_point_history__ts(addr: address, epoch: uint256) -> uint256: view
  30. interface VotingEscrowBoost:
  31. def adjusted_balance_of(_account: address) -> uint256: view
  32. event Deposit:
  33. provider: indexed(address)
  34. value: uint256
  35. event Withdraw:
  36. provider: indexed(address)
  37. value: uint256
  38. event UpdateLiquidityLimit:
  39. user: indexed(address)
  40. original_balance: uint256
  41. original_supply: uint256
  42. working_balance: uint256
  43. working_supply: uint256
  44. event CommitOwnership:
  45. admin: address
  46. event ApplyOwnership:
  47. admin: address
  48. event SetGaugeManager:
  49. _gauge_manager: address
  50. event Transfer:
  51. _from: indexed(address)
  52. _to: indexed(address)
  53. _value: uint256
  54. event Approval:
  55. _owner: indexed(address)
  56. _spender: indexed(address)
  57. _value: uint256
  58. struct Reward:
  59. token: address
  60. distributor: address
  61. period_finish: uint256
  62. rate: uint256
  63. last_update: uint256
  64. integral: uint256
  65. MAX_REWARDS: constant(uint256) = 8
  66. TOKENLESS_PRODUCTION: constant(uint256) = 40
  67. WEEK: constant(uint256) = 604800
  68. VERSION: constant(String[8]) = "v6.1.0" # <- updated from v6.0.0 (makes rewards semi-permissionless)
  69. EIP712_TYPEHASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")
  70. EIP2612_TYPEHASH: constant(bytes32) = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
  71. VERSION_HASH: constant(bytes32) = keccak256(VERSION)
  72. NAME_HASH: immutable(bytes32)
  73. CACHED_CHAIN_ID: immutable(uint256)
  74. salt: public(immutable(bytes32))
  75. CACHED_DOMAIN_SEPARATOR: immutable(bytes32)
  76. CRV: constant(address) = 0xD533a949740bb3306d119CC777fa900bA034cd52
  77. GAUGE_CONTROLLER: constant(address) = 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB
  78. MINTER: constant(address) = 0xd061D61a4d941c39E5453435B6345Dc261C2fcE0
  79. VEBOOST_PROXY: constant(address) = 0x8E0c00ed546602fD9927DF742bbAbF726D5B0d16
  80. VOTING_ESCROW: constant(address) = 0x5f3b5DfEb7B28CDbD7FAba78963EE202a494e2A2
  81. # ERC20
  82. balanceOf: public(HashMap[address, uint256])
  83. totalSupply: public(uint256)
  84. allowance: public(HashMap[address, HashMap[address, uint256]])
  85. name: public(String[64])
  86. symbol: public(String[40])
  87. # ERC2612
  88. nonces: public(HashMap[address, uint256])
  89. # Gauge
  90. factory: public(address)
  91. manager: public(address)
  92. lp_token: public(address)
  93. is_killed: public(bool)
  94. # [future_epoch_time uint40][inflation_rate uint216]
  95. inflation_params: uint256
  96. # For tracking external rewards
  97. reward_count: public(uint256)
  98. reward_data: public(HashMap[address, Reward])
  99. # claimant -> default reward receiver
  100. rewards_receiver: public(HashMap[address, address])
  101. # reward token -> claiming address -> integral
  102. reward_integral_for: public(HashMap[address, HashMap[address, uint256]])
  103. # user -> [uint128 claimable amount][uint128 claimed amount]
  104. claim_data: HashMap[address, HashMap[address, uint256]]
  105. working_balances: public(HashMap[address, uint256])
  106. working_supply: public(uint256)
  107. # 1e18 * (rate(t) / totalSupply(t) dt) from (last_action) till checkpoint
  108. integrate_inv_supply_of: public(HashMap[address, uint256])
  109. integrate_checkpoint_of: public(HashMap[address, uint256])
  110. # (balance * rate(t) / totalSupply(t) dt) from 0 till checkpoint
  111. # Units: rate * t = already number of coins per address to issue
  112. integrate_fraction: public(HashMap[address, uint256])
  113. # The goal is to be able to calculate (rate * balance / totalSupply dt) from 0 till checkpoint
  114. # All values are kept in units of being multiplied by 1e18
  115. period: public(int128)
  116. # array of reward tokens
  117. reward_tokens: public(address[MAX_REWARDS])
  118. period_timestamp: public(uint256[100000000000000000000000000000])
  119. # 1e18 * (rate(t) / totalSupply(t) dt) from 0 till checkpoint
  120. integrate_inv_supply: public(uint256[100000000000000000000000000000]) # bump epoch when rate() changes
  121. @external
  122. def __init__(_lp_token: address):
  123. """
  124. @notice Contract constructor
  125. @param _lp_token Liquidity Pool contract address
  126. """
  127. self.lp_token = _lp_token
  128. self.factory = msg.sender
  129. self.manager = tx.origin
  130. symbol: String[32] = ERC20Extended(_lp_token).symbol()
  131. name: String[64] = concat("Curve.fi ", symbol, " Gauge Deposit")
  132. self.name = name
  133. self.symbol = concat(symbol, "-gauge")
  134. self.period_timestamp[0] = block.timestamp
  135. self.inflation_params = (
  136. (CRV20(CRV).future_epoch_time_write() << 216)
  137. + CRV20(CRV).rate()
  138. )
  139. NAME_HASH = keccak256(name)
  140. salt = block.prevhash
  141. CACHED_CHAIN_ID = chain.id
  142. CACHED_DOMAIN_SEPARATOR = keccak256(
  143. _abi_encode(
  144. EIP712_TYPEHASH,
  145. NAME_HASH,
  146. VERSION_HASH,
  147. chain.id,
  148. self,
  149. salt,
  150. )
  151. )
  152. # Internal Functions
  153. @view
  154. @internal
  155. def _domain_separator() -> bytes32:
  156. if chain.id != CACHED_CHAIN_ID:
  157. return keccak256(
  158. _abi_encode(
  159. EIP712_TYPEHASH,
  160. NAME_HASH,
  161. VERSION_HASH,
  162. chain.id,
  163. self,
  164. salt,
  165. )
  166. )
  167. return CACHED_DOMAIN_SEPARATOR
  168. @internal
  169. def _checkpoint(addr: address):
  170. """
  171. @notice Checkpoint for a user
  172. @dev Updates the CRV emissions a user is entitled to receive
  173. @param addr User address
  174. """
  175. _period: int128 = self.period
  176. _period_time: uint256 = self.period_timestamp[_period]
  177. _integrate_inv_supply: uint256 = self.integrate_inv_supply[_period]
  178. inflation_params: uint256 = self.inflation_params
  179. prev_future_epoch: uint256 = inflation_params >> 216
  180. gauge_is_killed: bool = self.is_killed
  181. rate: uint256 = inflation_params % 2 ** 216
  182. new_rate: uint256 = rate
  183. if gauge_is_killed:
  184. rate = 0
  185. new_rate = 0
  186. if prev_future_epoch >= _period_time:
  187. future_epoch_time_write: uint256 = CRV20(CRV).future_epoch_time_write()
  188. if not gauge_is_killed:
  189. new_rate = CRV20(CRV).rate()
  190. self.inflation_params = (future_epoch_time_write << 216) + new_rate
  191. # Update integral of 1/supply
  192. if block.timestamp > _period_time:
  193. _working_supply: uint256 = self.working_supply
  194. Controller(GAUGE_CONTROLLER).checkpoint_gauge(self)
  195. prev_week_time: uint256 = _period_time
  196. week_time: uint256 = min((_period_time + WEEK) / WEEK * WEEK, block.timestamp)
  197. for i in range(500):
  198. dt: uint256 = week_time - prev_week_time
  199. w: uint256 = Controller(GAUGE_CONTROLLER).gauge_relative_weight(self, prev_week_time)
  200. if _working_supply > 0:
  201. if prev_future_epoch >= prev_week_time and prev_future_epoch < week_time:
  202. # If we went across one or multiple epochs, apply the rate
  203. # of the first epoch until it ends, and then the rate of
  204. # the last epoch.
  205. # If more than one epoch is crossed - the gauge gets less,
  206. # but that'd meen it wasn't called for more than 1 year
  207. _integrate_inv_supply += rate * w * (prev_future_epoch - prev_week_time) / _working_supply
  208. rate = new_rate
  209. _integrate_inv_supply += rate * w * (week_time - prev_future_epoch) / _working_supply
  210. else:
  211. _integrate_inv_supply += rate * w * dt / _working_supply
  212. # On precisions of the calculation
  213. # rate ~= 10e18
  214. # last_weight > 0.01 * 1e18 = 1e16 (if pool weight is 1%)
  215. # _working_supply ~= TVL * 1e18 ~= 1e26 ($100M for example)
  216. # The largest loss is at dt = 1
  217. # Loss is 1e-9 - acceptable
  218. if week_time == block.timestamp:
  219. break
  220. prev_week_time = week_time
  221. week_time = min(week_time + WEEK, block.timestamp)
  222. _period += 1
  223. self.period = _period
  224. self.period_timestamp[_period] = block.timestamp
  225. self.integrate_inv_supply[_period] = _integrate_inv_supply
  226. # Update user-specific integrals
  227. _working_balance: uint256 = self.working_balances[addr]
  228. self.integrate_fraction[addr] += _working_balance * (_integrate_inv_supply - self.integrate_inv_supply_of[addr]) / 10 ** 18
  229. self.integrate_inv_supply_of[addr] = _integrate_inv_supply
  230. self.integrate_checkpoint_of[addr] = block.timestamp
  231. @internal
  232. def _checkpoint_rewards(_user: address, _total_supply: uint256, _claim: bool, _receiver: address):
  233. """
  234. @notice Claim pending rewards and checkpoint rewards for a user
  235. """
  236. user_balance: uint256 = 0
  237. receiver: address = _receiver
  238. if _user != empty(address):
  239. user_balance = self.balanceOf[_user]
  240. if _claim and _receiver == empty(address):
  241. # if receiver is not explicitly declared, check if a default receiver is set
  242. receiver = self.rewards_receiver[_user]
  243. if receiver == empty(address):
  244. # if no default receiver is set, direct claims to the user
  245. receiver = _user
  246. reward_count: uint256 = self.reward_count
  247. for i in range(MAX_REWARDS):
  248. if i == reward_count:
  249. break
  250. token: address = self.reward_tokens[i]
  251. integral: uint256 = self.reward_data[token].integral
  252. last_update: uint256 = min(block.timestamp, self.reward_data[token].period_finish)
  253. duration: uint256 = last_update - self.reward_data[token].last_update
  254. if duration != 0 and _total_supply != 0:
  255. self.reward_data[token].last_update = last_update
  256. integral += duration * self.reward_data[token].rate * 10**18 / _total_supply
  257. self.reward_data[token].integral = integral
  258. if _user != empty(address):
  259. integral_for: uint256 = self.reward_integral_for[token][_user]
  260. new_claimable: uint256 = 0
  261. if integral_for < integral:
  262. self.reward_integral_for[token][_user] = integral
  263. new_claimable = user_balance * (integral - integral_for) / 10**18
  264. claim_data: uint256 = self.claim_data[_user][token]
  265. total_claimable: uint256 = (claim_data >> 128) + new_claimable
  266. if total_claimable > 0:
  267. total_claimed: uint256 = claim_data % 2**128
  268. if _claim:
  269. assert ERC20(token).transfer(receiver, total_claimable, default_return_value=True)
  270. self.claim_data[_user][token] = total_claimed + total_claimable
  271. elif new_claimable > 0:
  272. self.claim_data[_user][token] = total_claimed + (total_claimable << 128)
  273. @internal
  274. def _update_liquidity_limit(addr: address, l: uint256, L: uint256):
  275. """
  276. @notice Calculate limits which depend on the amount of CRV token per-user.
  277. Effectively it calculates working balances to apply amplification
  278. of CRV production by CRV
  279. @param addr User address
  280. @param l User's amount of liquidity (LP tokens)
  281. @param L Total amount of liquidity (LP tokens)
  282. """
  283. # To be called after totalSupply is updated
  284. voting_balance: uint256 = VotingEscrowBoost(VEBOOST_PROXY).adjusted_balance_of(addr)
  285. voting_total: uint256 = ERC20(VOTING_ESCROW).totalSupply()
  286. lim: uint256 = l * TOKENLESS_PRODUCTION / 100
  287. if voting_total > 0:
  288. lim += L * voting_balance / voting_total * (100 - TOKENLESS_PRODUCTION) / 100
  289. lim = min(l, lim)
  290. old_bal: uint256 = self.working_balances[addr]
  291. self.working_balances[addr] = lim
  292. _working_supply: uint256 = self.working_supply + lim - old_bal
  293. self.working_supply = _working_supply
  294. log UpdateLiquidityLimit(addr, l, L, lim, _working_supply)
  295. @internal
  296. def _transfer(_from: address, _to: address, _value: uint256):
  297. """
  298. @notice Transfer tokens as well as checkpoint users
  299. """
  300. self._checkpoint(_from)
  301. self._checkpoint(_to)
  302. if _value != 0:
  303. total_supply: uint256 = self.totalSupply
  304. is_rewards: bool = self.reward_count != 0
  305. if is_rewards:
  306. self._checkpoint_rewards(_from, total_supply, False, empty(address))
  307. new_balance: uint256 = self.balanceOf[_from] - _value
  308. self.balanceOf[_from] = new_balance
  309. self._update_liquidity_limit(_from, new_balance, total_supply)
  310. if is_rewards:
  311. self._checkpoint_rewards(_to, total_supply, False, empty(address))
  312. new_balance = self.balanceOf[_to] + _value
  313. self.balanceOf[_to] = new_balance
  314. self._update_liquidity_limit(_to, new_balance, total_supply)
  315. log Transfer(_from, _to, _value)
  316. # External User Facing Functions
  317. @external
  318. @nonreentrant('lock')
  319. def deposit(_value: uint256, _addr: address = msg.sender, _claim_rewards: bool = False):
  320. """
  321. @notice Deposit `_value` LP tokens
  322. @dev Depositting also claims pending reward tokens
  323. @param _value Number of tokens to deposit
  324. @param _addr Address to deposit for
  325. """
  326. assert _addr != empty(address) # dev: cannot deposit for zero address
  327. self._checkpoint(_addr)
  328. if _value != 0:
  329. is_rewards: bool = self.reward_count != 0
  330. total_supply: uint256 = self.totalSupply
  331. if is_rewards:
  332. self._checkpoint_rewards(_addr, total_supply, _claim_rewards, empty(address))
  333. total_supply += _value
  334. new_balance: uint256 = self.balanceOf[_addr] + _value
  335. self.balanceOf[_addr] = new_balance
  336. self.totalSupply = total_supply
  337. self._update_liquidity_limit(_addr, new_balance, total_supply)
  338. ERC20(self.lp_token).transferFrom(msg.sender, self, _value)
  339. log Deposit(_addr, _value)
  340. log Transfer(empty(address), _addr, _value)
  341. @external
  342. @nonreentrant('lock')
  343. def withdraw(_value: uint256, _claim_rewards: bool = False):
  344. """
  345. @notice Withdraw `_value` LP tokens
  346. @dev Withdrawing also claims pending reward tokens
  347. @param _value Number of tokens to withdraw
  348. """
  349. self._checkpoint(msg.sender)
  350. if _value != 0:
  351. is_rewards: bool = self.reward_count != 0
  352. total_supply: uint256 = self.totalSupply
  353. if is_rewards:
  354. self._checkpoint_rewards(msg.sender, total_supply, _claim_rewards, empty(address))
  355. total_supply -= _value
  356. new_balance: uint256 = self.balanceOf[msg.sender] - _value
  357. self.balanceOf[msg.sender] = new_balance
  358. self.totalSupply = total_supply
  359. self._update_liquidity_limit(msg.sender, new_balance, total_supply)
  360. ERC20(self.lp_token).transfer(msg.sender, _value)
  361. log Withdraw(msg.sender, _value)
  362. log Transfer(msg.sender, empty(address), _value)
  363. @external
  364. @nonreentrant('lock')
  365. def claim_rewards(_addr: address = msg.sender, _receiver: address = empty(address)):
  366. """
  367. @notice Claim available reward tokens for `_addr`
  368. @param _addr Address to claim for
  369. @param _receiver Address to transfer rewards to - if set to
  370. empty(address), uses the default reward receiver
  371. for the caller
  372. """
  373. if _receiver != empty(address):
  374. assert _addr == msg.sender # dev: cannot redirect when claiming for another user
  375. self._checkpoint_rewards(_addr, self.totalSupply, True, _receiver)
  376. @external
  377. @nonreentrant('lock')
  378. def transferFrom(_from: address, _to :address, _value: uint256) -> bool:
  379. """
  380. @notice Transfer tokens from one address to another.
  381. @dev Transferring claims pending reward tokens for the sender and receiver
  382. @param _from address The address which you want to send tokens from
  383. @param _to address The address which you want to transfer to
  384. @param _value uint256 the amount of tokens to be transferred
  385. """
  386. _allowance: uint256 = self.allowance[_from][msg.sender]
  387. if _allowance != max_value(uint256):
  388. self.allowance[_from][msg.sender] = _allowance - _value
  389. self._transfer(_from, _to, _value)
  390. return True
  391. @external
  392. @nonreentrant('lock')
  393. def transfer(_to: address, _value: uint256) -> bool:
  394. """
  395. @notice Transfer token for a specified address
  396. @dev Transferring claims pending reward tokens for the sender and receiver
  397. @param _to The address to transfer to.
  398. @param _value The amount to be transferred.
  399. """
  400. self._transfer(msg.sender, _to, _value)
  401. return True
  402. @external
  403. def approve(_spender : address, _value : uint256) -> bool:
  404. """
  405. @notice Approve the passed address to transfer the specified amount of
  406. tokens on behalf of msg.sender
  407. @dev Beware that changing an allowance via this method brings the risk
  408. that someone may use both the old and new allowance by unfortunate
  409. transaction ordering. This may be mitigated with the use of
  410. {incraseAllowance} and {decreaseAllowance}.
  411. https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
  412. @param _spender The address which will transfer the funds
  413. @param _value The amount of tokens that may be transferred
  414. @return bool success
  415. """
  416. self.allowance[msg.sender][_spender] = _value
  417. log Approval(msg.sender, _spender, _value)
  418. return True
  419. @external
  420. def permit(
  421. _owner: address,
  422. _spender: address,
  423. _value: uint256,
  424. _deadline: uint256,
  425. _v: uint8,
  426. _r: bytes32,
  427. _s: bytes32
  428. ) -> bool:
  429. """
  430. @notice Approves spender by owner's signature to expend owner's tokens.
  431. See https://eips.ethereum.org/EIPS/eip-2612.
  432. @dev Inspired by https://github.com/yearn/yearn-vaults/blob/main/contracts/Vault.vy#L753-L793
  433. @dev Supports smart contract wallets which implement ERC1271
  434. https://eips.ethereum.org/EIPS/eip-1271
  435. @param _owner The address which is a source of funds and has signed the Permit.
  436. @param _spender The address which is allowed to spend the funds.
  437. @param _value The amount of tokens to be spent.
  438. @param _deadline The timestamp after which the Permit is no longer valid.
  439. @param _v The bytes[64] of the valid secp256k1 signature of permit by owner
  440. @param _r The bytes[0:32] of the valid secp256k1 signature of permit by owner
  441. @param _s The bytes[32:64] of the valid secp256k1 signature of permit by owner
  442. @return True, if transaction completes successfully
  443. """
  444. assert _owner != empty(address) # dev: invalid owner
  445. assert block.timestamp <= _deadline # dev: permit expired
  446. nonce: uint256 = self.nonces[_owner]
  447. digest: bytes32 = keccak256(
  448. concat(
  449. b"\x19\x01",
  450. self._domain_separator(),
  451. keccak256(
  452. _abi_encode(
  453. EIP2612_TYPEHASH, _owner, _spender, _value, nonce, _deadline
  454. )
  455. ),
  456. )
  457. )
  458. assert ecrecover(digest, _v, _r, _s) == _owner # dev: invalid signature
  459. self.allowance[_owner][_spender] = _value
  460. self.nonces[_owner] = unsafe_add(nonce, 1)
  461. log Approval(_owner, _spender, _value)
  462. return True
  463. @external
  464. def increaseAllowance(_spender: address, _added_value: uint256) -> bool:
  465. """
  466. @notice Increase the allowance granted to `_spender` by the caller
  467. @dev This is alternative to {approve} that can be used as a mitigation for
  468. the potential race condition
  469. @param _spender The address which will transfer the funds
  470. @param _added_value The amount of to increase the allowance
  471. @return bool success
  472. """
  473. allowance: uint256 = self.allowance[msg.sender][_spender] + _added_value
  474. self.allowance[msg.sender][_spender] = allowance
  475. log Approval(msg.sender, _spender, allowance)
  476. return True
  477. @external
  478. def decreaseAllowance(_spender: address, _subtracted_value: uint256) -> bool:
  479. """
  480. @notice Decrease the allowance granted to `_spender` by the caller
  481. @dev This is alternative to {approve} that can be used as a mitigation for
  482. the potential race condition
  483. @param _spender The address which will transfer the funds
  484. @param _subtracted_value The amount of to decrease the allowance
  485. @return bool success
  486. """
  487. allowance: uint256 = self.allowance[msg.sender][_spender] - _subtracted_value
  488. self.allowance[msg.sender][_spender] = allowance
  489. log Approval(msg.sender, _spender, allowance)
  490. return True
  491. @external
  492. def user_checkpoint(addr: address) -> bool:
  493. """
  494. @notice Record a checkpoint for `addr`
  495. @param addr User address
  496. @return bool success
  497. """
  498. assert msg.sender in [addr, MINTER] # dev: unauthorized
  499. self._checkpoint(addr)
  500. self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
  501. return True
  502. @external
  503. def set_rewards_receiver(_receiver: address):
  504. """
  505. @notice Set the default reward receiver for the caller.
  506. @dev When set to empty(address), rewards are sent to the caller
  507. @param _receiver Receiver address for any rewards claimed via `claim_rewards`
  508. """
  509. self.rewards_receiver[msg.sender] = _receiver
  510. @external
  511. def kick(addr: address):
  512. """
  513. @notice Kick `addr` for abusing their boost
  514. @dev Only if either they had another voting event, or their voting escrow lock expired
  515. @param addr Address to kick
  516. """
  517. t_last: uint256 = self.integrate_checkpoint_of[addr]
  518. t_ve: uint256 = VotingEscrow(VOTING_ESCROW).user_point_history__ts(
  519. addr, VotingEscrow(VOTING_ESCROW).user_point_epoch(addr)
  520. )
  521. _balance: uint256 = self.balanceOf[addr]
  522. assert ERC20(VOTING_ESCROW).balanceOf(addr) == 0 or t_ve > t_last # dev: kick not allowed
  523. assert self.working_balances[addr] > _balance * TOKENLESS_PRODUCTION / 100 # dev: kick not needed
  524. self._checkpoint(addr)
  525. self._update_liquidity_limit(addr, self.balanceOf[addr], self.totalSupply)
  526. # Administrative Functions
  527. @external
  528. def set_gauge_manager(_gauge_manager: address):
  529. """
  530. @notice Change the gauge manager for a gauge
  531. @dev The manager of this contract, or the ownership admin can outright modify gauge
  532. managership. A gauge manager can also transfer managership to a new manager via this
  533. method, but only for the gauge which they are the manager of.
  534. @param _gauge_manager The account to set as the new manager of the gauge.
  535. """
  536. assert msg.sender in [self.manager, Factory(self.factory).admin()] # dev: only manager or factory admin
  537. self.manager = _gauge_manager
  538. log SetGaugeManager(_gauge_manager)
  539. @external
  540. @nonreentrant("lock")
  541. def deposit_reward_token(_reward_token: address, _amount: uint256, _epoch: uint256 = WEEK):
  542. """
  543. @notice Deposit a reward token for distribution
  544. @param _reward_token The reward token being deposited
  545. @param _amount The amount of `_reward_token` being deposited
  546. @param _epoch The duration the rewards are distributed across.
  547. """
  548. assert msg.sender == self.reward_data[_reward_token].distributor
  549. self._checkpoint_rewards(empty(address), self.totalSupply, False, empty(address))
  550. # transferFrom reward token and use transferred amount henceforth:
  551. amount_received: uint256 = ERC20(_reward_token).balanceOf(self)
  552. assert ERC20(_reward_token).transferFrom(
  553. msg.sender,
  554. self,
  555. _amount,
  556. default_return_value=True
  557. )
  558. amount_received = ERC20(_reward_token).balanceOf(self) - amount_received
  559. period_finish: uint256 = self.reward_data[_reward_token].period_finish
  560. assert amount_received > _epoch # dev: rate will tend to zero!
  561. if block.timestamp >= period_finish:
  562. self.reward_data[_reward_token].rate = amount_received / _epoch
  563. else:
  564. remaining: uint256 = period_finish - block.timestamp
  565. leftover: uint256 = remaining * self.reward_data[_reward_token].rate
  566. self.reward_data[_reward_token].rate = (amount_received + leftover) / _epoch
  567. self.reward_data[_reward_token].last_update = block.timestamp
  568. self.reward_data[_reward_token].period_finish = block.timestamp + _epoch
  569. @external
  570. def add_reward(_reward_token: address, _distributor: address):
  571. """
  572. @notice Add additional rewards to be distributed to stakers
  573. @param _reward_token The token to add as an additional reward
  574. @param _distributor Address permitted to fund this contract with the reward token
  575. """
  576. assert msg.sender in [self.manager, Factory(self.factory).admin()] # dev: only manager or factory admin
  577. assert _distributor != empty(address) # dev: distributor cannot be zero address
  578. reward_count: uint256 = self.reward_count
  579. assert reward_count < MAX_REWARDS
  580. assert self.reward_data[_reward_token].distributor == empty(address)
  581. self.reward_data[_reward_token].distributor = _distributor
  582. self.reward_tokens[reward_count] = _reward_token
  583. self.reward_count = reward_count + 1
  584. @external
  585. def set_reward_distributor(_reward_token: address, _distributor: address):
  586. """
  587. @notice Reassign the reward distributor for a reward token
  588. @param _reward_token The reward token to reassign distribution rights to
  589. @param _distributor The address of the new distributor
  590. """
  591. current_distributor: address = self.reward_data[_reward_token].distributor
  592. assert msg.sender in [current_distributor, Factory(self.factory).admin(), self.manager]
  593. assert current_distributor != empty(address)
  594. assert _distributor != empty(address)
  595. self.reward_data[_reward_token].distributor = _distributor
  596. @external
  597. def set_killed(_is_killed: bool):
  598. """
  599. @notice Set the killed status for this contract
  600. @dev When killed, the gauge always yields a rate of 0 and so cannot mint CRV
  601. @param _is_killed Killed status to set
  602. """
  603. assert msg.sender == Factory(self.factory).admin() # dev: only owner
  604. self.is_killed = _is_killed
  605. # View Methods
  606. @view
  607. @external
  608. def claimed_reward(_addr: address, _token: address) -> uint256:
  609. """
  610. @notice Get the number of already-claimed reward tokens for a user
  611. @param _addr Account to get reward amount for
  612. @param _token Token to get reward amount for
  613. @return uint256 Total amount of `_token` already claimed by `_addr`
  614. """
  615. return self.claim_data[_addr][_token] % 2**128
  616. @view
  617. @external
  618. def claimable_reward(_user: address, _reward_token: address) -> uint256:
  619. """
  620. @notice Get the number of claimable reward tokens for a user
  621. @param _user Account to get reward amount for
  622. @param _reward_token Token to get reward amount for
  623. @return uint256 Claimable reward token amount
  624. """
  625. integral: uint256 = self.reward_data[_reward_token].integral
  626. total_supply: uint256 = self.totalSupply
  627. if total_supply != 0:
  628. last_update: uint256 = min(block.timestamp, self.reward_data[_reward_token].period_finish)
  629. duration: uint256 = last_update - self.reward_data[_reward_token].last_update
  630. integral += (duration * self.reward_data[_reward_token].rate * 10**18 / total_supply)
  631. integral_for: uint256 = self.reward_integral_for[_reward_token][_user]
  632. new_claimable: uint256 = self.balanceOf[_user] * (integral - integral_for) / 10**18
  633. return (self.claim_data[_user][_reward_token] >> 128) + new_claimable
  634. @external
  635. def claimable_tokens(addr: address) -> uint256:
  636. """
  637. @notice Get the number of claimable tokens per user
  638. @dev This function should be manually changed to "view" in the ABI
  639. @return uint256 number of claimable tokens per user
  640. """
  641. self._checkpoint(addr)
  642. return self.integrate_fraction[addr] - Minter(MINTER).minted(addr, self)
  643. @view
  644. @external
  645. def integrate_checkpoint() -> uint256:
  646. """
  647. @notice Get the timestamp of the last checkpoint
  648. """
  649. return self.period_timestamp[self.period]
  650. @view
  651. @external
  652. def future_epoch_time() -> uint256:
  653. """
  654. @notice Get the locally stored CRV future epoch start time
  655. """
  656. return self.inflation_params >> 216
  657. @view
  658. @external
  659. def inflation_rate() -> uint256:
  660. """
  661. @notice Get the locally stored CRV inflation rate
  662. """
  663. return self.inflation_params % 2 ** 216
  664. @view
  665. @external
  666. def decimals() -> uint256:
  667. """
  668. @notice Get the number of decimals for this token
  669. @dev Implemented as a view method to reduce gas costs
  670. @return uint256 decimal places
  671. """
  672. return 18
  673. @view
  674. @external
  675. def version() -> String[8]:
  676. """
  677. @notice Get the version of this gauge contract
  678. """
  679. return VERSION
  680. @view
  681. @external
  682. def DOMAIN_SEPARATOR() -> bytes32:
  683. """
  684. @notice EIP712 domain separator.
  685. """
  686. return self._domain_separator()

Contract sourced from Etherscan. Solidity version vyper:0.3.10.

Panoramix decompilation

# Palkeoramix decompiler. 

def _fallback(?) payable: # default function
  revert

Decompilation generated by Panoramix.

Raw bytecode

0x5f3560e01c6002603f821660011b6128e501601e395f51565b63bfa0b133811861003657346128e15760206129a560403960206040f35b63095ea7b38118611c2d576044361034176128e1576004358060a01c6128e1576040526024356003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560243560605260206060a3600160605260206060f3611c2d565b6370a0823181186100f2576024361034176128e1576004358060a01c6128e15760405260016040516020525f5260405f205460605260206060f35b6301ddabf18118611c2d576024361034176128e1576004358060a01c6128e15760405260126040516020525f5260405f205460605260206060f3611c2d565b6318160ddd811861014d57346128e15760025460405260206040f35b6323b872dd8118611c2d576064361034176128e1576004358060a01c6128e1576103a0526024358060a01c6128e1576103c0525f546002146128e15760025f5560036103a0516020525f5260405f2080336020525f5260405f209050546103e0527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6103e0511461020b576103e0516044358082038281116128e1579050905060036103a0516020525f5260405f2080336020525f5260405f209050555b6103a0516102e0526103c051610300526044356103205261022a612779565b600161040052602061040060035f55f3611c2d565b63dd62ed3e8118610297576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260036040516020525f5260405f20806060516020525f5260405f2090505460805260206080f35b6384e9bd7e8118611c2d576024361034176128e1576004358060a01c6128e1576102e0525f61030052610f3a56611c2d565b6306fdde03811861034457346128e157602080604052806040016020600454015f81601f0160051c600381116128e157801561031857905b80600401548160051b850152600101818118610301575b5050508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f35b63331345838118611c2d576024361034176128e1576004358060a01c6128e1576102805261028051604052610377611cc4565b6019610280516020525f5260405f2054638b752bb06102a052610280516102c052306102e05260206102a060446102bc73d061d61a4d941c39e5453435b6345dc261c2fce05afa6103ca573d5f5f3e3d5ffd5b60203d106128e1576102a0518082038281116128e15790509050610300526020610300f3611c2d565b6395d89b418118611c2d57346128e157602080604052806040016020600754015f81601f0160051c600381116128e157801561044257905b80600701548160051b85015260010181811861042b575b5050508051806020830101601f825f03163682375050601f19601f825160200101169050810190506040f3611c2d565b637ecebe0081146003361116156104b3576024361034176128e1576004358060a01c6128e157604052600a6040516020525f5260405f205460605260206060f35b639c868ac08118611c2d57346128e157600e5460405260206040f3611c2d565b63c45a015581186104ef57346128e157600b5460405260206040f35b633644e5158118611c2d57346128e157602061050c610120611c31565b610120f3611c2d565b63481c6a75811861053157346128e157600c5460405260206040f35b6396c551758118611c2d576024361034176128e1576004358060a01c6128e157610280526018610280516020525f5260405f20546102a05263da020a1861032052610280516103405263010ae7576102e052610280516103005260206102e060246102fc735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa6105b8573d5f5f3e3d5ffd5b60203d106128e1576102e051610360526020610320604461033c735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa6105f5573d5f5f3e3d5ffd5b60203d106128e157610320516102c0526001610280516020525f5260405f20546102e0526370a082316103005261028051610320526020610300602461031c735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa610657573d5f5f3e3d5ffd5b60203d106128e1576103005161066e576001610678565b6102a0516102c051115b156128e1576102e051602881028160288204186128e15790506064810490506015610280516020525f5260405f205411156128e157610280516040526106bc611cc4565b610280516040526001610280516020525f5260405f20546060526002546080526106e46125c3565b00611c2d565b6382c630668118611c2d57346128e157600d5460405260206040f3611c2d565b63963c94b98118611c2d57346128e15760105460405260206040f3611c2d565b6348e9c65e8118611c2d576024361034176128e1576004358060a01c6128e15760405260116040516020525f5260405f2080546060526001810154608052600281015460a052600381015460c052600481015460e0526005810154610100525060c06060f3611c2d565b63f05cc0588118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260136040516020525f5260405f20806060516020525f5260405f2090505460805260206080f3611c2d565b6313ecb1ca8118611c2d576024361034176128e1576004358060a01c6128e15760405260156040516020525f5260405f205460605260206060f3611c2d565b6317e280898118611c2d57346128e15760165460405260206040f3611c2d565b63de263bfa8118611c2d576024361034176128e1576004358060a01c6128e15760405260176040516020525f5260405f205460605260206060f3611c2d565b639bd324f281186108c9576024361034176128e1576004358060a01c6128e15760405260186040516020525f5260405f205460605260206060f35b63e6f1daf28118611c2d57346128e157336102e0525f61030052610f3a56611c2d565b63094007078118610927576024361034176128e1576004358060a01c6128e15760405260196040516020525f5260405f205460605260206060f35b6383df67478118611c2d576064361034176128e1576024358060a01c6128e1576102e0526044358060011c6128e157610300525b5f546002146128e15760025f556102e051156128e1576102e051604052610980611cc4565b60043515610af2576010541515610320526002546103405261032051156109c2576102e05160405261034051606052610300516080525f60a0526109c26121f9565b610340516004358082018281106128e157905090506103405260016102e0516020525f5260405f20546004358082018281106128e15790509050610360526103605160016102e0516020525f5260405f2055610340516002556102e0516040526103605160605261034051608052610a386125c3565b600d546323b872dd61038052336103a052306103c0526004356103e0526020610380606461039c5f855af1610a6f573d5f5f3e3d5ffd5b60203d106128e157610380518060011c6128e1576104005261040050506102e0517fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c600435610380526020610380a26102e0515f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600435610380526020610380a35b60035f5500611c2d565b63ef78d4fd8118611c2d57346128e157601a5460405260206040f3611c2d565b6354c49fe98118610b49576024361034176128e157600435600781116128e157601b015460405260206040f35b63be5d1be98118611c2d57346128e157600f5460d81c60405260206040f3611c2d565b637598108c8118610ba5576024361034176128e1576004356c01431e0fae6d7217ca9fffffff81116128e1576023015460405260206040f35b63fec8ee0c8118611c2d576024361034176128e1576004356c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015460405260206040f3611c2d565b63b6b55f258118610c12576024361034176128e157336102e0525f6103005261095b565b636e553f658118611c2d576044361034176128e1576024358060a01c6128e1576102e0525f6103005261095b56611c2d565b632e1a7d4d8118610c63576024361034176128e1575f6102e052610d88565b63e8de0d4d8118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605233600c548118610ca5576001610ce8565b600b5463f851a44060e052602060e0600460fc845afa610cc7573d5f5f3e3d5ffd5b60203d106128e15760e0518060a01c6128e157610120526101209050518118155b9050156128e157606051156128e1576010546080526007608051116128e15760116040516020525f5260405f20600181019050546128e15760605160116040516020525f5260405f2060018101905055604051608051600781116128e157601b0155608051600181018181106128e157905060105500611c2d565b6338d074368118611c2d576044361034176128e1576024358060011c6128e1576102e0525b5f546002146128e15760025f5533604052610da1611cc4565b60043515610e9d57601054151561030052600254610320526103005115610de05733604052610320516060526102e0516080525f60a052610de06121f9565b610320516004358082038281116128e15790509050610320526001336020525f5260405f20546004358082038281116128e1579050905061034052610340516001336020525f5260405f205561032051600255336040526103405160605261032051608052610e4d6125c3565b600d5463a9059cbb6103605233610380526004356103a0526020610360604461037c5f855af1610e7f573d5f5f3e3d5ffd5b60203d106128e157610360518060011c6128e1576103c0526103c050505b337f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a9424364600435610300526020610300a25f337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef600435610300526020610300a360035f5500611c2d565b639faceb1b8118611c2d576044361034176128e1576004358060a01c6128e1576102e0526024358060a01c6128e157610300525b5f546002146128e15760025f556103005115610f5b57336102e051186128e1575b6102e05160405260025460605260016080526103005160a052610f7c6121f9565b60035f5500611c2d565b63a9059cbb8118611c2d576044361034176128e1576004358060a01c6128e1576103a0525f546002146128e15760025f55336102e0526103a0516103005260243561032052610fd3612779565b60016103c05260206103c060035f55f3611c2d565b63d505accf8118611c2d5760e4361034176128e1576004358060a01c6128e157610120526024358060a01c6128e157610140526084358060081c6128e1576101605261012051156128e15760643542116128e157600a610120516020525f5260405f2054610180525f60026101c0527f19010000000000000000000000000000000000000000000000000000000000006101e0526101c08051602082018361032001815181525050808301925050506110a2610200611c31565b610200518161032001526020810190507f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c961024052610120516102605261014051610280526044356102a052610180516102c0526064356102e05260c061022052610220805160208201209050816103200152602081019050806103005261030090508051602082012090506101a052610120515f610240526101a0516101c052610160516101e052604060a461020037602061024060806101c060015afa5061024051186128e1576044356003610120516020525f5260405f2080610140516020525f5260405f2090505560016101805101600a610120516020525f5260405f205561014051610120517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9256044356101c05260206101c0a360016101c05260206101c0f3611c2d565b63395093518118611c2d576044361034176128e1576004358060a01c6128e1576040526003336020525f5260405f20806040516020525f5260405f209050546024358082018281106128e157905090506060526060516003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560605160805260206080a3600160805260206080f3611c2d565b63a457c2d7811861134a576044361034176128e1576004358060a01c6128e1576040526003336020525f5260405f20806040516020525f5260405f209050546024358082038281116128e157905090506060526060516003336020525f5260405f20806040516020525f5260405f20905055604051337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560605160805260206080a3600160805260206080f35b6390b229978118611c2d576024361034176128e1576004358060011c6128e157604052600b5463f851a440606052602060606004607c845afa61138f573d5f5f3e3d5ffd5b60203d106128e1576060518060a01c6128e15760a05260a090505133186128e157604051600e5500611c2d565b634b8200938118611c2d576024361034176128e1576004358060a01c6128e15761028052336102805181186113f257600161140b565b73d061d61a4d941c39e5453435b6345dc261c2fce08118155b9050156128e15761028051604052611421611cc4565b610280516040526001610280516020525f5260405f20546060526002546080526114496125c3565b60016102a05260206102a0f3611c2d565b63bdf981168118611c2d576024361034176128e1576004358060a01c6128e1576040526040516012336020525f5260405f205500611c2d565b63aace1d488118611c2d576024361034176128e1576004358060a01c6128e15760405233600c5481186114c757600161150a565b600b5463f851a44060c052602060c0600460dc845afa6114e9573d5f5f3e3d5ffd5b60203d106128e15760c0518060a01c6128e157610100526101009050518118155b9050156128e157604051600c557fe83c80a0349150991cb09b4c940149ac4c37cb03673b6c0c669b4da27865c28b60405160605260206060a100611c2d565b6393f7aa67811861156b576044361034176128e15762093a80610300526115a7565b63313ce5678118611c2d57346128e157601260405260206040f3611c2d565b6333b50aed8118611826576064361034176128e157604435610300525b6004358060a01c6128e1576102e0525f546002146128e15760025f5560116102e0516020525f5260405f206001810190505433186128e1575f6040526002546060526040366080376115f76121f9565b6102e0516370a082316103405230610360526020610340602461035c845afa611622573d5f5f3e3d5ffd5b60203d106128e157610340905051610320526102e0516323b872dd61034052336103605230610380526024356103a0526020610340606461035c5f855af161166c573d5f5f3e3d5ffd5b3d61168357803b156128e15760016103c05261169c565b60203d106128e157610340518060011c6128e1576103c0525b6103c0905051156128e1576102e0516370a082316103405230610360526020610340602461035c845afa6116d2573d5f5f3e3d5ffd5b60203d106128e157610340905051610320518082038281116128e157905090506103205260116102e0516020525f5260405f206002810190505461034052610300516103205111156128e157610340514210156117b45761034051428082038281116128e15790509050610360526103605160116102e0516020525f5260405f20600381019050548082028115838383041417156128e157905090506103805261032051610380518082018281106128e157905090506103005180156128e1578082049050905060116102e0516020525f5260405f20600381019050556117e0565b610320516103005180156128e1578082049050905060116102e0516020525f5260405f20600381019050555b4260116102e0516020525f5260405f206004810190505542610300518082018281106128e1579050905060116102e0516020525f5260405f206002810190505560035f55005b63d31f3f6d8118611c2d57346128e157601a546c01431e0fae6d7217ca9fffffff81116128e1576023015460405260206040f3611c2d565b63058a3a248118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260116040516020525f5260405f20600181019050546080523360805181186118b857600161190f565b600b5463f851a440610100526020610100600461011c845afa6118dd573d5f5f3e3d5ffd5b60203d106128e157610100518060a01c6128e15761014052610140905051811861190857600161190f565b600c548118155b9050156128e157608051156128e157606051156128e15760605160116040516020525f5260405f206001810190505500611c2d565b63e77e74378118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260146040516020525f5260405f20806060516020525f5260405f209050546fffffffffffffffffffffffffffffffff8116905060805260206080f3611c2d565b6333fd6f748118611c2d576044361034176128e1576004358060a01c6128e1576040526024358060a01c6128e15760605260116060516020525f5260405f206005810190505460805260025460a05260a05115611aca574260116060516020525f5260405f20600281019050548082811882841002189050905060c05260c05160116060516020525f5260405f20600481019050548082038281116128e1579050905060e05260805160e05160116060516020525f5260405f20600381019050548082028115838383041417156128e15790509050670de0b6b3a7640000810281670de0b6b3a76400008204186128e157905060a05180156128e157808204905090508082018281106128e157905090506080525b60136060516020525f5260405f20806040516020525f5260405f2090505460c05260016040516020525f5260405f205460805160c0518082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a76400008104905060e05260146040516020525f5260405f20806060516020525f5260405f2090505460801c60e0518082018281106128e15790509050610100526020610100f3611c2d565b63180692d08118611baf57346128e157600f547affffffffffffffffffffffffffffffffffffffffffffffffffffff8116905060405260206040f35b6354fd4d508118611c2d57346128e15760208060805260066040527f76362e312e30000000000000000000000000000000000000000000000000000060605260408160800181518152602082015160208201528051806020830101601f825f03163682375050601f19601f8251602001011690509050810190506080f35b5f5ffd5b60206129855f395f514614611cb9577f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60605260206129656080397f71916ddf3efd1d13d14899519c9cd193fa3f9be07ea852717d2f225de1eb246660a0524660c0523060e05260206129a56101003960c06040526040805160208201209050815250611cc2565b60206129c58239505b565b601a546060526060516c01431e0fae6d7217ca9fffffff81116128e157602301546080526060516c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015460a052600f5460c05260c05160d81c60e052600e546101005260c0517affffffffffffffffffffffffffffffffffffffffffffffffffffff811690506101205261012051610140526101005115611d6857604036610120375b60805160e05110611e265763b26b238e610180526020610180600461019c5f73d533a949740bb3306d119cc777fa900ba034cd525af1611daa573d5f5f3e3d5ffd5b60203d106128e157610180516101605261010051611e0957632c4e722e610180526020610180600461019c73d533a949740bb3306d119cc777fa900ba034cd525afa611df8573d5f5f3e3d5ffd5b60203d106128e15761018051610140525b6101605160d81b610140518082018281106128e15790509050600f555b6080514211156120fa576016546101605263615e523761018052306101a052732f50d538606fa9edd2b11e2446beb18c9d5846bb3b156128e1575f610180602461019c5f732f50d538606fa9edd2b11e2446beb18c9d5846bb5af1611e8d573d5f5f3e3d5ffd5b6080516101805260805162093a8081018181106128e157905062093a808104905062093a8081028162093a808204186128e157905042808281188284100218905090506101a0525f6101f4905b806101c0526101a051610180518082038281116128e157905090506101e05263d3078c9461022052306102405261018051610260526020610220604461023c732f50d538606fa9edd2b11e2446beb18c9d5846bb5afa611f3c573d5f5f3e3d5ffd5b60203d106128e157610220516102005261016051156120b0576101805160e0511015611f68575f611f71565b6101a05160e051105b611fd35760a05161012051610200518082028115838383041417156128e157905090506101e0518082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a0526120b0565b60a05161012051610200518082028115838383041417156128e1579050905060e051610180518082038281116128e157905090508082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a052610140516101205260a05161012051610200518082028115838383041417156128e157905090506101a05160e0518082038281116128e157905090508082028115838383041417156128e157905090506101605180156128e157808204905090508082018281106128e1579050905060a0525b426101a051186120bf576120f7565b6101a051610180526101a05162093a8081018181106128e157905042808281188284100218905090506101a052600101818118611eda575b50505b6060516001810180600f0b81186128e1579050606052606051601a55426060516c01431e0fae6d7217ca9fffffff81116128e1576023015560a0516060516c01431e0fae6d7217ca9fffffff81116128e1576c01431e0fae6d7217caa0000023015560156040516020525f5260405f20546101605260196040516020525f5260405f2080546101605160a05160176040516020525f5260405f20548082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a7640000810490508082018281106128e1579050905081555060a05160176040516020525f5260405f20554260186040516020525f5260405f2055565b5f60c05260a05160e052604051156122545760016040516020525f5260405f205460c05260805161222a575f61222f565b60a051155b156122545760126040516020525f5260405f205460e05260e0516122545760405160e0525b601054610100525f6008905b8061012052610100516101205118612277576125bf565b61012051600781116128e157601b0154610140526011610140516020525f5260405f206005810190505461016052426011610140516020525f5260405f20600281019050548082811882841002189050905061018052610180516011610140516020525f5260405f20600481019050548082038281116128e157905090506101a0526101a0511561230c57606051151561230e565b5f5b156123ba57610180516011610140516020525f5260405f2060048101905055610160516101a0516011610140516020525f5260405f20600381019050548082028115838383041417156128e15790509050670de0b6b3a7640000810281670de0b6b3a76400008204186128e157905060605180156128e157808204905090508082018281106128e1579050905061016052610160516011610140516020525f5260405f20600581019050555b604051156125b4576013610140516020525f5260405f20806040516020525f5260405f209050546101c0525f6101e052610160516101c051101561245a57610160516013610140516020525f5260405f20806040516020525f5260405f2090505560c051610160516101c0518082038281116128e157905090508082028115838383041417156128e15790509050670de0b6b3a7640000810490506101e0525b60146040516020525f5260405f2080610140516020525f5260405f20905054610200526102005160801c6101e0518082018281106128e157905090506102205261022051156125b457610200516fffffffffffffffffffffffffffffffff811690506102405260805161250d576101e051156125b457610240516102205160801b8082018281106128e1579050905060146040516020525f5260405f2080610140516020525f5260405f209050556125b4565b6101405163a9059cbb6102605260e05161028052610220516102a0526020610260604461027c5f855af1612543573d5f5f3e3d5ffd5b3d61255a57803b156128e15760016102c052612573565b60203d106128e157610260518060011c6128e1576102c0525b6102c0905051156128e15761024051610220518082018281106128e1579050905060146040516020525f5260405f2080610140516020525f5260405f209050555b600101818118612260575b5050565b63bbf7408a60c05260405160e052602060c0602460dc738e0c00ed546602fd9927df742bbabf726d5b0d165afa6125fc573d5f5f3e3d5ffd5b60203d106128e15760c05160a0526318160ddd60e052602060e0600460fc735f3b5dfeb7b28cdbd7faba78963ee202a494e2a25afa61263d573d5f5f3e3d5ffd5b60203d106128e15760e05160c052606051602881028160288204186128e157905060648104905060e05260c051156126c45760e05160805160a0518082028115838383041417156128e1579050905060c05180156128e15780820490509050603c810281603c8204186128e15790506064810490508082018281106128e1579050905060e0525b60605160e0518082811882841002189050905060e05260156040516020525f5260405f20546101005260e05160156040516020525f5260405f205560165460e0518082018281106128e15790509050610100518082038281116128e1579050905061012052610120516016556040517f7ecd84343f76a23d2227290e0288da3251b045541698e575a5515af4f04197a3606051610140526080516101605260e05161018052610120516101a0526080610140a2565b6102e051604052612788611cc4565b61030051604052612797611cc4565b61032051156128a8576002546103405260105415156103605261036051156127d5576102e051604052610340516060526040366080376127d56121f9565b60016102e0516020525f5260405f2054610320518082038281116128e15790509050610380526103805160016102e0516020525f5260405f20556102e051604052610380516060526103405160805261282c6125c3565b61036051156128515761030051604052610340516060526040366080376128516121f9565b6001610300516020525f5260405f2054610320518082018281106128e1579050905061038052610380516001610300516020525f5260405f20556103005160405261038051606052610340516080526128a86125c3565b610300516102e0517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef61032051610340526020610340a3565b5f80fd047203f31c2d02c91c2d1c2d1c2d08ec1493082f07f01c2d0b6c0c441c2d0fe81b7311ec1c2d13bc1c2d04d3145a129d07941c2d1c2d0f061c2d0131072a1c2d1c2d1c2d1c2d1c2d185e0bee06ea15491c2d0b1c1c2d1c2d1c2d158a1c2d1c2d1c2d00b7088e001819b505150d6319441c2d070a084f0f861c2d0afc023f1c2dc8ee04fdc2a10ca266f610dee88f8c2e5e8e24119b7a8cb3b875c96e752eafad000000000000000000000000000000000000000000000000000000000000000160cd0881331f0a20bd8e377e8a9dea3ccfcc801d79964789cacc91d949c21fc34e00744274f25bf8def6ad677384cfdd6e3a93ee052683c404982ed6c320866d