decentdao / decent-contracts

Your Safe, Superpowered
https://app.fractalframework.xyz
MIT License
3 stars 3 forks source link

Eth Denver Connect 4 setup Script #24

Closed herbig closed 1 year ago

herbig commented 1 year ago

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.

herbig commented 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

herbig commented 1 year ago

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()
herbig commented 1 year ago

This is in the Connect 4 frontend repo:

https://github.com/decent-dao/fractal-workshop-connect-four/blob/main/dao_compete_setup.py