File size: 4,230 Bytes
963d10b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import os
import json
from web3 import Web3
from dotenv import load_dotenv

load_dotenv()

RPC_URL = os.environ.get("PURECHAIN_RPC_URL", "http://3.34.161.207:8545")
PRIVATE_KEY = os.environ.get("PURECHAIN_PRIVATE_KEY")
ADDR_OLD = os.environ.get("PURECHAIN_CONTRACT_ADDRESS_PUREBI_JUN11")
ADDR_NEW = os.environ.get("PURECHAIN_CONTRACT_ADDRESS")

def main():
    if not PRIVATE_KEY or not ADDR_OLD or not ADDR_NEW:
        print("❌ Missing environment variables. Check .env")
        return

    w3 = Web3(Web3.HTTPProvider(RPC_URL))
    if not w3.is_connected():
        print("❌ Web3 not connected.")
        return

    admin_account = w3.eth.account.from_key(PRIVATE_KEY)
    print(f"Admin Account: {admin_account.address}")
    
    # ABI for just the functions we need
    abi_minimal = json.loads('''[
        {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"address","name":"contributor","type":"address"},{"indexed":false,"internalType":"string","name":"phrase","type":"string"},{"indexed":false,"internalType":"string","name":"dialect","type":"string"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"EntryProposed","type":"event"},
        {"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"}],"name":"EntryVerified","type":"event"},
        {"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"userXP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},
        {"inputs":[{"internalType":"address[]","name":"_users","type":"address[]"},{"internalType":"string[]","name":"_dialects","type":"string[]"},{"internalType":"uint256[]","name":"_xps","type":"uint256[]"}],"name":"batchMigrateXP","outputs":[],"stateMutability":"nonpayable","type":"function"}
    ]''')
    
    contract_old = w3.eth.contract(address=w3.to_checksum_address(ADDR_OLD), abi=abi_minimal)
    contract_new = w3.eth.contract(address=w3.to_checksum_address(ADDR_NEW), abi=abi_minimal)

    # We need to discover users and dialects by parsing EntryProposed events
    print("🔍 Discovering users from old contract events...")
    try:
        events = contract_old.events.EntryProposed.get_logs(fromBlock=0, toBlock='latest')
    except Exception as e:
        print(f"Failed to fetch logs: {e}")
        return

    user_dialects = set()
    for ev in events:
        user = ev.args.contributor
        dialect = ev.args.dialect
        user_dialects.add((user, dialect))
        
    if not user_dialects:
        print("No users found.")
        return
        
    print(f"Found {len(user_dialects)} User/Dialect pairs. Checking XP balances...")
    
    users = []
    dialects = []
    xps = []
    
    for (user, dialect) in user_dialects:
        old_xp = contract_old.functions.userXP(user, dialect).call()
        if old_xp > 0:
            # Check if it was already migrated
            new_xp = contract_new.functions.userXP(user, dialect).call()
            if new_xp < old_xp:
                diff = old_xp - new_xp
                users.append(user)
                dialects.append(dialect)
                xps.append(diff)
                print(f"💡 Found {diff} unmigrated XP for {user} in {dialect}")
                
    if not users:
        print("✅ No new XP needs migration.")
        return
        
    print(f"🚀 Migrating {len(users)} records...")
    
    nonce = w3.eth.get_transaction_count(admin_account.address)
    tx = contract_new.functions.batchMigrateXP(users, dialects, xps).build_transaction({
        'chainId': w3.eth.chain_id,
        'gas': 3000000,
        'gasPrice': w3.eth.gas_price,
        'nonce': nonce,
    })
    
    signed_tx = w3.eth.account.sign_transaction(tx, PRIVATE_KEY)
    tx_hash = w3.eth.send_raw_transaction(signed_tx.raw_transaction)
    
    print(f"⏳ Migration TX sent! Hash: {tx_hash.hex()}")
    receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
    print(f"🎉 Migration complete in block {receipt.blockNumber}!")

if __name__ == "__main__":
    main()