acp-plugin-gamesdk 0.1.23__py3-none-any.whl → 0.1.26__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_plugin.py +194 -202
- acp_plugin_gamesdk/env.py +35 -0
- acp_plugin_gamesdk/interface.py +73 -112
- {acp_plugin_gamesdk-0.1.23.dist-info → acp_plugin_gamesdk-0.1.26.dist-info}/METADATA +4 -14
- acp_plugin_gamesdk-0.1.26.dist-info/RECORD +6 -0
- acp_plugin_gamesdk/acp_client.py +0 -304
- acp_plugin_gamesdk/acp_token.py +0 -352
- acp_plugin_gamesdk/acp_token_abi.py +0 -678
- acp_plugin_gamesdk/configs.py +0 -44
- acp_plugin_gamesdk-0.1.23.dist-info/RECORD +0 -9
- {acp_plugin_gamesdk-0.1.23.dist-info → acp_plugin_gamesdk-0.1.26.dist-info}/WHEEL +0 -0
acp_plugin_gamesdk/acp_token.py
DELETED
@@ -1,352 +0,0 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from enum import IntEnum
|
3
|
-
import json
|
4
|
-
import time
|
5
|
-
import traceback
|
6
|
-
from typing import Optional, Tuple, TypedDict
|
7
|
-
|
8
|
-
from eth_account import Account
|
9
|
-
from eth_account.messages import encode_defunct
|
10
|
-
import requests
|
11
|
-
from web3 import Web3
|
12
|
-
|
13
|
-
from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
|
14
|
-
from acp_plugin_gamesdk.configs import ACPContractConfig
|
15
|
-
|
16
|
-
|
17
|
-
class MemoType(IntEnum):
|
18
|
-
MESSAGE = 0
|
19
|
-
CONTEXT_URL = 1
|
20
|
-
IMAGE_URL = 2
|
21
|
-
VOICE_URL = 3
|
22
|
-
OBJECT_URL = 4
|
23
|
-
TXHASH = 5
|
24
|
-
|
25
|
-
|
26
|
-
class IMemo(TypedDict):
|
27
|
-
content: str
|
28
|
-
memoType: MemoType
|
29
|
-
isSecured: bool
|
30
|
-
nextPhase: int
|
31
|
-
jobId: int
|
32
|
-
numApprovals: int
|
33
|
-
sender: str
|
34
|
-
|
35
|
-
|
36
|
-
class IJob(TypedDict):
|
37
|
-
id: int
|
38
|
-
client: str
|
39
|
-
provider: str
|
40
|
-
budget: int
|
41
|
-
amountClaimed: int
|
42
|
-
phase: int
|
43
|
-
memoCount: int
|
44
|
-
expiredAt: int
|
45
|
-
evaluatorCount: int
|
46
|
-
|
47
|
-
|
48
|
-
JobResult = Tuple[int, str, str, str, str, str, str, str, int]
|
49
|
-
|
50
|
-
|
51
|
-
class AcpToken:
|
52
|
-
def __init__(
|
53
|
-
self,
|
54
|
-
wallet_private_key: str,
|
55
|
-
agent_wallet_address: str,
|
56
|
-
config: ACPContractConfig,
|
57
|
-
):
|
58
|
-
self.web3 = Web3(Web3.HTTPProvider(config.rpc_url))
|
59
|
-
self.account = Account.from_key(wallet_private_key)
|
60
|
-
self.agent_wallet_address = agent_wallet_address
|
61
|
-
self.contract_address = Web3.to_checksum_address(config.contract_address)
|
62
|
-
self.virtuals_token_address = Web3.to_checksum_address(config.virtuals_token_address)
|
63
|
-
self.contract = self.web3.eth.contract(
|
64
|
-
address=self.contract_address,
|
65
|
-
abi=ACP_TOKEN_ABI
|
66
|
-
)
|
67
|
-
self.virtuals_token_contract = self.web3.eth.contract(
|
68
|
-
address=self.virtuals_token_address,
|
69
|
-
abi=[{
|
70
|
-
"inputs": [
|
71
|
-
{
|
72
|
-
"internalType": "address",
|
73
|
-
"name": "spender",
|
74
|
-
"type": "address"
|
75
|
-
},
|
76
|
-
{
|
77
|
-
"internalType": "uint256",
|
78
|
-
"name": "amount",
|
79
|
-
"type": "uint256"
|
80
|
-
}
|
81
|
-
],
|
82
|
-
"name": "approve",
|
83
|
-
"outputs": [
|
84
|
-
{
|
85
|
-
"internalType": "bool",
|
86
|
-
"name": "",
|
87
|
-
"type": "bool"
|
88
|
-
}
|
89
|
-
],
|
90
|
-
"stateMutability": "nonpayable",
|
91
|
-
"type": "function"
|
92
|
-
}]
|
93
|
-
)
|
94
|
-
self.acp_base_url = config.acp_api_url
|
95
|
-
self.game_api_url = config.game_api_url
|
96
|
-
|
97
|
-
def get_agent_wallet_address(self) -> str:
|
98
|
-
return self.agent_wallet_address
|
99
|
-
|
100
|
-
def get_contract_address(self) -> str:
|
101
|
-
return self.contract_address
|
102
|
-
|
103
|
-
def validate_transaction(self, hash_value: str) -> object:
|
104
|
-
try:
|
105
|
-
response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result",
|
106
|
-
json={"userOpHash": hash_value})
|
107
|
-
return response.json()
|
108
|
-
except Exception as error:
|
109
|
-
print(traceback.format_exc())
|
110
|
-
raise Exception(f"Failed to get job_id {error}")
|
111
|
-
|
112
|
-
def create_job(
|
113
|
-
self,
|
114
|
-
provider_address: str,
|
115
|
-
evaluator_address: str,
|
116
|
-
expire_at: datetime
|
117
|
-
) -> dict:
|
118
|
-
try:
|
119
|
-
provider_address = Web3.to_checksum_address(provider_address)
|
120
|
-
evaluator_address = Web3.to_checksum_address(evaluator_address)
|
121
|
-
expire_timestamp = int(expire_at.timestamp())
|
122
|
-
|
123
|
-
# Sign the transaction
|
124
|
-
trx_data, signature = self._sign_transaction(
|
125
|
-
"createJob",
|
126
|
-
[provider_address, evaluator_address, expire_timestamp]
|
127
|
-
)
|
128
|
-
|
129
|
-
# Prepare payload
|
130
|
-
payload = {
|
131
|
-
"agentWallet": self.get_agent_wallet_address(),
|
132
|
-
"trxData": trx_data,
|
133
|
-
"signature": signature
|
134
|
-
}
|
135
|
-
|
136
|
-
# Submit to custom API
|
137
|
-
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
138
|
-
response = requests.post(api_url, json=payload)
|
139
|
-
|
140
|
-
if response.json().get("error"):
|
141
|
-
raise Exception(
|
142
|
-
f"Failed to create job {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
|
143
|
-
|
144
|
-
# Return transaction hash or response ID
|
145
|
-
return {"txHash": response.json().get("data", {}).get("userOpHash", "")}
|
146
|
-
|
147
|
-
except Exception as e:
|
148
|
-
raise
|
149
|
-
|
150
|
-
def approve_allowance(self, price_in_wei: int) -> str:
|
151
|
-
try:
|
152
|
-
trx_data, signature = self._sign_transaction(
|
153
|
-
"approve",
|
154
|
-
[self.contract_address, price_in_wei],
|
155
|
-
self.virtuals_token_address
|
156
|
-
)
|
157
|
-
|
158
|
-
payload = {
|
159
|
-
"agentWallet": self.get_agent_wallet_address(),
|
160
|
-
"trxData": trx_data,
|
161
|
-
"signature": signature
|
162
|
-
}
|
163
|
-
|
164
|
-
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
165
|
-
response = requests.post(api_url, json=payload)
|
166
|
-
|
167
|
-
if (response.json().get("error")):
|
168
|
-
raise Exception(
|
169
|
-
f"Failed to approve allowance {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
|
170
|
-
|
171
|
-
return response.json()
|
172
|
-
except Exception as e:
|
173
|
-
print(f"An error occurred while approving allowance: {e}")
|
174
|
-
raise
|
175
|
-
|
176
|
-
def create_memo(
|
177
|
-
self,
|
178
|
-
job_id: int,
|
179
|
-
content: str,
|
180
|
-
memo_type: MemoType,
|
181
|
-
is_secured: bool,
|
182
|
-
next_phase: int
|
183
|
-
) -> dict:
|
184
|
-
retries = 3
|
185
|
-
error = None
|
186
|
-
while retries > 0:
|
187
|
-
try:
|
188
|
-
trx_data, signature = self._sign_transaction(
|
189
|
-
"createMemo",
|
190
|
-
[job_id, content, memo_type, is_secured, next_phase]
|
191
|
-
)
|
192
|
-
|
193
|
-
payload = {
|
194
|
-
"agentWallet": self.get_agent_wallet_address(),
|
195
|
-
"trxData": trx_data,
|
196
|
-
"signature": signature
|
197
|
-
}
|
198
|
-
|
199
|
-
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
200
|
-
response = requests.post(api_url, json=payload)
|
201
|
-
|
202
|
-
if (response.json().get("error")):
|
203
|
-
raise Exception(
|
204
|
-
f"Failed to create memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
|
205
|
-
|
206
|
-
return {"txHash": response.json().get("txHash", response.json().get("id", "")),
|
207
|
-
"memoId": response.json().get("memoId", "")}
|
208
|
-
except Exception as e:
|
209
|
-
print(f"{e}")
|
210
|
-
print(traceback.format_exc())
|
211
|
-
error = e
|
212
|
-
retries -= 1
|
213
|
-
time.sleep(2 * (3 - retries))
|
214
|
-
|
215
|
-
if error:
|
216
|
-
raise Exception(f"{error}")
|
217
|
-
|
218
|
-
def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[
|
219
|
-
dict, str]:
|
220
|
-
if contract_address:
|
221
|
-
encoded_data = self.virtuals_token_contract.encode_abi(method_name, args=args)
|
222
|
-
else:
|
223
|
-
encoded_data = self.contract.encode_abi(method_name, args=args)
|
224
|
-
|
225
|
-
trx_data = {
|
226
|
-
"target": contract_address if contract_address else self.get_contract_address(),
|
227
|
-
"value": "0",
|
228
|
-
"data": encoded_data
|
229
|
-
}
|
230
|
-
|
231
|
-
message_json = json.dumps(trx_data, separators=(",", ":"), sort_keys=False)
|
232
|
-
message_bytes = message_json.encode()
|
233
|
-
|
234
|
-
# Sign the transaction
|
235
|
-
message = encode_defunct(message_bytes)
|
236
|
-
signature = "0x" + self.account.sign_message(message).signature.hex()
|
237
|
-
|
238
|
-
return trx_data, signature
|
239
|
-
|
240
|
-
def sign_memo(
|
241
|
-
self,
|
242
|
-
memo_id: int,
|
243
|
-
is_approved: bool,
|
244
|
-
reason: Optional[str] = ""
|
245
|
-
) -> str:
|
246
|
-
retries = 3
|
247
|
-
error = None
|
248
|
-
while retries > 0:
|
249
|
-
try:
|
250
|
-
trx_data, signature = self._sign_transaction(
|
251
|
-
"signMemo",
|
252
|
-
[memo_id, is_approved, reason]
|
253
|
-
)
|
254
|
-
|
255
|
-
payload = {
|
256
|
-
"agentWallet": self.get_agent_wallet_address(),
|
257
|
-
"trxData": trx_data,
|
258
|
-
"signature": signature
|
259
|
-
}
|
260
|
-
|
261
|
-
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
262
|
-
response = requests.post(api_url, json=payload)
|
263
|
-
|
264
|
-
if (response.json().get("error")):
|
265
|
-
raise Exception(
|
266
|
-
f"Failed to sign memo {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
|
267
|
-
|
268
|
-
return response.json()
|
269
|
-
|
270
|
-
except Exception as e:
|
271
|
-
error = e
|
272
|
-
print(f"{error}")
|
273
|
-
print(traceback.format_exc())
|
274
|
-
retries -= 1
|
275
|
-
time.sleep(2 * (3 - retries))
|
276
|
-
|
277
|
-
raise Exception(f"Failed to sign memo {error}")
|
278
|
-
|
279
|
-
def set_budget(self, job_id: int, budget: int) -> str:
|
280
|
-
try:
|
281
|
-
trx_data, signature = self._sign_transaction(
|
282
|
-
"setBudget",
|
283
|
-
[job_id, budget]
|
284
|
-
)
|
285
|
-
|
286
|
-
payload = {
|
287
|
-
"agentWallet": self.get_agent_wallet_address(),
|
288
|
-
"trxData": trx_data,
|
289
|
-
"signature": signature
|
290
|
-
}
|
291
|
-
|
292
|
-
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
293
|
-
response = requests.post(api_url, json=payload)
|
294
|
-
|
295
|
-
if (response.json().get("error")):
|
296
|
-
raise Exception(
|
297
|
-
f"Failed to set budget {response.json().get('error').get('status')}, Message: {response.json().get('error').get('message')}")
|
298
|
-
|
299
|
-
return response.json()
|
300
|
-
except Exception as error:
|
301
|
-
raise Exception(f"{error}")
|
302
|
-
|
303
|
-
def get_job(self, job_id: int) -> Optional[IJob]:
|
304
|
-
try:
|
305
|
-
job_data = self.contract.functions.jobs(job_id).call()
|
306
|
-
|
307
|
-
if not job_data:
|
308
|
-
return None
|
309
|
-
|
310
|
-
return {
|
311
|
-
'id': job_data[0],
|
312
|
-
'client': job_data[1],
|
313
|
-
'provider': job_data[2],
|
314
|
-
'budget': int(job_data[3]),
|
315
|
-
'amountClaimed': int(job_data[4]),
|
316
|
-
'phase': int(job_data[5]),
|
317
|
-
'memoCount': int(job_data[6]),
|
318
|
-
'expiredAt': int(job_data[7]),
|
319
|
-
'evaluatorCount': int(job_data[8])
|
320
|
-
}
|
321
|
-
except Exception as error:
|
322
|
-
raise Exception(f"{error}")
|
323
|
-
|
324
|
-
def get_memo_by_job(
|
325
|
-
self,
|
326
|
-
job_id: int,
|
327
|
-
memo_type: Optional[MemoType] = None
|
328
|
-
) -> Optional[IMemo]:
|
329
|
-
try:
|
330
|
-
memos = self.contract.functions.getAllMemos(job_id).call()
|
331
|
-
|
332
|
-
if memo_type is not None:
|
333
|
-
filtered_memos = [m for m in memos if m['memoType'] == memo_type]
|
334
|
-
return filtered_memos[-1] if filtered_memos else None
|
335
|
-
else:
|
336
|
-
return memos[-1] if memos else None
|
337
|
-
except Exception as error:
|
338
|
-
raise Exception(f"Failed to get memo by job {error}")
|
339
|
-
|
340
|
-
def get_memos_for_phase(
|
341
|
-
self,
|
342
|
-
job_id: int,
|
343
|
-
phase: int,
|
344
|
-
target_phase: int
|
345
|
-
) -> Optional[IMemo]:
|
346
|
-
try:
|
347
|
-
memos = self.contract.functions.getMemosForPhase(job_id, phase).call()
|
348
|
-
|
349
|
-
target_memos = [m for m in memos if m['nextPhase'] == target_phase]
|
350
|
-
return target_memos[-1] if target_memos else None
|
351
|
-
except Exception as error:
|
352
|
-
raise Exception(f"Failed to get memos for phase {error}")
|