import uuid, time from eth_account import Account from eth_account.messages import encode_typed_data from web3 import Web3 class EIP712Signer: def __init__(self, private_key: str, contract_address: str, chain_id: int): self.account = Account.from_key(private_key) self.contract_address = Web3.to_checksum_address(contract_address) self.chain_id = chain_id def build_typed_data(self, gateway_uuid_bytes: bytes, merkle_root_bytes: bytes, object_count: int, nonce: int, timestamp: int) -> dict: return { "types": { "EIP712Domain": [ {"name": "name", "type": "string"}, {"name": "version", "type": "string"}, {"name": "chainId", "type": "uint256"}, {"name": "verifyingContract", "type": "address"} ], "AuditReceipt": [ {"name": "gatewayUuid", "type": "bytes16"}, {"name": "merkleRoot", "type": "bytes32"}, {"name": "objectCount", "type": "uint256"}, {"name": "timestamp", "type": "uint256"}, {"name": "nonce", "type": "uint256"} ] }, "primaryType": "AuditReceipt", "domain": {"name": "AuditReceiptLedger", "version": "1", "chainId": self.chain_id, "verifyingContract": self.contract_address}, "message": {"gatewayUuid": gateway_uuid_bytes, "merkleRoot": merkle_root_bytes, "objectCount": object_count, "timestamp": timestamp, "nonce": nonce} } def sign_audit_receipt(self, gateway_uuid_str: str, merkle_root_hex: str, object_count: int, nonce: int) -> dict: g_bytes = uuid.UUID(gateway_uuid_str).bytes m_bytes = bytes.fromhex(merkle_root_hex.replace("0x", "")) ts = int(time.time()) typed_data = self.build_typed_data(g_bytes, m_bytes, object_count, nonce, ts) sig = self.account.sign_message(encode_typed_data(full_message=typed_data)).signature.hex() return { "receipt": {"gatewayUuid": "0x" + g_bytes.hex(), "merkleRoot": "0x" + m_bytes.hex(), "objectCount": object_count, "timestamp": ts, "nonce": nonce}, "signature": sig, "signer": self.account.address }