acp-plugin-gamesdk 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,291 @@
1
+ import asyncio
2
+ from enum import IntEnum
3
+ import time
4
+ from typing import Optional, Tuple, TypedDict, List
5
+ from datetime import datetime
6
+ from web3 import Web3
7
+ from eth_account import Account
8
+ import sys
9
+ import os
10
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
11
+ from .acp_token_abi import ACP_TOKEN_ABI
12
+
13
+ class MemoType(IntEnum):
14
+ MESSAGE = 0
15
+ CONTEXT_URL = 1
16
+ IMAGE_URL = 2
17
+ VOICE_URL = 3
18
+ OBJECT_URL = 4
19
+ TXHASH = 5
20
+
21
+ class IMemo(TypedDict):
22
+ content: str
23
+ memoType: MemoType
24
+ isSecured: bool
25
+ nextPhase: int
26
+ jobId: int
27
+ numApprovals: int
28
+ sender: str
29
+
30
+ class IJob(TypedDict):
31
+ id: int
32
+ client: str
33
+ provider: str
34
+ budget: int
35
+ amountClaimed: int
36
+ phase: int
37
+ memoCount: int
38
+ expiredAt: int
39
+ evaluatorCount: int
40
+
41
+ JobResult = Tuple[int, str, str, str, str, str, str, str, int]
42
+
43
+ class AcpToken:
44
+ def __init__(
45
+ self,
46
+ wallet_private_key: str,
47
+ network_url: str,
48
+ contract_address: str = "0x5e4ee2620482f7c4fee12bf27b095e48d441f5cf",
49
+ virtuals_token_address: str = "0xbfAB80ccc15DF6fb7185f9498d6039317331846a"
50
+ ):
51
+ self.web3 = Web3(Web3.HTTPProvider(network_url))
52
+ self.account = Account.from_key(wallet_private_key)
53
+ self.contract_address = Web3.to_checksum_address(contract_address)
54
+ self.virtuals_token_address = Web3.to_checksum_address(virtuals_token_address)
55
+ self.contract = self.web3.eth.contract(
56
+ address=self.contract_address,
57
+ abi=ACP_TOKEN_ABI
58
+ )
59
+
60
+ def get_contract_address(self) -> str:
61
+ return self.contract_address
62
+
63
+ def get_wallet_address(self) -> str:
64
+ return self.account.address
65
+
66
+ def create_job(
67
+ self,
68
+ provider_address: str,
69
+ expire_at: datetime
70
+ ) -> dict:
71
+ try:
72
+ provider_address = Web3.to_checksum_address(provider_address)
73
+ expire_timestamp = int(expire_at.timestamp())
74
+
75
+ transaction = self.contract.functions.createJob(
76
+ provider_address,
77
+ expire_timestamp
78
+ ).build_transaction({
79
+ 'from': self.account.address,
80
+ 'nonce': self.web3.eth.get_transaction_count(self.account.address),
81
+ })
82
+
83
+ signed_txn = self.web3.eth.account.sign_transaction(
84
+ transaction,
85
+ self.account.key
86
+ )
87
+
88
+ tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
89
+ receipt = self.web3.eth.wait_for_transaction_receipt(tx_hash)
90
+
91
+ # Get job ID from event logs
92
+ job_created_event = self.contract.events.JobCreated().process_receipt(receipt)
93
+ job_id = job_created_event[0]['args']['jobId']
94
+
95
+ return {
96
+ 'txHash': tx_hash.hex(),
97
+ 'jobId': job_id
98
+ }
99
+ except Exception as error:
100
+ print(f"Error creating job: {error}")
101
+ raise Exception("Failed to create job")
102
+
103
+ def approve_allowance(self, price_in_wei: int) -> str:
104
+ try:
105
+ erc20_contract = self.web3.eth.contract(
106
+ address=self.virtuals_token_address,
107
+ abi=[{
108
+ "inputs": [
109
+ {"name": "spender", "type": "address"},
110
+ {"name": "amount", "type": "uint256"}
111
+ ],
112
+ "name": "approve",
113
+ "outputs": [{"name": "", "type": "bool"}],
114
+ "stateMutability": "nonpayable",
115
+ "type": "function"
116
+ }]
117
+ )
118
+
119
+ transaction = erc20_contract.functions.approve(
120
+ self.contract_address,
121
+ price_in_wei
122
+ ).build_transaction({
123
+ 'from': self.account.address,
124
+ 'nonce': self.web3.eth.get_transaction_count(self.account.address),
125
+ })
126
+
127
+ signed_txn = self.web3.eth.account.sign_transaction(
128
+ transaction,
129
+ self.account.key
130
+ )
131
+ tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
132
+ self.web3.eth.wait_for_transaction_receipt(tx_hash)
133
+
134
+ return tx_hash.hex()
135
+ except Exception as error:
136
+ print(f"Error approving allowance: {error}")
137
+ raise Exception("Failed to approve allowance")
138
+
139
+ def create_memo(
140
+ self,
141
+ job_id: int,
142
+ content: str,
143
+ memo_type: MemoType,
144
+ is_secured: bool,
145
+ next_phase: int
146
+ ) -> dict:
147
+ retries = 3
148
+ while retries > 0:
149
+ try:
150
+ transaction = self.contract.functions.createMemo(
151
+ jobId = job_id,
152
+ content = content,
153
+ memoType = memo_type,
154
+ isSecured = is_secured,
155
+ nextPhase = next_phase
156
+ ).build_transaction({
157
+ 'from': self.account.address,
158
+ 'nonce': self.web3.eth.get_transaction_count(self.account.address),
159
+ })
160
+
161
+ signed_txn = self.web3.eth.account.sign_transaction(
162
+ transaction,
163
+ self.account.key
164
+ )
165
+ tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
166
+ receipt = self.web3.eth.wait_for_transaction_receipt(tx_hash)
167
+
168
+ # Get memo ID from event logs
169
+ new_memo_event = self.contract.events.NewMemo().process_receipt(receipt)
170
+ memo_id = new_memo_event[0]['args']['memoId']
171
+
172
+ return {
173
+ 'txHash': tx_hash.hex(),
174
+ 'memoId': memo_id
175
+ }
176
+ except Exception as error:
177
+ print(f"Error creating memo: {error}")
178
+ retries -= 1
179
+ time.sleep(2 * (3 - retries))
180
+
181
+ raise Exception("Failed to create memo")
182
+
183
+ def sign_memo(
184
+ self,
185
+ memo_id: int,
186
+ is_approved: bool,
187
+ reason: Optional[str] = ""
188
+ ) -> str:
189
+ retries = 3
190
+ while retries > 0:
191
+ try:
192
+ transaction = self.contract.functions.signMemo(
193
+ memo_id,
194
+ is_approved,
195
+ reason or ""
196
+ ).build_transaction({
197
+ 'from': self.account.address,
198
+ 'nonce': self.web3.eth.get_transaction_count(self.account.address),
199
+ })
200
+
201
+ signed_txn = self.web3.eth.account.sign_transaction(
202
+ transaction,
203
+ self.account.key
204
+ )
205
+
206
+ tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
207
+ self.web3.eth.wait_for_transaction_receipt(tx_hash)
208
+
209
+ return tx_hash.hex()
210
+ except Exception as error:
211
+ print(f"Error signing memo: {error}")
212
+ retries -= 1
213
+ time.sleep(2 * (3 - retries))
214
+
215
+ raise Exception("Failed to sign memo")
216
+
217
+ def set_budget(self, job_id: int, budget: int) -> str:
218
+ try:
219
+ transaction = self.contract.functions.setBudget(
220
+ job_id,
221
+ budget
222
+ ).build_transaction({
223
+ 'from': self.account.address,
224
+ 'nonce': self.web3.eth.get_transaction_count(self.account.address),
225
+ })
226
+
227
+ signed_txn = self.web3.eth.account.sign_transaction(
228
+ transaction,
229
+ self.account.key
230
+ )
231
+ tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
232
+ self.web3.eth.wait_for_transaction_receipt(tx_hash)
233
+
234
+ return tx_hash.hex()
235
+ except Exception as error:
236
+ print(f"Error setting budget: {error}")
237
+ raise Exception("Failed to set budget")
238
+
239
+ def get_job(self, job_id: int) -> Optional[IJob]:
240
+ try:
241
+ job_data = self.contract.functions.jobs(job_id).call()
242
+
243
+ if not job_data:
244
+ return None
245
+
246
+ return {
247
+ 'id': job_data[0],
248
+ 'client': job_data[1],
249
+ 'provider': job_data[2],
250
+ 'budget': int(job_data[3]),
251
+ 'amountClaimed': int(job_data[4]),
252
+ 'phase': int(job_data[5]),
253
+ 'memoCount': int(job_data[6]),
254
+ 'expiredAt': int(job_data[7]),
255
+ 'evaluatorCount': int(job_data[8])
256
+ }
257
+ except Exception as error:
258
+ print(f"Error getting job: {error}")
259
+ raise Exception("Failed to get job")
260
+
261
+ def get_memo_by_job(
262
+ self,
263
+ job_id: int,
264
+ memo_type: Optional[MemoType] = None
265
+ ) -> Optional[IMemo]:
266
+ try:
267
+ memos = self.contract.functions.getAllMemos(job_id).call()
268
+
269
+ if memo_type is not None:
270
+ filtered_memos = [m for m in memos if m['memoType'] == memo_type]
271
+ return filtered_memos[-1] if filtered_memos else None
272
+ else:
273
+ return memos[-1] if memos else None
274
+ except Exception as error:
275
+ print(f"Error getting memo: {error}")
276
+ raise Exception("Failed to get memo")
277
+
278
+ def get_memos_for_phase(
279
+ self,
280
+ job_id: int,
281
+ phase: int,
282
+ target_phase: int
283
+ ) -> Optional[IMemo]:
284
+ try:
285
+ memos = self.contract.functions.getMemosForPhase(job_id, phase).call()
286
+
287
+ target_memos = [m for m in memos if m['nextPhase'] == target_phase]
288
+ return target_memos[-1] if target_memos else None
289
+ except Exception as error:
290
+ print(f"Error getting memos: {error}")
291
+ raise Exception("Failed to get memos")