账户
0x65...2eb4
0x65...2EB4

0x65...2EB4

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.3.10+commit.91361694
语言
Vyper
合同源代码
文件 1 的 1:Compass-EVM.vy
#pragma version 0.3.10
#pragma optimize gas
#pragma evm-version paris
"""
@title Compass-EVM
@author Volume.Finance
"""

MAX_VALIDATORS: constant(uint256) = 200
MAX_PAYLOAD: constant(uint256) = 10240
MAX_BATCH: constant(uint256) = 64

POWER_THRESHOLD: constant(uint256) = 2_863_311_530 # 2/3 of 2^32, Validator powers will be normalized to sum to 2 ^ 32 in every valset update.
compass_id: public(immutable(bytes32))

interface ERC20:
    def balanceOf(_owner: address) -> uint256: view
    def transfer(_to: address, _value: uint256) -> bool: nonpayable
    def transferFrom(_from: address, _to: address, _value: uint256) -> bool: nonpayable

struct Valset:
    validators: DynArray[address, MAX_VALIDATORS] # Validator addresses
    powers: DynArray[uint256, MAX_VALIDATORS] # Powers of given validators, in the same order as validators array
    valset_id: uint256 # nonce of this validator set

struct Signature:
    v: uint256
    r: uint256
    s: uint256

struct Consensus:
    valset: Valset # Valset data
    signatures: DynArray[Signature, MAX_VALIDATORS] # signatures in the same order as validator array in valset

struct LogicCallArgs:
    logic_contract_address: address # the arbitrary contract address to external call
    payload: Bytes[MAX_PAYLOAD] # payloads

struct TokenSendArgs:
    receiver: DynArray[address, MAX_BATCH]
    amount: DynArray[uint256, MAX_BATCH]

event ValsetUpdated:
    checkpoint: bytes32
    valset_id: uint256
    event_id: uint256

event LogicCallEvent:
    logic_contract_address: address
    payload: Bytes[MAX_PAYLOAD]
    message_id: uint256
    event_id: uint256

event SendToPalomaEvent:
    token: address
    sender: address
    receiver: String[64]
    amount: uint256
    event_id: uint256

event BatchSendEvent:
    token: address
    batch_id: uint256
    event_id: uint256

event ERC20DeployedEvent:
    paloma_denom: String[64]
    token_contract: address
    name: String[64]
    symbol: String[32]
    decimals: uint8
    event_id: uint256

last_checkpoint: public(bytes32)
last_valset_id: public(uint256)
last_event_id: public(uint256)
last_batch_id: public(HashMap[address, uint256])
message_id_used: public(HashMap[uint256, bool])

# compass_id: unique identifier for compass instance
# valset: initial validator set
@external
def __init__(_compass_id: bytes32, _event_id: uint256, valset: Valset):
    compass_id = _compass_id
    cumulative_power: uint256 = 0
    i: uint256 = 0
    # check cumulative power is enough
    for validator in valset.validators:
        cumulative_power += valset.powers[i]
        if cumulative_power >= POWER_THRESHOLD:
            break
        i = unsafe_add(i, 1)
    assert cumulative_power >= POWER_THRESHOLD, "Insufficient Power"
    new_checkpoint: bytes32 = keccak256(_abi_encode(valset.validators, valset.powers, valset.valset_id, compass_id, method_id=method_id("checkpoint(address[],uint256[],uint256,bytes32)")))
    self.last_checkpoint = new_checkpoint
    self.last_valset_id = valset.valset_id
    self.last_event_id = _event_id
    log ValsetUpdated(new_checkpoint, valset.valset_id, _event_id)

# utility function to verify EIP712 signature
@internal
@pure
def verify_signature(signer: address, hash: bytes32, sig: Signature) -> bool:
    message_digest: bytes32 = keccak256(concat(convert("\x19Ethereum Signed Message:\n32", Bytes[28]), hash))
    return signer == ecrecover(message_digest, sig.v, sig.r, sig.s)

# consensus: validator set and signatures
# hash: what we are checking they have signed
@internal
def check_validator_signatures(consensus: Consensus, hash: bytes32):
    i: uint256 = 0
    cumulative_power: uint256 = 0
    for sig in consensus.signatures:
        if sig.v != 0:
            assert self.verify_signature(consensus.valset.validators[i], hash, sig), "Invalid Signature"
            cumulative_power += consensus.valset.powers[i]
            if cumulative_power >= POWER_THRESHOLD:
                break
        i = unsafe_add(i, 1)
    assert cumulative_power >= POWER_THRESHOLD, "Insufficient Power"

