Snapshot Splitter

Description:

Smart contract deployed on Ethereum with Factory features.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Vyper",
  "sources": {
    ".venv/lib/pypy3.11/site-packages/snekmate/auth/ownable.vy": {
      "content": "# pragma version ~=0.4.3
# pragma nonreentrancy off
"""
@title Owner-Based Access Control Functions
@custom:contract-name ownable
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
@notice These functions can be used to implement a basic access
        control mechanism, where there is an account (an owner)
        that can be granted exclusive access to specific functions.
        By default, the owner account will be the one that deploys
        the contract. This can later be changed with `transfer_ownership`.
        An exemplary integration can be found in the ERC-20 implementation here:
        https://github.com/pcaversaccio/snekmate/blob/main/src/snekmate/tokens/erc20.vy.
        The implementation is inspired by OpenZeppelin's implementation here:
        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/access/Ownable.sol.
"""


# @dev Returns the address of the current owner.
# @notice If you declare a variable as `public`,
# Vyper automatically generates an `external`
# getter function for the variable.
owner: public(address)


# @dev Emitted when the ownership is transferred
# from `previous_owner` to `new_owner`.
event OwnershipTransferred:
    previous_owner: indexed(address)
    new_owner: indexed(address)


@deploy
@payable
def __init__():
    """
    @dev To omit the opcodes for checking the `msg.value`
         in the creation-time EVM bytecode, the constructor
         is declared as `payable`.
    @notice The `owner` role will be assigned to
            the `msg.sender`.
    """
    self._transfer_ownership(msg.sender)


@external
def transfer_ownership(new_owner: address):
    """
    @dev Transfers the ownership of the contract
         to a new account `new_owner`.
    @notice Note that this function can only be
            called by the current `owner`. Also,
            the `new_owner` cannot be the zero address.
    @param new_owner The 20-byte address of the new owner.
    """
    self._check_owner()
    assert new_owner != empty(address), "ownable: new owner is the zero address"
    self._transfer_ownership(new_owner)


@external
def renounce_ownership():
    """
    @dev Leaves the contract without an owner.
    @notice Renouncing ownership will leave the
            contract without an owner, thereby
            removing any functionality that is
            only available to the owner.
    """
    self._check_owner()
    self._transfer_ownership(empty(address))


@internal
def _check_owner():
    """
    @dev Throws if the sender is not the owner.
    """
    assert msg.sender == self.owner, "ownable: caller is not the owner"


@internal
def _transfer_ownership(new_owner: address):
    """
    @dev Transfers the ownership of the contract
         to a new account `new_owner`.
    @notice This is an `internal` function without
            access restriction.
    @param new_owner The 20-byte address of the new owner.
    """
    old_owner: address = self.owner
    self.owner = new_owner
    log OwnershipTransferred(previous_owner=old_owner, new_owner=new_owner)
",
      "sha256sum": "2bebfade7e8fab0293285cac09686d2747423553081e45dd9f35b25801253dc1"
    },
    "contracts/dao/SnapshotSplitter.vy": {
      "content": "# @version 0.4.3
"""
@title Snapshot Splitter
@author Yield Basis
@license MIT
@notice Splits allocation of deposited tokens towards veCRV (old Aragon) balances who voted for specified votes
"""
from snekmate.auth import ownable
from ethereum.ercs import IERC20


initializes: ownable

exports: (
    ownable.renounce_ownership,
    ownable.owner
)


interface Aragon:
    # VoterState: 0 = Absent, 1 = Yes, 2 = No, 3 = Even
    def getVoterState(_voteID: uint256, _voter: address) -> uint8: view
    def getVote(_voteID: uint256) -> VoteState: view

interface VeCRV:
    def balanceOfAt(user: address, block: uint256) -> uint256: view


struct VoteState:
    open: bool
    executed: bool
    startDate: uint64
    snapshotBlock: uint256
    supportRequired: uint64
    minAcceptQuorum: uint64
    yea: uint256
    nay: uint256
    votingPower: uint256
    script: Bytes[1000]


struct WeightedVote:
    vid: uint256
    weight: uint256
    block: uint256


ARAGON: public(immutable(Aragon))
VE: public(immutable(VeCRV))
TOKEN: public(immutable(IERC20))
splits: public(HashMap[uint256, HashMap[address, uint256]])
weighted_votes: public(WeightedVote[10])
address_mappings: public(HashMap[address, address])
claimed: public(HashMap[address, bool])
total_claimed: public(uint256)


@deploy
def __init__(aragon: Aragon, ve: VeCRV, token: IERC20):
    """
    For Curve voting: Aragon = 0xE478de485ad2fe566d49342Cbd03E49ed7DB3356
    """
    ownable.__init__()
    ARAGON = aragon
    VE = ve
    TOKEN = token


@external
def register_split(vote_id: uint256, voter: address, yay: uint256, nay: uint256):
    ownable._check_owner()
    assert yay > 0 and nay > 0
    self.splits[vote_id][voter] = 10**18 * yay // (yay + nay)


@external
def register_votes(vote_ids: DynArray[uint256, 10], weights: DynArray[uint256, 10], relevant_ve: DynArray[uint256, 10]):
    ownable._check_owner()
    total_weight: uint256 = 0
    for w: uint256 in weights:
        total_weight += w
    i: uint256 = 0
    for vid: uint256 in vote_ids:
        state: VoteState = staticcall ARAGON.getVote(vid)
        self.weighted_votes[i] = WeightedVote(
            vid=vid,
            weight=(total_weight * relevant_ve[i] // weights[i] + 1),  # We will divide ve amount by this, so it has to be LARGER than the original weight
            block=state.snapshotBlock
        )
        i += 1


@external
def register_mappings(in_addrs: DynArray[address, 20], out_addrs: DynArray[address, 20]):
    ownable._check_owner()
    i: uint256 = 0
    for in_addr: address in in_addrs:
        self.address_mappings[in_addr] = out_addrs[i]
        i += 1


@internal
@view
def _get_fraction(voter: address) -> uint256:
    weight: uint256 = 0

    for i: uint256 in range(10):
        wv: WeightedVote = self.weighted_votes[i]
        if wv.block == 0:
            break

        vote: uint8 = staticcall ARAGON.getVoterState(wv.vid, voter)
        if vote == 1:
            split: uint256 = self.splits[wv.vid][voter]
            if split == 0:
                split = 10**18
            weight += (staticcall VE.balanceOfAt(voter, wv.block)) * split // wv.weight

    return weight


@external
@view
def get_fraction(voter: address) -> uint256:
    return self._get_fraction(voter)


@nonreentrant
@external
def claim(_for: address = msg.sender) -> uint256:
    assert ownable.owner == empty(address)
    assert not self.claimed[_for], "Already claimed"
    amount: uint256 = (staticcall TOKEN.balanceOf(self) + self.total_claimed) * self._get_fraction(_for) // 10**18
    self.claimed[_for] = True
    _to: address = self.address_mappings[_for]
    if _to == empty(address):
        _to = _for
    self.total_claimed += amount
    extcall TOKEN.transfer(_to, amount)
    return amount
",
      "sha256sum": "550472a6e5cc856b75a4451c68f4824096e80a9a30bb80721c912f452a79f0b6"
    }
  },
  "settings": {
    "outputSelection": {
      "contracts/dao/SnapshotSplitter.vy": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    },
    "search_paths": [
      ".venv/lib/pypy3.11/site-packages",
      "."
    ]
  },
  "compiler_version": "v0.4.3+commit.bff19ea2",
  "integrity": "c6b9a07d364c32cf33c29a7e55287f237a1eed7a4750e6f10074701ce34f9612"
}}

Tags:
Factory|addr:0xc8aa884f54ea5eaef5ef4086607c00580a103928|verified:true|block:23582414|tx:0x95181e0d5c2df68a548a25feb08c73bf83dfc2bdce87300e6b3a324ceda06489|first_check:1760523156

Submitted on: 2025-10-15 12:12:41

Comments

Log in to comment.

No comments yet.