Closed herbig closed 1 year ago
For the internal testing this week, this script should:
We have two token voting DAOs:
fractalfalcons.eth
sarcosquids.eth
And moneydao.eth
has 1,000,000 of each DAO's tokens, to transfer out to the account running the script.
The list of participants will be:
0x4Fdd2d00df223d085C9EC5116dDbBefDf23ef5cC 0x7ce718Fb6bf6178AD63E1111F80088B5CE926219 0xC6eFD4C3ac07b359483CfC76b8953745123c4458 0x34B4d26C5f813713678355932ff909eF7c50006D 0x7482a3159E94a9162d910c99Dc50C8eAF55e7997 0xfcf7a2794D066110162ADdcE3085dfd6221D4ddD 0x2884b7Bf17Ca966bB2e4099bf384734a48885Df0 0x7930DdA80157Fcc47ba9c3836398c82d89C16416 0xC8375cC2C5fA34eba6aC386fECCaBaE1E51158c8 0x026820C8F89B0Ea06a28fe67D690EEb9caF8329D 0x629750317d320B8bB4d48D345A6d699Cc855c4a6 0x8E2695650D09FD940516d6e050D0Ba87d8deF032 0x099B6173699Cb08aA2c52228845c79e4D2f9775a 0xacb5e9D5470dd45F0B72A65eEf596e88E9fDD38e
Not sure we need to add this to the repo, so I'll post what I used here. It could definitely use some help, as it died on me a few times and I needed to restart it, just with the 15 internal folks we have.
@tbwebb22 @mudrila I have some questions on how to optimize gas to make sure these transactions go through...
To run, you'll need to install Python web3:
pip3 install web3
#!/usr/bin/env python3
from eth_account import Account
from web3 import Web3
import secrets
import random
ERC20_VOTES_ABI = '[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegator","type":"address"},{"indexed":true,"internalType":"address","name":"fromDelegate","type":"address"},{"indexed":true,"internalType":"address","name":"toDelegate","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegate","type":"address"},{"indexed":false,"internalType":"uint256","name":"previousBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newBalance","type":"uint256"}],"name":"DelegateVotesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"Snapshot","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"snapshotId","type":"uint256"}],"name":"balanceOfAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"captureSnapShot","outputs":[{"internalType":"uint256","name":"snapId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint32","name":"pos","type":"uint32"}],"name":"checkpoints","outputs":[{"components":[{"internalType":"uint32","name":"fromBlock","type":"uint32"},{"internalType":"uint224","name":"votes","type":"uint224"}],"internalType":"struct ERC20VotesUpgradeable.Checkpoint","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"delegatee","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"expiry","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateBySig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"delegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"blockNumber","type":"uint256"}],"name":"getPastVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getVotes","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"numCheckpoints","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"initializeParams","type":"bytes"}],"name":"setUp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"snapshotId","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]'
DELEGATION_GAS = 0.00001
def main():
infuraAPIKey = input('Enter your Infura API key: ')
web3 = Web3(Web3.HTTPProvider(f'https://goerli.infura.io/v3/{infuraAPIKey}'))
executionPrivateKey = input('Enter execution wallet private key: ')
executionAccount = Account.from_key(executionPrivateKey)
dao1TokenAddress = input('Enter DAO 1 token address: ')
dao2TokenAddress = input('Enter DAO 2 token address: ')
participantAddresses = input('Enter comma delimited participant addresses (no spaces): ').split(',')
random.shuffle(participantAddresses)
participantVotingPower = input('Enter participant DAO voting power: ')
participantEth = input('Enter Goerli Eth for each participant: ')
for index, participantAddress in enumerate(participantAddresses):
print('--- Starting ' + participantAddress + ' ---')
# create a new account we'll delegate to the participant from
account = create_account()
# send a bit of Eth to this new account, to pay for delegation
send_eth(web3, executionAccount.address, executionPrivateKey, account[0].address, DELEGATION_GAS)
# send the voting tokens to our account
erc20Contract = web3.eth.contract((dao1TokenAddress if index%2==1 else dao2TokenAddress), abi=ERC20_VOTES_ABI)
send_erc20(web3, erc20Contract, executionAccount.address, executionPrivateKey, account[0].address, participantVotingPower)
# delegate to the participant from our account
delegate_erc20(web3, erc20Contract, account[0].address, account[1], participantAddress)
# send the participant some Eth to play the game with
send_eth(web3, executionAccount.address, executionPrivateKey, participantAddress, participantEth)
print('--- Finished ' + participantAddress + ' ---')
def create_account():
privateKey = '0x' + secrets.token_hex(32)
account = Account.from_key(privateKey)
print('Generated wallet address: ' + account.address)
return account, privateKey
def send_eth(web3, fromAddress, fromKey, to, decimalAmount):
signed_tx = web3.eth.account.sign_transaction({
'nonce': web3.eth.getTransactionCount(fromAddress),
'to': to,
'value': web3.toWei(decimalAmount, 'ether'),
'gas': 2000000,
'gasPrice': web3.eth.gasPrice,
}, fromKey)
# send transaction
tx_hash = web3.toHex(web3.eth.sendRawTransaction(signed_tx.rawTransaction))
print('Sending Eth: ' + etherScan(tx_hash))
web3.eth.wait_for_transaction_receipt(tx_hash)
print('Sending complete!')
def send_erc20(web3, contract, fromAddress, fromKey, to, amount):
signed_tx = web3.eth.account.sign_transaction({
'nonce': web3.eth.getTransactionCount(fromAddress),
'to': contract.address,
'from': fromAddress,
'gasPrice': web3.eth.gasPrice,
'gas': 20000000,
'value': '0x0',
'data': contract.encodeABI('transfer', args=(to, web3.toWei(amount, 'ether'))), # assuming 18 decimals, like Eth here
}, fromKey)
tx_hash = web3.toHex(web3.eth.sendRawTransaction(signed_tx.rawTransaction))
print('Sending ERC20: ' + etherScan(tx_hash))
web3.eth.wait_for_transaction_receipt(tx_hash)
print('Sending complete!')
def delegate_erc20(web3, contract, fromAddress, fromKey, to):
signed_tx = web3.eth.account.sign_transaction(contract.functions.delegate(to).buildTransaction(
{
'nonce': web3.eth.getTransactionCount(fromAddress),
'from': fromAddress,
'gasPrice': web3.eth.gasPrice,
'gas': 20000000,
'value': '0x0',
}
), fromKey)
tx_hash = web3.toHex(web3.eth.sendRawTransaction(signed_tx.rawTransaction))
print('Delegating ERC20: ' + etherScan(tx_hash))
web3.eth.wait_for_transaction_receipt(tx_hash)
print('Delegation complete!')
def etherScan(tx_hash):
return f'https://goerli.etherscan.io/tx/{tx_hash}'
main()
This is in the Connect 4 frontend repo:
https://github.com/decent-dao/fractal-workshop-connect-four/blob/main/dao_compete_setup.py
For the Eth Denver connect 4 setup, we will have two competing token voting DAOs, with members each having equal voting power.
To set this up, we'll need to have a script that, given a list of participant Ethereum wallet participant addresses (up to 250) it:
The end result is that we have each participant delegated with token voting power to play the game (vote on DAO proposals), without having to do any setup themselves.