# Make a new checkpoint from the supplied validator set
# A checkpoint is a hash of all relevant information about the valset. This is stored by the contract,
# instead of storing the information directly. This saves on storage and gas.
# The format of the checkpoint is:
# keccak256 hash of abi_encoded checkpoint(validators[], powers[], valset_id, compass_id)
# The validator powers must be decreasing or equal. This is important for checking the signatures on the
# next valset, since it allows the caller to stop verifying signatures once a quorum of signatures have been verified.
@internal
@view
def make_checkpoint(valset: Valset) -> bytes32:
    return keccak256(_abi_encode(valset.validators, valset.powers, valset.valset_id, compass_id, method_id=method_id("checkpoint(address[],uint256[],uint256,bytes32)")))

# This updates the valset by checking that the validators in the current valset have signed off on the
# new valset. The signatures supplied are the signatures of the current valset over the checkpoint hash
# generated from the new valset.
# Anyone can call this function, but they must supply valid signatures of constant_powerThreshold of the current valset over
# the new valset.
# valset: new validator set to update with
# consensus: current validator set and signatures
@external
def update_valset(consensus: Consensus, new_valset: Valset):
    # check if new valset_id is greater than current valset_id
    assert new_valset.valset_id > consensus.valset.valset_id, "Invalid Valset ID"
    cumulative_power: uint256 = 0
    i: uint256 = 0
    # check cumulative power is enough
    for validator in new_valset.validators:
        cumulative_power += new_valset.powers[i]
        if cumulative_power >= POWER_THRESHOLD:
            break
        i = unsafe_add(i, 1)
    assert cumulative_power >= POWER_THRESHOLD, "Insufficient Power"
    # check if the supplied current validator set matches the saved checkpoint
    assert self.last_checkpoint == self.make_checkpoint(consensus.valset), "Incorrect Checkpoint"
    # calculate the new checkpoint
    new_checkpoint: bytes32 = self.make_checkpoint(new_valset)
    # check if enough validators signed new validator set (new checkpoint)
    self.check_validator_signatures(consensus, new_checkpoint)
    self.last_checkpoint = new_checkpoint
    self.last_valset_id = new_valset.valset_id
    event_id: uint256 = unsafe_add(self.last_event_id, 1)
    self.last_event_id = event_id
    log ValsetUpdated(new_checkpoint, new_valset.valset_id, event_id)

# This makes calls to contracts that execute arbitrary logic
# message_id is to prevent replay attack and every message_id can be used only once
@external
def submit_logic_call(consensus: Consensus, args: LogicCallArgs, message_id: uint256, deadline: uint256):
    assert block.timestamp <= deadline, "Timeout"
    assert not self.message_id_used[message_id], "Used Message_ID"
    self.message_id_used[message_id] = True
    # check if the supplied current validator set matches the saved checkpoint
    assert self.last_checkpoint == self.make_checkpoint(consensus.valset), "Incorrect Checkpoint"
    # signing data is keccak256 hash of abi_encoded logic_call(args, message_id, compass_id, deadline)
    args_hash: bytes32 = keccak256(_abi_encode(args, message_id, compass_id, deadline, method_id=method_id("logic_call((address,bytes),uint256,bytes32,uint256)")))
    # check if enough validators signed args_hash
    self.check_validator_signatures(consensus, args_hash)
    # make call to logic contract
    raw_call(args.logic_contract_address, args.payload)
    event_id: uint256 = unsafe_add(self.last_event_id, 1)
    self.last_event_id = event_id
    log LogicCallEvent(args.logic_contract_address, args.payload, message_id, event_id)

@external
def send_token_to_paloma(token: address, receiver: String[64], amount: uint256):
    _balance: uint256 = ERC20(token).balanceOf(self)
    assert ERC20(token).transferFrom(msg.sender, self, amount, default_return_value=True), "TF fail"
    _balance = ERC20(token).balanceOf(self) - _balance
    assert _balance > 0, "Zero Transfer"
    event_id: uint256 = unsafe_add(self.last_event_id, 1)
    self.last_event_id = event_id
    log SendToPalomaEvent(token, msg.sender, receiver, amount, event_id)

@external
def submit_batch(consensus: Consensus, token: address, args: TokenSendArgs, batch_id: uint256, deadline: uint256):
    assert block.timestamp <= deadline, "Timeout"
    assert self.last_batch_id[token] < batch_id, "Wrong batch id"
    length: uint256 = len(args.receiver)
    assert length == len(args.amount), "Unmatched Params"
    # check if the supplied current validator set matches the saved checkpoint
    assert self.last_checkpoint == self.make_checkpoint(consensus.valset), "Incorrect Checkpoint"
    # signing data is keccak256 hash of abi_encoded batch_call(args, batch_id, compass_id, deadline)
    args_hash: bytes32 = keccak256(_abi_encode(token, args, batch_id, compass_id, deadline, method_id=method_id("batch_call(address,(address[],uint256[]),uint256,bytes32,uint256)")))
    # check if enough validators signed args_hash
    self.check_validator_signatures(consensus, args_hash)
    # make call to logic contract
    for i in range(MAX_BATCH):
        if  i >= length:
            break
        assert ERC20(token).transfer(args.receiver[i], args.amount[i], default_return_value=True), "Tr fail"
    event_id: uint256 = unsafe_add(self.last_event_id, 1)
    self.last_event_id = event_id
    self.last_batch_id[token] = batch_id
    log BatchSendEvent(token, batch_id, event_id)

