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.
- acp_plugin_gamesdk/acp_client.py +173 -0
- acp_plugin_gamesdk/acp_plugin.py +459 -0
- acp_plugin_gamesdk/acp_token.py +291 -0
- acp_plugin_gamesdk/acp_token_abi.py +690 -0
- acp_plugin_gamesdk/interface.py +70 -0
- acp_plugin_gamesdk-0.1.0.dist-info/METADATA +201 -0
- acp_plugin_gamesdk-0.1.0.dist-info/RECORD +8 -0
- acp_plugin_gamesdk-0.1.0.dist-info/WHEEL +4 -0
@@ -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")
|