TOOL - Python Decoder
import base64
# WormholeMsg:
# Header
# byte version (VAA Version)
# u32 guardian_set_index (Indicates which guardian set is signing)
# u8 len_signatures (Number of signatures stored)
#
# [][66]byte signatures (Collection of ecdsa signatures)
#
# Body:
# u32 timestamp
# u32 nonce
# u16 emitter_chain
# [32]byte emitter_address
# u64 sequence
# u8 consistency_level
# []byte payload
#
# Payload:
# u8 action
# [u8] payload
#
# Body:
# u256 amount
# [u8; 32] token_address
# u16 token_chain
# [u8; 40] recipient
# u16 recipient_chain
# u256 fee
## these are in bytes
VERSION_POS = 0
HEADER_LEN = 6
LEN_SIGNATURE_POS = 5
SIGNATURE_LEN = 66
EMITTER_CHAIN_POS = 8
PAYLOAD_POS = 51
MSG_PAYLOAD_POS = 1
AMOUNT_POS = 0
AMOUNT_SIZE = 32
TOKEN_ADDR_POS = 32
TOKEN_ADDR_SIZE = 40
FEE_POS = 100
RECIPIENT_POS = 66
RECIPIENT_SIZE = 32
TIMESTAMP_SIZE = 4
NONCE_SIZE = 4
EMITTER_CHAIN_SIZE = 2
EMITTER_ADDRESS_SIZE = 32
SEQUENCE_SIZE = 8
def parse_vaa(vaa: str, address_matcher = None):
# Load as bytearray
#print (base64.b64decode(vaa).hex())
vaa_arr = list(bytearray(base64.b64decode(vaa)))
len_signatures = vaa_arr[LEN_SIGNATURE_POS]
body_offset = HEADER_LEN + (len_signatures * SIGNATURE_LEN)
body_arr = vaa_arr[body_offset:]
body_print = ''.join(list(map(lambda x: hex(x)[2:], body_arr)))
#print((body_print))
emitter_chain = body_arr[EMITTER_CHAIN_POS:EMITTER_CHAIN_POS+2]
payload = body_arr[PAYLOAD_POS:]
# body payload - stuffs
body_timestamp = body_arr[0:TIMESTAMP_SIZE]
body_nonce = body_arr[TIMESTAMP_SIZE : TIMESTAMP_SIZE+NONCE_SIZE]
body_emitter_chain = body_arr[TIMESTAMP_SIZE+NONCE_SIZE : TIMESTAMP_SIZE+NONCE_SIZE+EMITTER_CHAIN_SIZE]
body_emitter_address = body_arr[TIMESTAMP_SIZE+NONCE_SIZE+EMITTER_CHAIN_SIZE : TIMESTAMP_SIZE+NONCE_SIZE+EMITTER_CHAIN_SIZE+EMITTER_ADDRESS_SIZE]
body_sequence = body_arr[TIMESTAMP_SIZE+NONCE_SIZE+EMITTER_CHAIN_SIZE+EMITTER_ADDRESS_SIZE : TIMESTAMP_SIZE+NONCE_SIZE+EMITTER_CHAIN_SIZE+EMITTER_ADDRESS_SIZE+SEQUENCE_SIZE]
# payload - stuffs
msg_payload = payload[MSG_PAYLOAD_POS:]
payload_print = ''.join(list(map(lambda x: hex(x)[2:], msg_payload)))
#print((payload_print))
amount = msg_payload[AMOUNT_POS :AMOUNT_POS + AMOUNT_SIZE]
amount = int.from_bytes(bytes(amount), 'big')
token_addr = msg_payload[TOKEN_ADDR_POS:TOKEN_ADDR_POS + TOKEN_ADDR_SIZE]
recipient = msg_payload[RECIPIENT_POS:RECIPIENT_POS + RECIPIENT_SIZE]
recipient = base64.b64encode(bytes(recipient))
token_name = ''.join(list(map(lambda x: chr(x), token_addr)))
# Only care for uusd and uluna, since we don't have price data for every token
token_name = token_name[-5:].lstrip('\x00')
if not token_name in ['uluna', 'uusd']:
token_name = base64.b64encode(bytes(token_addr)).decode()
if not address_matcher is None:
info = address_matcher(token_name)
token_name = info['symbol']
amount /= (10 ** info['decimals'])
else:
_t = {'uluna': 'LUNA', 'uusd': 'UST'}
token_name = _t[token_name]
amount /= 1e6
fee = msg_payload[FEE_POS:FEE_POS+32]
fee = int.from_bytes(bytes(fee), 'big')
return {
'BODY_TIMESTAMP' : int(''.join(list(map(lambda x: hex(x)[2:], body_timestamp))), 16),
'BODY_NONCE' : int(''.join(list(map(lambda x: hex(x)[2:], body_nonce))), 16),
'BODY_EMITTER_CHAIN' : int(''.join(list(map(lambda x: hex(x)[2:], body_emitter_chain))), 16),
'BODY_EMITTER_ADDRESS' : '0x' + (''.join(list(map(lambda x: hex(x)[2:], body_emitter_address)))),
'BODY_SEQUENCE' : int(''.join(list(map(lambda x: hex(x)[2:], body_sequence))), 16),
'FROM_CHAIN': bytearr_to_int(emitter_chain[-1::-1]),
'RECIPIENT': '0x' + base64_to_hex(recipient.decode()),
'AMOUNT': amount,
'CURRENCY': base64_to_hex(token_name),
'FEE': fee
}
def base64_to_hex(strr):
return base64.b64decode(strr).hex()
def bytearr_to_int(arr):
result = 0
for i, v in enumerate(arr):
result += v * (2**i)
return result
print(parse_vaa("AQAAAAENAEra1O4PWf+lCMPdVT2TYAlpnAV2Dj8nifMwPscQzl+nYIQ8cGMN5vjPLG+qTm3BqpOvAUW97w9E+SXdKqrnGi8BArpfKRD5hWvp189gixEn69I6U34PUprlv7nh7DUOBciiErfh3QLFpHGq8ssyb1Edl42Nsx/evZOCqOdXK0IKfPwBA9c9Ca3rW85YJxmstZ04KWw1CmiiO9/pLlmPvbNz7b8ZBjCNaSegivnCKOWeyc0TQKtpRDl5dYqclqX5oIK0/RsBBCXsyleTp6J6buhFKOiMr5bEZe8+wgihajG/vr0YwQLTF/RDjL8aG70gEcwEygFTftYzzIPZ4m0cXQ28+uauTaQABab4dhN6IEdivgYV3+26jdunU7l9AjUt4/XWxTCmeEKMaHDNm9cVm0Nmk4wns9+CYIYuScSjq+ct000RysJrZH4BBp1jNX7LZAWaFazQI77TnEFxqvXAJ6IDiveJPfV52+V9Y4PKrFtrBeNZvtawvVn9REEfjzCxdVv7WRHCq3xYMI0BBxlE7sYJQed9ljeQXFcPgdDqkolPlpRqHgxXJVO01Zv1DwReKpQqHgdQdkhgbpjl5zUN8+AG6NwTdUGs9tJNLsIACQ0rCpbulX7p1ErMDDOOi0sHyyg0AbTV86KZhtgir7tGZwA2PukQwmpa37Dd8Xf2R7Uy5RV+/FFIN1i1Kp9fZPgACvWKi3JAv2i6NJo3cu7etJwwuXlujatb+yzycJRcXNzOByrfBuRJDUj++7cV2mwKO2Ajvy0/nSCgxbpobSgCbLABC+qZ/z/SF3wEms2fOMjv3e7hFZczGyi6QRMBiVLcwS48C5zeKcyC1yokOBOjpEjOeFI+uNy90/vq0jrB/gjLigUADYBLCA8oET63Ox2fvY2mWNPu58Pjw23vbU09CejT80PsA1QTmfy2d/N5gdmEIbAY4jZ11nJn5Z9gM/GUdcTW/68BDze3EmYGdpc0PGipOMxPEaEQ8xuowrDy6tc5s3awskpvfdTReH5JrMyyUA3JlZ7wtrAPuz4rHxMI9Y/5g2vR9ZEAEPSooatTwT1FtnS86DpMdY6x0zLg0P9Nod1jkLCNUG3SA3qfTicZmc8oGGwUNLfOlslRQhTYIwM7F+Rs4ZmAXx4AYeKIvgABIS8AAexzcpldXMhzI5f7CtNcASHg6qkNJvgopTTKtUORs6T1AAAAAAAAw0wgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmJaAxvp6877brTo9ZfNqq8l0MbG75MLS9uDkfKYCA0UvXWEAAQAAAAAAAAAAAAAAAFQvfd6bI2a9vqPzc4itfwciYQE2AAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="))
Last updated