Skip to content

Instantly share code, notes, and snippets.

@fubuloubu
Created February 28, 2019 18:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fubuloubu/3a51757f5dbbce8af9c5c294a0f0d201 to your computer and use it in GitHub Desktop.
Save fubuloubu/3a51757f5dbbce8af9c5c294a0f0d201 to your computer and use it in GitHub Desktop.
import random
from web3 import Web3, EthereumTesterProvider
from vyper import compile_code as compiler
from eth_tester.exceptions import TransactionFailed
w3 = Web3(EthereumTesterProvider())
max_stakers = 5
with open('cma.vy', 'r') as f:
interface = compiler(f.read(),
output_formats=['abi', 'bytecode', 'bytecode_runtime'])
txn_hash = w3.eth.contract(**interface).constructor().transact()
receipt = w3.eth.waitForTransactionReceipt(txn_hash)
contract = w3.eth.contract(receipt['contractAddress'], **interface)
logout_filter = contract.events.LogoutStarted.createFilter(fromBlock=receipt['blockNumber'])
def print_accounts():
accounts = [(a, contract.functions.staked(a).call()) for a in w3.eth.accounts]
sorted_accounts = list(reversed(sorted(accounts, key=lambda a: a[1])))
sorted_accounts = [a for a in sorted_accounts if a[1] != 0]
[print(s, "staked", a, "tokens") for s, a in sorted_accounts]
if len(sorted_accounts) > 0:
print("True average:", sum([a for _, a in sorted_accounts]) / len(sorted_accounts))
print()
avg_stake = 1000
while avg_stake < 2000:
try:
# Clear out stakers that are below the threshold
for loser in w3.eth.accounts:
amount = contract.functions.staked(loser).call()
if contract.functions.num_staked().call() > max_stakers and amount != 0 and amount < avg_stake:
print(loser, "is logging out", amount, "tokens")
contract.functions.logout().transact({'from':loser})
# Choose a staker at random to either stake as new or existing account
avg_stake = contract.functions.average_stake().call()
staker = random.choice([a for a in w3.eth.accounts])
amount = avg_stake+random.randint(1, 100)-contract.functions.staked(staker).call()
if amount < 1:
continue
print("Staker", staker, "adding", amount, "tokens")
txn_hash = contract.functions.stake().transact({'from':staker, 'value':amount})
w3.eth.waitForTransactionReceipt(txn_hash) # Wait for finished execution
except TransactionFailed as e:
print("Failure! Stake dump:")
break
finally:
print_accounts()
avg_stake = contract.functions.average_stake().call()
print("Average Stake:", avg_stake)
LogoutStarted: event({validator: address})
MAX_STAKERS: constant(uint256) = 5
LOWEST_STAKE: constant(uint256(wei)) = 1000
staked: public(map(address, uint256(wei)))
num_staked: public(uint256)
average_stake: public(uint256(wei))
@public
def __init__():
# Seed the CMA algorithm with the lowest stake
self.average_stake = LOWEST_STAKE
@public
@payable
def stake():
"""
Stake on getting a slot in the validator pool
Must stake at least enough to put you at or above average
"""
currently_staked: bool = (self.staked[msg.sender] > 0)
self.staked[msg.sender] += msg.value
# Allows current stakers to add more
assert self.staked[msg.sender] >= self.average_stake
if not currently_staked:
# CMA_n+1 = CMA_n + (x_n+1 - CMA_n) / (n + 1)
self.average_stake += (msg.value - self.average_stake) / (self.num_staked + 1)
self.num_staked += 1 # Increase number of samples
else:
self.average_stake += msg.value / (self.num_staked)
@public
def logout():
assert self.num_staked > MAX_STAKERS
assert self.staked[msg.sender] > 0
_amount: uint256(wei) = self.staked[msg.sender]
self.staked[msg.sender] = 0
# Inverse of stake formula
# CMA_n-1 = (n * CMA_n - x_n) / (n - 1)
self.average_stake = (self.num_staked * self.average_stake - _amount) / (self.num_staked - 1)
self.num_staked -= 1 # Decrease number of samples
send(msg.sender, _amount)
web3[tester]==5.0.0a6
vyper==0.1.0b8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment