def _deserialize_channel_withdraw( raw_event: Dict[str, Any], identity_address_map: Dict[ChecksumAddress, ChecksumAddress], ) -> ChannelWithdraw: """Deserialize a channel withdraw event. Only for Tom pool. It may raise KeyError. TODO: use bidict or already pass the inverse identity_address_map. """ inverse_identity_address_map = { address: identity for identity, address in identity_address_map.items() } address = to_checksum_address(raw_event['user']) identity_address = inverse_identity_address_map[address] amount = FVal(raw_event['amount']) / ADX_AMOUNT_MANTISSA return ChannelWithdraw( tx_hash=HexStr(raw_event['id'].split(':')[0]), address=address, identity_address=identity_address, timestamp=Timestamp(raw_event['timestamp']), value=Balance(amount=amount), channel_id=HexStr(raw_event['channelId']), pool_id=TOM_POOL_ID, )
def _get_user_identity(address: ChecksumAddress) -> ChecksumEthAddress: """Given an address (signer) returns its protocol user identity. """ return generate_address_via_create2( address=HexAddress(HexStr(IDENTITY_FACTORY_ADDR)), salt=HexStr(CREATE2_SALT), init_code=HexStr(IDENTITY_PROXY_INIT_CODE.format(signer_address=address)), )
def test_generate_address_via_create2( address, salt, init_code, expected_contract_address, ): """Test the CREATE2 opcode Python implementation. """ contract_address = generate_address_via_create2( address=HexAddress(address), salt=HexStr(salt), init_code=HexStr(init_code), ) assert contract_address == to_checksum_address(expected_contract_address)
def _deserialize_bond( self, raw_event: Dict[str, Any], identity_address_map: Dict[ChecksumAddress, ChecksumAddress], ) -> Bond: """Deserialize a bond event. It may raise KeyError. """ event_core_data = self._deserialize_event_core_data( raw_event=raw_event, identity_address_map=identity_address_map, ) amount_int = int(raw_event['amount']) amount = FVal(raw_event['amount']) / ADX_AMOUNT_MANTISSA pool_id = HexStr(raw_event['poolId']) nonce = int(raw_event['nonce']) bond_id = self._get_bond_id( identity_address=event_core_data.identity_address, amount=amount_int, pool_id=pool_id, nonce=nonce, ) return Bond( tx_hash=event_core_data.tx_hash, address=event_core_data.address, identity_address=event_core_data.identity_address, timestamp=event_core_data.timestamp, bond_id=bond_id, value=Balance(amount=amount), pool_id=pool_id, nonce=nonce, slashed_at=Timestamp(int(raw_event['slashedAtStart'])), )
def _get_bond_id( identity_address: ChecksumAddress, amount: int, pool_id: HexStr, nonce: int, ) -> HexStr: """Given a LogBond event data, return its `bondId`. """ arg_types = ['address', 'address', 'uint', 'bytes32', 'uint'] args = [STAKING_ADDR, identity_address, amount, pool_id, nonce] return HexStr(Web3.keccak(Web3().codec.encode_abi(arg_types, args)).hex())
def _deserialize_event_core_data( raw_event: Dict[str, Any], identity_address_map: Dict[ChecksumAddress, ChecksumAddress], ) -> EventCoreData: """Deserialize the common event attributes. It may raise KeyError. Id for unbond and unbond request events is 'tx_hash:address'. """ identity_address = to_checksum_address(raw_event['owner']) return EventCoreData( tx_hash=HexStr(raw_event['id'].split(':')[0]), address=identity_address_map[identity_address], identity_address=identity_address, timestamp=Timestamp(raw_event['timestamp']), )
def _deserialize_unbond( self, raw_event: Dict[str, Any], identity_address_map: Dict[ChecksumAddress, ChecksumAddress], ) -> Unbond: """Deserialize an unbond event. It may raise KeyError. """ event_core_data = self._deserialize_event_core_data( raw_event=raw_event, identity_address_map=identity_address_map, ) return Unbond( tx_hash=event_core_data.tx_hash, address=event_core_data.address, identity_address=event_core_data.identity_address, timestamp=event_core_data.timestamp, bond_id=HexStr(raw_event['bondId']), value=Balance(), )
class UnbondRequest: tx_hash: HexStr # from unbond.id address: ChecksumAddress identity_address: ChecksumAddress timestamp: Timestamp bond_id: HexStr unlock_at: Timestamp # from unbondRequest.willUnlock value: Balance # from bond.amount pool_id: HexStr = HexStr('') # from bond.pool_id def serialize(self) -> Dict[str, Any]: return { 'tx_hash': self.tx_hash, 'identity_address': self.identity_address, 'timestamp': self.timestamp, 'bond_id': self.bond_id, 'pool_id': self.pool_id, 'pool_name': POOL_ID_POOL_NAME.get(self.pool_id, None), 'value': self.value.serialize(), 'event_type': str(AdexEventType.UNBOND_REQUEST), } def to_db_tuple(self) -> AdexEventDBTuple: return ( str(self.tx_hash), str(self.address), str(self.identity_address), int(self.timestamp), str(AdexEventType.UNBOND_REQUEST), str(self.pool_id), str(self.value.amount), str(self.value.usd_value), str(self.bond_id), None, # nonce None, # slashed_at int(self.unlock_at), None, # channel_id None, # token )
def test_get_bond_id(): """Test our Python port of AdEX `getBondId()` calculates the expected bond id using the LogBond event data. Bond tx (origin of `owner`, `amount`, `pool_id` and `nonce`): 0x7944c10032e2a079d3f03ad57a90a93bde468b0baba84121be79790162215855 Unbond tx (claiming and re-staking), its log index 283 contains the expected bond id: 0xc59d65bc6c18e11a3650e8d7ec41a11f58016bbf843376c7f4cb0833402399f1 AdEX `getBondId()`: https://github.com/AdExNetwork/adex-staking/blob/master/src/helpers/bonds.js#L5 """ expected_bond_id = '0xf1570226030766ce222ffa240231bbfc2a8de995516e63927c672b1b46c7f2c6' bond_id = Adex._get_bond_id( identity_address=TEST_ADDR_USER_IDENTITY, amount=10661562521452745365522, pool_id=HexStr(TOM_POOL_ID), nonce=1596569185, ) assert bond_id == expected_bond_id
def deserialize_ethereum_address(symbol: str) -> ChecksumEthAddress: return ChecksumEthAddress(HexAddress(HexStr(symbol)))
def deserialize_adex_event_from_db( event_tuple: AdexEventDBTuple, ) -> Union[Bond, Unbond, UnbondRequest, ChannelWithdraw]: """Turns a tuple read from DB into an appropriate AdEx event. May raise a DeserializationError if something is wrong with the DB data. Event_tuple index - Schema columns ---------------------------------- 0 - tx_hash 1 - address 2 - identity_address 3 - timestamp 4 - type 5 - pool_id 6 - amount 7 - usd_value 8 - bond_id 9 - nonce 10 - slashed_at 11 - unlock_at 12 - channel_id 13 - token """ db_event_type = event_tuple[4] if db_event_type not in {str(event_type) for event_type in AdexEventType}: raise DeserializationError( f'Failed to deserialize event type. Unknown event: {db_event_type}.', ) tx_hash = HexStr(event_tuple[0]) address = deserialize_ethereum_address(event_tuple[1]) identity_address = deserialize_ethereum_address(event_tuple[2]) timestamp = deserialize_timestamp(event_tuple[3]) pool_id = HexStr(event_tuple[5]) amount = deserialize_asset_amount(event_tuple[6]) usd_value = deserialize_asset_amount(event_tuple[7]) value = Balance(amount=amount, usd_value=usd_value) if db_event_type == str(AdexEventType.BOND): if any(event_tuple[idx] is None for idx in (8, 9, 10)): raise DeserializationError( f'Failed to deserialize bond event. Unexpected data: {event_tuple}.', ) return Bond( tx_hash=tx_hash, address=address, identity_address=identity_address, timestamp=timestamp, pool_id=pool_id, value=value, bond_id=HexStr(cast(str, event_tuple[8])), nonce=cast(int, event_tuple[9]), slashed_at=Timestamp(cast(int, event_tuple[10])), ) if db_event_type == str(AdexEventType.UNBOND): if any(event_tuple[idx] is None for idx in (8, )): raise DeserializationError( f'Failed to deserialize bond event. Unexpected data: {event_tuple}.', ) return Unbond( tx_hash=tx_hash, address=address, identity_address=identity_address, timestamp=timestamp, pool_id=pool_id, value=value, bond_id=HexStr(cast(str, event_tuple[8])), ) if db_event_type == str(AdexEventType.UNBOND_REQUEST): if any(event_tuple[idx] is None for idx in (8, 11)): raise DeserializationError( f'Failed to deserialize unbond request event. Unexpected data: {event_tuple}.', ) return UnbondRequest( tx_hash=tx_hash, address=address, identity_address=identity_address, timestamp=timestamp, pool_id=pool_id, value=value, bond_id=HexStr(cast(str, event_tuple[8])), unlock_at=Timestamp(cast(int, event_tuple[11])), ) if db_event_type == str(AdexEventType.CHANNEL_WITHDRAW): # NB: `token` (event_tuple[13]) could be None, do not check below. if any(event_tuple[idx] is None for idx in (12, )): raise DeserializationError( f'Failed to deserialize unbond request event. Unexpected data: {event_tuple}.', ) token = None if event_tuple[13] is not None: try: token = EthereumToken(event_tuple[13]) except (UnknownAsset, UnsupportedAsset) as e: asset_tag = 'Unknown' if isinstance( e, UnknownAsset) else 'Unsupported' raise DeserializationError( f'{asset_tag} {e.asset_name} found while processing adex event. ' f'Unexpected data: {event_tuple}', ) from e return ChannelWithdraw( tx_hash=tx_hash, address=address, identity_address=identity_address, timestamp=timestamp, value=value, pool_id=pool_id, channel_id=HexStr(cast(str, event_tuple[12])), token=token, ) raise DeserializationError( f'Failed to deserialize event. Unexpected event type case: {db_event_type}.', )
from dataclasses import asdict from eth_utils.typing import HexAddress, HexStr from rotkehlchen.assets.asset import EthereumToken from rotkehlchen.assets.unknown_asset import UnknownEthereumToken from rotkehlchen.typing import ChecksumEthAddress SHUF_ETHEREUM_ADDRESS = ChecksumEthAddress( HexAddress(HexStr('0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E')), ) SHUF_SYMBOL = 'SHUF' SHUF_NAME = 'Shuffle.Monster V3' SHUF_DECIMALS = 18 # Test initialization def test_init_default(): ue_token = UnknownEthereumToken( ethereum_address=SHUF_ETHEREUM_ADDRESS, symbol=SHUF_SYMBOL, ) assert ue_token.ethereum_address == SHUF_ETHEREUM_ADDRESS assert ue_token.symbol == SHUF_SYMBOL assert ue_token.name is None assert ue_token.decimals is None # Test operators def test_eq(): ue_token_1 = UnknownEthereumToken( ethereum_address=SHUF_ETHEREUM_ADDRESS,
from dataclasses import dataclass from enum import Enum from typing import Any, Callable, Dict, List, NamedTuple, Optional, Tuple, Union from eth_typing.evm import ChecksumAddress from eth_utils.typing import HexStr from rotkehlchen.accounting.structures import Balance from rotkehlchen.assets.asset import EthereumToken from rotkehlchen.fval import FVal from rotkehlchen.typing import Timestamp # Pools data TOM_POOL_ID = HexStr( '0x2ce0c96383fb229d9776f33846e983a956a7d95844fac57b180ed0071d93bb28') POOL_ID_POOL_NAME = { TOM_POOL_ID: 'Tom', } AdexEventDBTuple = ( Tuple[str, # tx_hash str, # address str, # identity_address int, # timestamp str, # type str, # pool_id str, # amount str, # usd_value Optional[str], # bond_id Optional[int], # nonce Optional[int], # slashed_at