Skip to content

Commit 4d84a1c

Browse files
authored
feat: implement username transaction types (#149)
1 parent 2ca1892 commit 4d84a1c

13 files changed

+1113
-3
lines changed

crypto/enums/abi_function.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
class AbiFunction(Enum):
44
VOTE = 'vote'
55
UNVOTE = 'unvote'
6+
USERNAME_REGISTRATION = 'registerUsername'
7+
USERNAME_RESIGNATION = 'resignUsername'
68
VALIDATOR_REGISTRATION = 'registerValidator'
79
VALIDATOR_RESIGNATION = 'resignValidator'

crypto/enums/contract_abi_type.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from enum import Enum
2+
3+
class ContractAbiType(Enum):
4+
CUSTOM = 'custom'
5+
CONSENSUS = 'consensus'
6+
USERNAMES = 'usernames'

crypto/enums/contract_addresses.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from enum import Enum
2+
3+
class ContractAddresses(Enum):
4+
CONSENSUS = '0x535B3D7A252fa034Ed71F0C53ec0C6F784cB64E1'
5+
MULTIPAYMENT = '0x83769BeEB7e5405ef0B7dc3C66C43E3a51A6d27f'
6+
USERNAMES = '0x2c1DE3b4Dbb4aDebEbB5dcECAe825bE2a9fc6eb6'

crypto/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ class ArkSerializerException(ArkCryptoException):
88

99
class ArkInvalidTransaction(ArkCryptoException):
1010
"""Raised when transaction is not valid"""
11+
12+
13+
class InvalidUsernameException(Exception):
14+
"""Raised when username is invalid"""
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import re
2+
from typing import Optional
3+
from crypto.enums.contract_addresses import ContractAddresses
4+
from crypto.exceptions import InvalidUsernameException
5+
from crypto.transactions.builder.base import AbstractTransactionBuilder
6+
from crypto.transactions.types.username_registration import UsernameRegistration
7+
8+
class UsernameRegistrationBuilder(AbstractTransactionBuilder):
9+
def __init__(self, data: Optional[dict] = None):
10+
super().__init__(data)
11+
12+
self.recipient_address(ContractAddresses.USERNAMES.value)
13+
14+
def username(self, username: str):
15+
self.is_valid_username(username)
16+
17+
self.transaction.data['username'] = username
18+
self.transaction.refresh_payload_data()
19+
20+
return self
21+
22+
def get_transaction_instance(self, data: dict):
23+
return UsernameRegistration(data)
24+
25+
@staticmethod
26+
def is_valid_username(username: str) -> bool:
27+
if len(username) < 1 or len(username) > 20:
28+
raise InvalidUsernameException(f'Username must be between 1 and 20 characters long. Got {len(username)} characters.')
29+
30+
if re.match('/[^a-z0-9_]/', username):
31+
raise InvalidUsernameException('Username can only contain lowercase letters, numbers and underscores.')
32+
33+
if re.match('/^_|_$/', username):
34+
raise InvalidUsernameException('Username cannot start or end with an underscore.')
35+
36+
if re.match('/__/', username):
37+
raise InvalidUsernameException('Username cannot contain consecutive underscores.')
38+
39+
return True
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from typing import Optional
2+
from crypto.enums.contract_addresses import ContractAddresses
3+
from crypto.transactions.builder.base import AbstractTransactionBuilder
4+
from crypto.transactions.types.username_resignation import UsernameResignation
5+
6+
class UsernameResignationBuilder(AbstractTransactionBuilder):
7+
def __init__(self, data: Optional[dict] = None):
8+
super().__init__(data)
9+
10+
self.recipient_address(ContractAddresses.USERNAMES.value)
11+
12+
def get_transaction_instance(self, data: dict):
13+
return UsernameResignation(data)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from crypto.enums.contract_abi_type import ContractAbiType
2+
from crypto.transactions.types.abstract_transaction import AbstractTransaction
3+
from crypto.utils.abi_encoder import AbiEncoder
4+
from crypto.enums.abi_function import AbiFunction
5+
6+
class UsernameRegistration(AbstractTransaction):
7+
def __init__(self, data: dict = None):
8+
data = data or {}
9+
payload = self.decode_payload(data)
10+
if payload:
11+
data['username'] = payload.get('args', [None])[0] if payload.get('args') else None
12+
13+
super().__init__(data)
14+
15+
def get_payload(self) -> str:
16+
if 'username' not in self.data:
17+
return ''
18+
19+
encoder = AbiEncoder(ContractAbiType.USERNAMES)
20+
21+
return encoder.encode_function_call(AbiFunction.USERNAME_REGISTRATION.value, [self.data['username']])
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from crypto.enums.contract_abi_type import ContractAbiType
2+
from crypto.transactions.types.abstract_transaction import AbstractTransaction
3+
from crypto.utils.abi_encoder import AbiEncoder
4+
from crypto.enums.abi_function import AbiFunction
5+
6+
class UsernameResignation(AbstractTransaction):
7+
def get_payload(self) -> str:
8+
encoder = AbiEncoder(ContractAbiType.USERNAMES)
9+
10+
return encoder.encode_function_call(AbiFunction.USERNAME_RESIGNATION.value)

0 commit comments

Comments
 (0)