Skip to content

Commit 5ad403c

Browse files
authored
feat: integrate multipayments (#152)
1 parent 3df7f1e commit 5ad403c

17 files changed

Lines changed: 329 additions & 24 deletions

crypto/enums/abi_function.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
class AbiFunction(Enum):
44
VOTE = 'vote'
55
UNVOTE = 'unvote'
6+
MULTIPAYMENT = 'pay'
67
USERNAME_REGISTRATION = 'registerUsername'
78
USERNAME_RESIGNATION = 'resignUsername'
89
VALIDATOR_REGISTRATION = 'registerValidator'

crypto/enums/contract_abi_type.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
class ContractAbiType(Enum):
44
CUSTOM = 'custom'
55
CONSENSUS = 'consensus'
6+
MULTIPAYMENT = 'multipayment'
67
USERNAMES = 'usernames'

crypto/enums/contract_addresses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
class ContractAddresses(Enum):
44
CONSENSUS = '0x535B3D7A252fa034Ed71F0C53ec0C6F784cB64E1'
5-
MULTIPAYMENT = '0x83769BeEB7e5405ef0B7dc3C66C43E3a51A6d27f'
5+
MULTIPAYMENT = '0x00EFd0D4639191C49908A7BddbB9A11A994A8527'
66
USERNAMES = '0x2c1DE3b4Dbb4aDebEbB5dcECAe825bE2a9fc6eb6'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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.multipayment import Multipayment
5+
6+
class MultipaymentBuilder(AbstractTransactionBuilder):
7+
def __init__(self, data: Optional[dict] = None):
8+
super().__init__(data)
9+
10+
self.transaction.data['pay'] = [[], []]
11+
12+
self.recipient_address(ContractAddresses.MULTIPAYMENT.value)
13+
self.transaction.refresh_payload_data()
14+
15+
def pay(self, address: str, amount: str):
16+
self.transaction.data['pay'][0].append(address)
17+
self.transaction.data['pay'][1].append(amount)
18+
19+
self.transaction.refresh_payload_data()
20+
21+
self.transaction.data['value'] = str(int(self.transaction.data['value']) + int(amount))
22+
23+
return self
24+
25+
def get_transaction_instance(self, data: dict):
26+
return Multipayment(data)

crypto/transactions/deserializer.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from crypto.enums.constants import Constants
44
from crypto.enums.contract_abi_type import ContractAbiType
55
from crypto.transactions.types.abstract_transaction import AbstractTransaction
6+
from crypto.transactions.types.multipayment import Multipayment
67
from crypto.transactions.types.transfer import Transfer
78
from crypto.transactions.types.evm_call import EvmCall
89
from crypto.transactions.types.username_registration import UsernameRegistration
@@ -59,20 +60,26 @@ def deserialize(self) -> AbstractTransaction:
5960
return transaction
6061

6162
def guess_transaction_from_data(self, data: dict) -> AbstractTransaction:
63+
multipayment_payload_data = self.decode_payload(data, ContractAbiType.MULTIPAYMENT)
64+
if multipayment_payload_data is not None:
65+
function_name = multipayment_payload_data.get('functionName')
66+
if function_name == AbiFunction.MULTIPAYMENT.value:
67+
return Multipayment(data, multipayment_payload_data)
68+
6269
if data['value'] != '0':
6370
return Transfer(data)
6471

6572
consensus_payload_data = self.decode_payload(data)
6673
if consensus_payload_data is not None:
6774
function_name = consensus_payload_data.get('functionName')
6875
if function_name == AbiFunction.VOTE.value:
69-
return Vote(data)
76+
return Vote(data, consensus_payload_data)
7077

7178
if function_name == AbiFunction.UNVOTE.value:
7279
return Unvote(data)
7380

7481
if function_name == AbiFunction.VALIDATOR_REGISTRATION.value:
75-
return ValidatorRegistration(data)
82+
return ValidatorRegistration(data, consensus_payload_data)
7683

7784
if function_name == AbiFunction.VALIDATOR_RESIGNATION.value:
7885
return ValidatorResignation(data)
@@ -81,7 +88,7 @@ def guess_transaction_from_data(self, data: dict) -> AbstractTransaction:
8188
if username_payload_data is not None:
8289
function_name = username_payload_data.get('functionName')
8390
if function_name == AbiFunction.USERNAME_REGISTRATION.value:
84-
return UsernameRegistration(data)
91+
return UsernameRegistration(data, username_payload_data)
8592

8693
if function_name == AbiFunction.USERNAME_RESIGNATION.value:
8794
return UsernameResignation(data)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
from typing import Optional
2+
from crypto.enums.contract_abi_type import ContractAbiType
3+
from crypto.transactions.types.abstract_transaction import AbstractTransaction
4+
from crypto.utils.abi_encoder import AbiEncoder
5+
from crypto.enums.abi_function import AbiFunction
6+
7+
class Multipayment(AbstractTransaction):
8+
def __init__(self, data: Optional[dict] = None, payload: Optional[dict] = None):
9+
data = data or {}
10+
if payload is None:
11+
payload = self.decode_payload(data, ContractAbiType.MULTIPAYMENT)
12+
13+
if payload:
14+
data['pay'] = payload.get('args', [])
15+
16+
super().__init__(data)
17+
18+
def get_payload(self) -> str:
19+
if 'pay' not in self.data:
20+
return ''
21+
22+
encoder = AbiEncoder(ContractAbiType.MULTIPAYMENT)
23+
24+
return encoder.encode_function_call(AbiFunction.MULTIPAYMENT.value, self.data['pay'])

crypto/transactions/types/username_registration.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
from crypto.enums.abi_function import AbiFunction
66

77
class UsernameRegistration(AbstractTransaction):
8-
def __init__(self, data: Optional[dict] = None):
8+
def __init__(self, data: Optional[dict] = None, payload: Optional[dict] = None):
99
data = data or {}
10-
payload = self.decode_payload(data, ContractAbiType.USERNAMES)
10+
if payload is None:
11+
payload = self.decode_payload(data, ContractAbiType.USERNAMES)
12+
1113
if payload:
1214
data['username'] = payload.get('args', [None])[0] if payload.get('args') else None
1315

crypto/transactions/types/validator_registration.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
from crypto.utils.transaction_utils import TransactionUtils
66

77
class ValidatorRegistration(AbstractTransaction):
8-
def __init__(self, data: Optional[dict] = None):
8+
def __init__(self, data: Optional[dict] = None, payload: Optional[dict] = None):
99
data = data or {}
10-
payload = self.decode_payload(data)
10+
11+
if payload is None:
12+
payload = self.decode_payload(data)
13+
1114
if payload:
1215
data['validatorPublicKey'] = TransactionUtils.parse_hex_from_str(payload.get('args', [None])[0]) if payload.get('args') else None
1316
super().__init__(data)

crypto/transactions/types/vote.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44
from crypto.enums.abi_function import AbiFunction
55

66
class Vote(AbstractTransaction):
7-
def __init__(self, data: Optional[dict] = None):
7+
def __init__(self, data: Optional[dict] = None, payload: Optional[dict] = None):
88
data = data or {}
9-
payload = self.decode_payload(data)
9+
if payload is None:
10+
payload = self.decode_payload(data)
11+
1012
if payload:
1113
data['vote'] = payload.get('args', [None])[0] if payload.get('args') else None
1214

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"abi": [
3+
{
4+
"type": "function",
5+
"name": "pay",
6+
"inputs": [
7+
{
8+
"name": "recipients",
9+
"type": "address[]",
10+
"internalType": "address payable[]"
11+
},
12+
{
13+
"name": "amounts",
14+
"type": "uint256[]",
15+
"internalType": "uint256[]"
16+
}
17+
],
18+
"outputs": [],
19+
"stateMutability": "payable"
20+
},
21+
{ "type": "error", "name": "FailedToSendEther", "inputs": [] },
22+
{ "type": "error", "name": "InvalidValue", "inputs": [] },
23+
{
24+
"type": "error",
25+
"name": "RecipientsAndAmountsMismatch",
26+
"inputs": []
27+
}
28+
],
29+
"bytecode": {
30+
"object": "0x6080604052348015600e575f5ffd5b506102c98061001c5f395ff3fe60806040526004361061001d575f3560e01c8063084ce70814610021575b5f5ffd5b61003461002f3660046101c1565b610036565b005b828114610056576040516366d5293b60e11b815260040160405180910390fd5b5f805b8281101561008f578383828181106100735761007361022d565b90506020020135826100859190610241565b9150600101610059565b508034146100b057604051632a9ffab760e21b815260040160405180910390fd5b5f5b84811015610171575f8686838181106100cd576100cd61022d565b90506020020160208101906100e29190610266565b6001600160a01b03168585848181106100fd576100fd61022d565b905060200201356040515f6040518083038185875af1925050503d805f8114610141576040519150601f19603f3d011682016040523d82523d5f602084013e610146565b606091505b505090508061016857604051630dcf35db60e41b815260040160405180910390fd5b506001016100b2565b505050505050565b5f5f83601f840112610189575f5ffd5b50813567ffffffffffffffff8111156101a0575f5ffd5b6020830191508360208260051b85010111156101ba575f5ffd5b9250929050565b5f5f5f5f604085870312156101d4575f5ffd5b843567ffffffffffffffff8111156101ea575f5ffd5b6101f687828801610179565b909550935050602085013567ffffffffffffffff811115610215575f5ffd5b61022187828801610179565b95989497509550505050565b634e487b7160e01b5f52603260045260245ffd5b8082018082111561026057634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610276575f5ffd5b81356001600160a01b038116811461028c575f5ffd5b939250505056fea2646970667358221220bfee9113d4628767f3e4ea5baeb21f9c3bd88ea4c440d0a915dae090d37cd9a664736f6c634300081b0033",
31+
"sourceMap": "81:836:23:-:0;;;;;;;;;;;;;;;;;;;",
32+
"linkReferences": {}
33+
},
34+
"deployedBytecode": {
35+
"object": "0x60806040526004361061001d575f3560e01c8063084ce70814610021575b5f5ffd5b61003461002f3660046101c1565b610036565b005b828114610056576040516366d5293b60e11b815260040160405180910390fd5b5f805b8281101561008f578383828181106100735761007361022d565b90506020020135826100859190610241565b9150600101610059565b508034146100b057604051632a9ffab760e21b815260040160405180910390fd5b5f5b84811015610171575f8686838181106100cd576100cd61022d565b90506020020160208101906100e29190610266565b6001600160a01b03168585848181106100fd576100fd61022d565b905060200201356040515f6040518083038185875af1925050503d805f8114610141576040519150601f19603f3d011682016040523d82523d5f602084013e610146565b606091505b505090508061016857604051630dcf35db60e41b815260040160405180910390fd5b506001016100b2565b505050505050565b5f5f83601f840112610189575f5ffd5b50813567ffffffffffffffff8111156101a0575f5ffd5b6020830191508360208260051b85010111156101ba575f5ffd5b9250929050565b5f5f5f5f604085870312156101d4575f5ffd5b843567ffffffffffffffff8111156101ea575f5ffd5b6101f687828801610179565b909550935050602085013567ffffffffffffffff811115610215575f5ffd5b61022187828801610179565b95989497509550505050565b634e487b7160e01b5f52603260045260245ffd5b8082018082111561026057634e487b7160e01b5f52601160045260245ffd5b92915050565b5f60208284031215610276575f5ffd5b81356001600160a01b038116811461028c575f5ffd5b939250505056fea2646970667358221220bfee9113d4628767f3e4ea5baeb21f9c3bd88ea4c440d0a915dae090d37cd9a664736f6c634300081b0033",
36+
"sourceMap": "81:836:23:-:0;;;;;;;;;;;;;;;;;;;;;209:706;;;;;;:::i;:::-;;:::i;:::-;;;318:35;;;314:103;;376:30;;-1:-1:-1;;;376:30:23;;;;;;;;;;;314:103;492:13;;519:89;539:18;;;519:89;;;587:7;;595:1;587:10;;;;;;;:::i;:::-;;;;;;;578:19;;;;;:::i;:::-;;-1:-1:-1;559:3:23;;519:89;;;;634:5;621:9;:18;617:70;;662:14;;-1:-1:-1;;;662:14:23;;;;;;;;;;;617:70;702:9;697:212;717:21;;;697:212;;;760:9;774:10;;785:1;774:13;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;774:18:23;800:7;;808:1;800:10;;;;;;;:::i;:::-;;;;;;;774:41;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;759:56;;;834:4;829:70;;865:19;;-1:-1:-1;;;865:19:23;;;;;;;;;;;829:70;-1:-1:-1;740:3:23;;697:212;;;;304:611;209:706;;;;:::o;14:375:25:-;85:8;95:6;149:3;142:4;134:6;130:17;126:27;116:55;;167:1;164;157:12;116:55;-1:-1:-1;190:20:25;;233:18;222:30;;219:50;;;265:1;262;255:12;219:50;302:4;294:6;290:17;278:29;;362:3;355:4;345:6;342:1;338:14;330:6;326:27;322:38;319:47;316:67;;;379:1;376;369:12;316:67;14:375;;;;;:::o;394:792::-;524:6;532;540;548;601:2;589:9;580:7;576:23;572:32;569:52;;;617:1;614;607:12;569:52;657:9;644:23;690:18;682:6;679:30;676:50;;;722:1;719;712:12;676:50;761:78;831:7;822:6;811:9;807:22;761:78;:::i;:::-;858:8;;-1:-1:-1;735:104:25;-1:-1:-1;;946:2:25;931:18;;918:32;975:18;962:32;;959:52;;;1007:1;1004;997:12;959:52;1046:80;1118:7;1107:8;1096:9;1092:24;1046:80;:::i;:::-;394:792;;;;-1:-1:-1;1145:8:25;-1:-1:-1;;;;394:792:25:o;1191:127::-;1252:10;1247:3;1243:20;1240:1;1233:31;1283:4;1280:1;1273:15;1307:4;1304:1;1297:15;1323:222;1388:9;;;1409:10;;;1406:133;;;1461:10;1456:3;1452:20;1449:1;1442:31;1496:4;1493:1;1486:15;1524:4;1521:1;1514:15;1406:133;1323:222;;;;:::o;1550:294::-;1617:6;1670:2;1658:9;1649:7;1645:23;1641:32;1638:52;;;1686:1;1683;1676:12;1638:52;1712:23;;-1:-1:-1;;;;;1764:31:25;;1754:42;;1744:70;;1810:1;1807;1800:12;1744:70;1833:5;1550:294;-1:-1:-1;;;1550:294:25:o",
37+
"linkReferences": {}
38+
},
39+
"methodIdentifiers": { "pay(address[],uint256[])": "084ce708" },
40+
"rawMetadata": "{\"compiler\":{\"version\":\"0.8.27+commit.40a35a09\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"name\":\"FailedToSendEther\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"InvalidValue\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"RecipientsAndAmountsMismatch\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address payable[]\",\"name\":\"recipients\",\"type\":\"address[]\"},{\"internalType\":\"uint256[]\",\"name\":\"amounts\",\"type\":\"uint256[]\"}],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"src/multi-payment/MultiPayment.sol\":\"MultiPayment\"},\"evmVersion\":\"shanghai\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[\":@contracts/=src/\",\":@forge-std/=lib/forge-std/src/\",\":@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/\",\":@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/\",\":ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/\",\":erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/\",\":forge-std/=lib/forge-std/src/\",\":halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/\",\":openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/\",\":openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/\",\":openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/\",\":solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/\"]},\"sources\":{\"src/multi-payment/MultiPayment.sol\":{\"keccak256\":\"0x804a5823ffdb1866bc5f42a0fe3688c8e153da92e279c7b79aa3d73472a1ce35\",\"license\":\"GNU GENERAL PUBLIC LICENSE\",\"urls\":[\"bzz-raw://43df2e2ee0c0a76dd4ee8bd6cc81d65c80fb1aaada5e925d129a91de434d2c52\",\"dweb:/ipfs/QmRj4GTC5R6QTcaBxmghrH3ABuDXXwvHpi4yYjd1e8VduW\"]}},\"version\":1}",
41+
"metadata": {
42+
"compiler": { "version": "0.8.27+commit.40a35a09" },
43+
"language": "Solidity",
44+
"output": {
45+
"abi": [
46+
{ "inputs": [], "type": "error", "name": "FailedToSendEther" },
47+
{ "inputs": [], "type": "error", "name": "InvalidValue" },
48+
{
49+
"inputs": [],
50+
"type": "error",
51+
"name": "RecipientsAndAmountsMismatch"
52+
},
53+
{
54+
"inputs": [
55+
{
56+
"internalType": "address payable[]",
57+
"name": "recipients",
58+
"type": "address[]"
59+
},
60+
{
61+
"internalType": "uint256[]",
62+
"name": "amounts",
63+
"type": "uint256[]"
64+
}
65+
],
66+
"stateMutability": "payable",
67+
"type": "function",
68+
"name": "pay"
69+
}
70+
],
71+
"devdoc": { "kind": "dev", "methods": {}, "version": 1 },
72+
"userdoc": { "kind": "user", "methods": {}, "version": 1 }
73+
},
74+
"settings": {
75+
"remappings": [
76+
"@contracts/=src/",
77+
"@forge-std/=lib/forge-std/src/",
78+
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
79+
"@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/",
80+
"ds-test/=lib/openzeppelin-contracts-upgradeable/lib/forge-std/lib/ds-test/src/",
81+
"erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
82+
"forge-std/=lib/forge-std/src/",
83+
"halmos-cheatcodes/=lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
84+
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
85+
"openzeppelin-contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/",
86+
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
87+
"solidity-stringutils/=lib/openzeppelin-foundry-upgrades/lib/solidity-stringutils/"
88+
],
89+
"optimizer": { "enabled": true, "runs": 200 },
90+
"metadata": { "bytecodeHash": "ipfs" },
91+
"compilationTarget": {
92+
"src/multi-payment/MultiPayment.sol": "MultiPayment"
93+
},
94+
"evmVersion": "shanghai",
95+
"libraries": {}
96+
},
97+
"sources": {
98+
"src/multi-payment/MultiPayment.sol": {
99+
"keccak256": "0x804a5823ffdb1866bc5f42a0fe3688c8e153da92e279c7b79aa3d73472a1ce35",
100+
"urls": [
101+
"bzz-raw://43df2e2ee0c0a76dd4ee8bd6cc81d65c80fb1aaada5e925d129a91de434d2c52",
102+
"dweb:/ipfs/QmRj4GTC5R6QTcaBxmghrH3ABuDXXwvHpi4yYjd1e8VduW"
103+
],
104+
"license": "GNU GENERAL PUBLIC LICENSE"
105+
}
106+
},
107+
"version": 1
108+
},
109+
"id": 23
110+
}

0 commit comments

Comments
 (0)