@external
def deploy_erc20(_paloma_denom: String[64], _name: String[64], _symbol: String[32], _decimals: uint8, _blueprint: address):
    assert msg.sender == self, "Invalid"
    erc20: address = create_from_blueprint(_blueprint, self, _name, _symbol, _decimals, code_offset=3)
    event_id: uint256 = unsafe_add(self.last_event_id, 1)
    self.last_event_id = event_id
    log ERC20DeployedEvent(_paloma_denom, erc20, _name, _symbol, _decimals, event_id)
设置
{
  "compilationTarget": {
    "Compass-EVM.vy": "Compass-EVM"
  },
  "evmVersion": "paris",
  "outputSelection": {
    "Compass-EVM.vy": [
      "abi",
      "ast",
      "interface",
      "ir",
      "userdoc",
      "devdoc",
      "evm.bytecode.object",
      "evm.bytecode.opcodes",
      "evm.deployedBytecode.object",
      "evm.deployedBytecode.opcodes",
      "evm.deployedBytecode.sourceMap",
      "evm.methodIdentifiers"
    ]
  },
  "search_paths": [
    "."
  ]
}
ABI
[{"anonymous":false,"inputs":[{"indexed":false,"name":"checkpoint","type":"bytes32"},{"indexed":false,"name":"valset_id","type":"uint256"},{"indexed":false,"name":"event_id","type":"uint256"}],"name":"ValsetUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"logic_contract_address","type":"address"},{"indexed":false,"name":"payload","type":"bytes"},{"indexed":false,"name":"message_id","type":"uint256"},{"indexed":false,"name":"event_id","type":"uint256"}],"name":"LogicCallEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"receiver","type":"string"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"event_id","type":"uint256"}],"name":"SendToPalomaEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"batch_id","type":"uint256"},{"indexed":false,"name":"event_id","type":"uint256"}],"name":"BatchSendEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"paloma_denom","type":"string"},{"indexed":false,"name":"token_contract","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"event_id","type":"uint256"}],"name":"ERC20DeployedEvent","type":"event"},{"inputs":[{"name":"_compass_id","type":"bytes32"},{"name":"_event_id","type":"uint256"},{"components":[{"name":"validators","type":"address[]"},{"name":"powers","type":"uint256[]"},{"name":"valset_id","type":"uint256"}],"name":"valset","type":"tuple"}],"outputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"components":[{"name":"validators","type":"address[]"},{"name":"powers","type":"uint256[]"},{"name":"valset_id","type":"uint256"}],"name":"valset","type":"tuple"},{"components":[{"name":"v","type":"uint256"},{"name":"r","type":"uint256"},{"name":"s","type":"uint256"}],"name":"signatures","type":"tuple[]"}],"name":"consensus","type":"tuple"},{"components":[{"name":"validators","type":"address[]"},{"name":"powers","type":"uint256[]"},{"name":"valset_id","type":"uint256"}],"name":"new_valset","type":"tuple"}],"name":"update_valset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"name":"validators","type":"address[]"},{"name":"powers","type":"uint256[]"},{"name":"valset_id","type":"uint256"}],"name":"valset","type":"tuple"},{"components":[{"name":"v","type":"uint256"},{"name":"r","type":"uint256"},{"name":"s","type":"uint256"}],"name":"signatures","type":"tuple[]"}],"name":"consensus","type":"tuple"},{"components":[{"name":"logic_contract_address","type":"address"},{"name":"payload","type":"bytes"}],"name":"args","type":"tuple"},{"name":"message_id","type":"uint256"},{"name":"deadline","type":"uint256"}],"name":"submit_logic_call","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"token","type":"address"},{"name":"receiver","type":"string"},{"name":"amount","type":"uint256"}],"name":"send_token_to_paloma","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"name":"validators","type":"address[]"},{"name":"powers","type":"uint256[]"},{"name":"valset_id","type":"uint256"}],"name":"valset","type":"tuple"},{"components":[{"name":"v","type":"uint256"},{"name":"r","type":"uint256"},{"name":"s","type":"uint256"}],"name":"signatures","type":"tuple[]"}],"name":"consensus","type":"tuple"},{"name":"token","type":"address"},{"components":[{"name":"receiver","type":"address[]"},{"name":"amount","type":"uint256[]"}],"name":"args","type":"tuple"},{"name":"batch_id","type":"uint256"},{"name":"deadline","type":"uint256"}],"name":"submit_batch","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_paloma_denom","type":"string"},{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_blueprint","type":"address"}],"name":"deploy_erc20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"compass_id","outputs":[{"name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_checkpoint","outputs":[{"name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_valset_id","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"last_event_id","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"address"}],"name":"last_batch_id","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"name":"arg0","type":"uint256"}],"name":"message_id_used","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"}]