Spaces:
Running
Running
| 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() | |