Contract 0x230a59F4d9ADc147480f03B0D3fFfeCd56c3289a 7

Txn Hash Method
Block
From
To
Value [Txn Fee]
0xf1b6844ed66ea8b26e126184356bd33b52ebf9edb5bdfb8040f8bb72849f759d0x6020610e266152082023-02-23 0:02:45106 days 15 hrs ago0x089775364ea24957da94348cf740375826ed3330 IN  Create: Vyper_contract0 xDAI0.001567694065
[ Download CSV Export 
View more zero value Internal Transactions in Advanced View mode
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vyper_contract

Compiler Version
vyper:0.3.1

Optimization Enabled:
N/A

Other Settings:
, None license

Contract Source Code (Vyper language format)

# @version 0.3.1
"""
@title Child-Chain Streamer
@author Curve.Fi
@license MIT
@notice Evenly streams one or more reward tokens to a single recipient
"""

from vyper.interfaces import ERC20

event RewardDistributorUpdated:
    reward_token: indexed(address)
    distributor: address

event RewardDurationUpdated:
    reward_token: indexed(address)
    duration: uint256

struct RewardToken:
    distributor: address
    period_finish: uint256
    rate: uint256
    duration: uint256
    received: uint256
    paid: uint256

MAX_REWARDS: constant(uint256) = 8
WEEK: constant(uint256) = 7 * 86400

BAL_TOKEN: immutable(address)
AUTHORIZER_ADAPTOR: immutable(address)

# The reward receiver is actually immutable, but kept in storage due to this contract being used via proxies
reward_receiver: public(address)

reward_tokens: public(address[MAX_REWARDS])
reward_count: public(uint256)
reward_data: public(HashMap[address, RewardToken])
last_update_time: public(uint256)


@external
def __init__(_bal_token: address, _authorizerAdaptor: address):
    BAL_TOKEN = _bal_token
    AUTHORIZER_ADAPTOR = _authorizerAdaptor

    # prevent initialization of implementation
    self.reward_receiver = 0x000000000000000000000000000000000000dEaD


@internal
def _add_reward(_token: address, _distributor: address, _duration: uint256):
    """
    @notice Add a reward token
    @param _token Address of the reward token
    @param _distributor Address permitted to call `notify_reward_amount` for this token
    @param _duration Number of seconds that rewards of this token are streamed over
    """
    assert self.reward_data[_token].distributor == ZERO_ADDRESS, "Reward token already added"

    idx: uint256 = self.reward_count
    self.reward_tokens[idx] = _token
    self.reward_count = idx + 1
    self.reward_data[_token].distributor = _distributor
    self.reward_data[_token].duration = _duration
    log RewardDistributorUpdated(_token, _distributor)
    log RewardDurationUpdated(_token, _duration)


@external
def add_reward(_token: address, _distributor: address, _duration: uint256):
    """
    @notice Add a reward token
    @param _token Address of the reward token
    @param _distributor Address permitted to call `notify_reward_amount` for this token
    @param _duration Number of seconds that rewards of this token are streamed over
    """
    assert msg.sender == AUTHORIZER_ADAPTOR  # dev: owner only
    self._add_reward(_token, _distributor, _duration)

@external
def remove_reward(_token: address, _recipient: address):
    """
    @notice Remove a reward token
    @dev Any remaining balance of the reward token is transferred to the owner
    @param _token Address of the reward token
    @param _recipient Address to receive the removed tokens
    """
    assert msg.sender == AUTHORIZER_ADAPTOR  # dev: only owner
    assert self.reward_data[_token].distributor != ZERO_ADDRESS, "Reward token not added"

    self.reward_data[_token] = empty(RewardToken)
    amount: uint256 = ERC20(_token).balanceOf(self)
    response: Bytes[32] = raw_call(
        _token,
        concat(
            method_id("transfer(address,uint256)"),
            convert(_recipient, bytes32),
            convert(amount, bytes32),
        ),
        max_outsize=32,
    )
    if len(response) != 0:
        assert convert(response, bool)

    idx: uint256 = self.reward_count - 1
    for i in range(MAX_REWARDS):
        if self.reward_tokens[i] == _token:
            self.reward_tokens[i] = self.reward_tokens[idx]
            self.reward_tokens[idx] = ZERO_ADDRESS
            self.reward_count = idx
            log RewardDistributorUpdated(_token, ZERO_ADDRESS)
            log RewardDurationUpdated(_token, 0)
            return
    raise  # this should never be reached


@internal
def _update_reward(_token: address, _last_update: uint256):
    # update data about a reward and distribute any pending tokens to the receiver
    last_time: uint256 = min(block.timestamp, self.reward_data[_token].period_finish)
    if last_time > _last_update:
        amount: uint256 = (last_time - _last_update) * self.reward_data[_token].rate
        if amount > 0:
            self.reward_data[_token].paid += amount
            response: Bytes[32] = raw_call(
                _token,
                concat(
                    method_id("transfer(address,uint256)"),
                    convert(self.reward_receiver, bytes32),
                    convert(amount, bytes32),
                ),
                max_outsize=32,
            )
            if len(response) != 0:
                assert convert(response, bool)

@external
def get_reward():
    """
    @notice Claim pending rewards for `reward_receiver`
    """
    last_update: uint256 = self.last_update_time
    for token in self.reward_tokens:
        if token == ZERO_ADDRESS:
            break
        self._update_reward(token, last_update)
    self.last_update_time = block.timestamp


@external
def notify_reward_amount(_token: address):
    """
    @notice Notify the contract of a newly received reward
    @dev Only callable by the distributor if there is an active reward period.
         The reward tokens must be transferred into the contract prior to calling
         this function. Rewards are distributed over `reward_duration` seconds.
         Updating the reward amount while an existing reward period is still active
         causes the remaining rewards to be evenly distributed over the new period.
    @param _token Address of the reward token
    """
    last_update: uint256 = self.last_update_time
    is_updated: bool = False
    for token in self.reward_tokens:
        if token == ZERO_ADDRESS:
            break

        self._update_reward(token, last_update)
        if token == _token:
            received: uint256 = self.reward_data[token].received
            expected_balance: uint256 = received - self.reward_data[token].paid
            actual_balance: uint256 = ERC20(token).balanceOf(self)

            if actual_balance > expected_balance:
                new_amount: uint256 = actual_balance - expected_balance
                duration: uint256 = self.reward_data[token].duration

                if block.timestamp >= self.reward_data[token].period_finish:
                    self.reward_data[token].rate = new_amount / duration
                else:
                    assert msg.sender == self.reward_data[_token].distributor, "Reward period still active"
                    remaining: uint256 = self.reward_data[token].period_finish - block.timestamp
                    leftover: uint256 = remaining * self.reward_data[token].rate
                    self.reward_data[token].rate = (new_amount + leftover) / duration

                self.reward_data[token].period_finish = block.timestamp + duration
                self.reward_data[token].received = received + new_amount
                is_updated = True

    assert is_updated, "Invalid token or no new reward"
    self.last_update_time = block.timestamp


@external
def set_reward_duration(_token: address, _duration: uint256):
    """
    @notice Modify the duration that rewards are distributed over
    @dev Only callable when there is not an active reward period
    @param _token Address of the reward token
    @param _duration Number of seconds to distribute rewards over
    """
    assert msg.sender == self.reward_data[_token].distributor  # dev: only owner
    assert block.timestamp > self.reward_data[_token].period_finish, "Reward period still active"
    self.reward_data[_token].duration = _duration
    log RewardDurationUpdated(_token, _duration)


@external
def set_reward_distributor(_token: address, _distributor: address):
    """
    @notice Modify the reward distributor
    @param _token Address of the reward token
    @param _distributor Reward distributor
    """
    assert msg.sender == self.reward_data[_token].distributor or msg.sender == AUTHORIZER_ADAPTOR
    self.reward_data[_token].distributor = _distributor
    log RewardDistributorUpdated(_token, _distributor)

# Initializer

@external
def initialize(reward_receiver: address):
    """
    @notice Contract constructor
    @param reward_receiver RewardsOnlyGauge address
    """
    assert self.reward_receiver == ZERO_ADDRESS
    assert reward_receiver != ZERO_ADDRESS
    
    self.reward_receiver = reward_receiver

    # The first reward token will always be BAL, we then have the authorizer adaptor
    # as the distributor to ensure that governance has the ability to distribute.
    # The Authorizer adaptor can always update the distributor should Balancer governance wish.
    self._add_reward(BAL_TOKEN, AUTHORIZER_ADAPTOR, WEEK)

Contract ABI

[{"name":"RewardDistributorUpdated","inputs":[{"name":"reward_token","type":"address","indexed":true},{"name":"distributor","type":"address","indexed":false}],"anonymous":false,"type":"event"},{"name":"RewardDurationUpdated","inputs":[{"name":"reward_token","type":"address","indexed":true},{"name":"duration","type":"uint256","indexed":false}],"anonymous":false,"type":"event"},{"stateMutability":"nonpayable","type":"constructor","inputs":[{"name":"_bal_token","type":"address"},{"name":"_authorizerAdaptor","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"add_reward","inputs":[{"name":"_token","type":"address"},{"name":"_distributor","type":"address"},{"name":"_duration","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"remove_reward","inputs":[{"name":"_token","type":"address"},{"name":"_recipient","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"get_reward","inputs":[],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"notify_reward_amount","inputs":[{"name":"_token","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_reward_duration","inputs":[{"name":"_token","type":"address"},{"name":"_duration","type":"uint256"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"set_reward_distributor","inputs":[{"name":"_token","type":"address"},{"name":"_distributor","type":"address"}],"outputs":[]},{"stateMutability":"nonpayable","type":"function","name":"initialize","inputs":[{"name":"reward_receiver","type":"address"}],"outputs":[]},{"stateMutability":"view","type":"function","name":"reward_receiver","inputs":[],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"reward_tokens","inputs":[{"name":"arg0","type":"uint256"}],"outputs":[{"name":"","type":"address"}]},{"stateMutability":"view","type":"function","name":"reward_count","inputs":[],"outputs":[{"name":"","type":"uint256"}]},{"stateMutability":"view","type":"function","name":"reward_data","inputs":[{"name":"arg0","type":"address"}],"outputs":[{"name":"","type":"tuple","components":[{"name":"distributor","type":"address"},{"name":"period_finish","type":"uint256"},{"name":"rate","type":"uint256"},{"name":"duration","type":"uint256"},{"name":"received","type":"uint256"},{"name":"paid","type":"uint256"}]}]},{"stateMutability":"view","type":"function","name":"last_update_time","inputs":[],"outputs":[{"name":"","type":"uint256"}]}]

6020610e176080396080518060a01c610e125760e05260206020610e17016080396080518060a01c610e12576101005260e05161012052610100516101405261dead600055610de056600436101561000d57610a8d565b60046000601c3760005134610d925763661ab0b28118610077576004358060a01c610d9257610180526024358060a01c610d92576101a0526020602038036080396080513318610d92576101805160e0526101a0516101005260443561012052610075610a93565b005b63393e582b81186103ac576004358060a01c610d925760e0526024358060a01c610d9257610100526020602038036080396080513318610d92576000600a60e05160a052608052604060802054141561013f576016610120527f52657761726420746f6b656e206e6f74206164646564000000000000000000006101405261012050610120518061014001818260206001820306601f82010390500336823750506308c379a060e0526020610100526101205160206001820306601f820103905060440160fcfd5b600a60e05160a0526080526040608020600081556000600182015560006002820155600060038201556000600482015560006005820155506370a082316101405230610160526020610140602461015c60e0515afa6101a3573d600060003e3d6000fd5b601f3d1115610d9257610140516101205260006004610180527fa9059cbb000000000000000000000000000000000000000000000000000000006101a0526101806004806020846101c00101826020850160045afa505080518201915050610100516020826101c0010152602081019050610120516020826101c0010152602081019050806101c0526101c0505060206102606101c0516101e0600060e0515af1610253573d600060003e3d6000fd5b61024060203d8082116102665781610268565b805b905090508152805160200180610140828460045afa90505050600061014051146102a757610160516101405181816020036008021c9050905015610d92575b6009546001808210610d925780820390509050610180526101a060006008818352015b60e05160016101a0516008811015610d9257026001015418610393576001610180516008811015610d9257026001015460016101a0516008811015610d9257026001015560006001610180516008811015610d925702600101556101805160095560e0517f1df95772db25c427e0df149fe4cd5b7c2e970e096ee7305ff86f6e1167281d7b60006101c05260206101c0a260e0517f2c59073844bbfca50d5cce6497d479fe434747687af5de022514e5a0c8ba479c60006101c05260206101c0a25050506103aa565b81516001018083528114156102ca57505060006000fd5b005b631afe22a6811861041257600b546102a0526102e060006008818352015b6102e051600101546102c0526102c0516103e35761040a565b6102c05160e0526102a051610100526103fa610bea565b81516001018083528114156103ca575b505042600b55005b63a4f6af7081186107ac576004358060a01c610d92576102a052600b546102c05260006102e05261032060006008818352015b6103205160010154610300526103005161045e57610725565b6103005160e0526102c05161010052610475610bea565b6102a0516103005118610715576004600a6103005160a0526080526040608020015461034052610340516005600a6103005160a05260805260406080200154808210610d925780820390509050610360526370a082316103a052306103c05260206103a060246103bc610300515afa6104f3573d600060003e3d6000fd5b601f3d1115610d92576103a0516103805261036051610380511115610715576103805161036051808210610d9257808203905090506103a0526003600a6103005160a052608052604060802001546103c0526001600a6103005160a0526080526040608020015442101561068c57600a6102a05160a05260805260406080205433146105f057601a6103e0527f52657761726420706572696f64207374696c6c20616374697665000000000000610400526103e0506103e0518061040001818260206001820306601f82010390500336823750506308c379a06103a05260206103c0526103e05160206001820306601f82010390506044016103bcfd5b6001600a6103005160a0526080526040608020015442808210610d9257808203905090506103e0526103e0516002600a6103005160a05260805260406080200154808202821582848304141715610d925790509050610400526103a051610400518181830110610d9257808201905090506103c051808015610d92578204905090506002600a6103005160a052608052604060802001556106b7565b6103a0516103c051808015610d92578204905090506002600a6103005160a052608052604060802001555b426103c0518181830110610d9257808201905090506001600a6103005160a05260805260406080200155610340516103a0518181830110610d9257808201905090506004600a6103005160a0526080526040608020015560016102e0525b8151600101808352811415610445575b50506102e0516107a657601e610300527f496e76616c696420746f6b656e206f72206e6f206e65772072657761726400006103205261030050610300518061032001818260206001820306601f82010390500336823750506308c379a06102c05260206102e0526103005160206001820306601f82010390506044016102dcfd5b42600b55005b63d88449ee81186108b4576004358060a01c610d925760e052600a60e05160a0526080526040608020543318610d92576001600a60e05160a05260805260406080200154421161086a57601a610100527f52657761726420706572696f64207374696c6c206163746976650000000000006101205261010050610100518061012001818260206001820306601f82010390500336823750506308c379a060c052602060e0526101005160206001820306601f820103905060440160dcfd5b6024356003600a60e05160a0526080526040608020015560e0517f2c59073844bbfca50d5cce6497d479fe434747687af5de022514e5a0c8ba479c602435610100526020610100a2005b63058a3a248118610957576004358060a01c610d925760e0526024358060a01c610d925761010052600a60e05160a05260805260406080205433186108fa576001610909565b60206020380360803960805133145b15610d925761010051600a60e05160a05260805260406080205560e0517f1df95772db25c427e0df149fe4cd5b7c2e970e096ee7305ff86f6e1167281d7b61010051610120526020610120a2005b63c4d66de881186109bb576004358060a01c610d925761018052600054610d925760006101805114610d92576101805160005560206040380360803960805160e0526020602038036080396080516101005262093a80610120526109b9610a93565b005b63b618ba6281186109d25760005460e052602060e0f35b6354c49fe981186109f95760016004356008811015610d9257026001015460e052602060e0f35b63963c94b98118610a105760095460e052602060e0f35b6348e9c65e8118610a74576004358060a01c610d925760e052600a60e05160a052608052604060802080546101005260018101546101205260028101546101405260038101546101605260048101546101805260058101546101a0525060c0610100f35b63629d46c28118610a8b57600b5460e052602060e0f35b505b60006000fd5b600a60e05160a05260805260406080205415610b2057601a610140527f52657761726420746f6b656e20616c72656164792061646465640000000000006101605261014050610140518061016001818260206001820306601f82010390500336823750506308c379a0610100526020610120526101405160206001820306601f820103905060440161011cfd5b6009546101405260e0516001610140516008811015610d925702600101556101405160018181830110610d92578082019050905060095561010051600a60e05160a052608052604060802055610120516003600a60e05160a0526080526040608020015560e0517f1df95772db25c427e0df149fe4cd5b7c2e970e096ee7305ff86f6e1167281d7b61010051610160526020610160a260e0517f2c59073844bbfca50d5cce6497d479fe434747687af5de022514e5a0c8ba479c61012051610160526020610160a2565b426001600a60e05160a05260805260406080200154808211610c0c5781610c0e565b805b905090506101205261010051610120511115610d90576101205161010051808210610d9257808203905090506002600a60e05160a05260805260406080200154808202821582848304141715610d925790509050610140526000610140511115610d90576005600a60e05160a0526080526040608020018054610140518181830110610d925780820190509050815550600060046101a0527fa9059cbb000000000000000000000000000000000000000000000000000000006101c0526101a06004806020846101e00101826020850160045afa5050805182019150506000546020826101e0010152602081019050610140516020826101e0010152602081019050806101e0526101e0505060206102806101e051610200600060e0515af1610d3c573d600060003e3d6000fd5b61026060203d808211610d4f5781610d51565b805b905090508152805160200180610160828460045afa9050505060006101605114610d9057610180516101605181816020036008021c9050905015610d92575b565b600080fd5b610049610de00361004961016039610049610de003610120518161016001526101405181610180015280604001610160f35b600080fd0000000000000000000000007ef541e2a22058048904fe5744f9c7e4c57af7170000000000000000000000005addcca35b7a0d07c74063c48700c8590e87864e

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

0000000000000000000000007ef541e2a22058048904fe5744f9c7e4c57af7170000000000000000000000005addcca35b7a0d07c74063c48700c8590e87864e

-----Decoded View---------------
Arg [0] : _bal_token (address): 0x7ef541e2a22058048904fe5744f9c7e4c57af717
Arg [1] : _authorizerAdaptor (address): 0x5addcca35b7a0d07c74063c48700c8590e87864e

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 0000000000000000000000007ef541e2a22058048904fe5744f9c7e4c57af717
Arg [1] : 0000000000000000000000005addcca35b7a0d07c74063c48700c8590e87864e


Block Transaction Gas Used Reward
Age Block Fee Address BC Fee Address Voting Power Jailed Incoming
Block Uncle Number Difficulty Gas Used Reward
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.