Skip to content

Instantly share code, notes, and snippets.

@banteg
Created December 21, 2018 08:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save banteg/abfdc98cd66aad0d86e00a95b74dc5ce to your computer and use it in GitHub Desktop.
Save banteg/abfdc98cd66aad0d86e00a95b74dc5ce to your computer and use it in GitHub Desktop.
import json
from collections import Counter, defaultdict
from dataclasses import dataclass, field
from decimal import Decimal
from itertools import chain
import requests
from eth_abi import encode_single
from eth_utils import function_signature_to_4byte_selector, decode_hex, encode_hex
from itertools import count
from web3.auto import w3
@dataclass
class Voter:
yays: list = field(default_factory=list)
weight: Decimal = Decimal()
def get_contract(address):
resp = requests.get(
'http://api.etherscan.io/api',
params=dict(module='contract', action='getabi', address=address, format='raw')
)
return w3.eth.contract(address, abi=resp.json())
def decode_spell(address):
spell = get_contract(address)
whom = spell.functions.whom().call()
mom = get_contract(whom)
return mom.decode_function_input(spell.functions.data().call())
def get_slates(chief):
etches = chief.events.Etch().createFilter(fromBlock=4749331).get_all_entries()
slates = {encode_hex(etch['args']['slate']) for etch in etches}
return slates
def slate_to_addresses(chief, slate):
addresses = []
for i in count():
try:
addresses.append(chief.functions.slates(slate, i).call())
except ValueError:
break
return addresses
def func_topic(func):
return encode_hex(encode_single('bytes32', function_signature_to_4byte_selector(func)))
def get_notes(chief):
# get yays and slate votes
return w3.eth.getLogs({
'address': chief.address,
'topics': [
[func_topic('vote(address[])'), func_topic('vote(bytes32)')]
],
'fromBlock': 4749331,
})
def notes_to_voters(chief, notes, slates_yays):
voters = defaultdict(Voter)
for note in notes:
data = decode_hex(note['data'])[96:]
try:
func, args = chief.decode_function_input(data)
except:
continue
sender = w3.toChecksumAddress(note['topics'][1][12:])
v = voters[sender]
v.yays = slates_yays[encode_hex(args['slate'])] if 'slate' in args else args['yays']
for v in voters:
voters[v].weight = w3.fromWei(chief.functions.deposits(v).call(), 'ether')
return voters
def voters_to_results(voters):
proposals = Counter()
for addr in voters:
for yay in voters[addr].yays:
proposals[yay] += voters[addr].weight
return proposals.most_common()
def votes_for_proposal(proposal, voters):
votes = Counter()
for addr in voters:
if proposal in voters[addr].yays and voters[addr].weight > 0:
votes[addr] = voters[addr].weight
return votes.most_common()
def voters():
chief = get_contract('0x8E2a84D6adE1E7ffFEe039A35EF5F19F13057152')
slates = get_slates(chief)
slates_yays = {slate: slate_to_addresses(chief, slate) for slate in slates}
notes = get_notes(chief)
voters = notes_to_voters(chief, notes, slates_yays)
for proposal, votes in voters_to_results(voters):
print(proposal, votes)
try:
func, args = decode_spell(proposal)
print(func, args)
except:
print('unknown spell')
for voter, weight in votes_for_proposal(proposal, voters):
print(' ', voter, weight)
print()
if __name__ == '__main__':
voters()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment