acp-plugin-gamesdk 0.1.0__py3-none-any.whl → 0.1.2__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 +83 -37
- acp_plugin_gamesdk/acp_plugin.py +20 -15
- acp_plugin_gamesdk/acp_token.py +150 -111
- acp_plugin_gamesdk/acp_token_abi.py +56 -68
- {acp_plugin_gamesdk-0.1.0.dist-info → acp_plugin_gamesdk-0.1.2.dist-info}/METADATA +70 -24
- acp_plugin_gamesdk-0.1.2.dist-info/RECORD +8 -0
- acp_plugin_gamesdk-0.1.0.dist-info/RECORD +0 -8
- {acp_plugin_gamesdk-0.1.0.dist-info → acp_plugin_gamesdk-0.1.2.dist-info}/WHEEL +0 -0
acp_plugin_gamesdk/acp_client.py
CHANGED
@@ -2,74 +2,116 @@ from datetime import datetime, timedelta
|
|
2
2
|
from typing import List, Optional
|
3
3
|
from web3 import Web3
|
4
4
|
import requests
|
5
|
+
from acp_plugin_gamesdk.interface import AcpAgent, AcpJobPhases, AcpState
|
6
|
+
from acp_plugin_gamesdk.acp_token import AcpToken, MemoType
|
7
|
+
import time
|
5
8
|
|
6
|
-
import sys
|
7
|
-
import os
|
8
|
-
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
|
9
|
-
from .interface import AcpAgent, AcpJobPhases, AcpState
|
10
|
-
from .acp_token import AcpToken, MemoType
|
11
9
|
|
12
10
|
class AcpClient:
|
13
|
-
def __init__(self, api_key: str, acp_token: AcpToken):
|
11
|
+
def __init__(self, api_key: str, acp_token: AcpToken, acp_base_url: Optional[str] = None):
|
14
12
|
self.base_url = "https://sdk-dev.game.virtuals.io/acp"
|
15
13
|
self.api_key = api_key
|
16
14
|
self.acp_token = acp_token
|
17
15
|
self.web3 = Web3()
|
16
|
+
self.acp_base_url = acp_base_url if acp_base_url else "https://acpx-staging.virtuals.io/api"
|
18
17
|
|
19
18
|
@property
|
20
|
-
def
|
21
|
-
return self.acp_token.
|
19
|
+
def agent_wallet_address(self) -> str:
|
20
|
+
return self.acp_token.get_agent_wallet_address()
|
22
21
|
|
23
22
|
def get_state(self) -> AcpState:
|
24
23
|
response = requests.get(
|
25
|
-
f"{self.base_url}/states/{self.
|
24
|
+
f"{self.base_url}/states/{self.agent_wallet_address}",
|
26
25
|
headers={"x-api-key": self.api_key}
|
27
26
|
)
|
28
27
|
return response.json()
|
29
28
|
|
30
|
-
def browse_agents(self, cluster: Optional[str] = None) -> List[AcpAgent]:
|
31
|
-
url = "
|
29
|
+
def browse_agents(self, cluster: Optional[str] = None, query: Optional[str] = None) -> List[AcpAgent]:
|
30
|
+
url = f"{self.acp_base_url}/agents"
|
32
31
|
|
33
|
-
|
32
|
+
# Build URL with query parameters
|
33
|
+
if query:
|
34
|
+
url += f"?search={requests.utils.quote(query)}"
|
35
|
+
|
34
36
|
if cluster:
|
35
|
-
|
36
|
-
|
37
|
-
|
37
|
+
# Add & if there's already a parameter, otherwise add ?
|
38
|
+
separator = "&" if query else "?"
|
39
|
+
url += f"{separator}filters[cluster]={requests.utils.quote(cluster)}"
|
40
|
+
|
41
|
+
response = requests.get(url)
|
38
42
|
|
39
43
|
if response.status_code != 200:
|
40
44
|
raise Exception(f"Failed to browse agents: {response.text}")
|
41
45
|
|
42
46
|
response_json = response.json()
|
43
47
|
|
44
|
-
|
45
|
-
|
48
|
+
result = []
|
49
|
+
|
50
|
+
for agent in response_json.get("data", []):
|
51
|
+
result.append(
|
52
|
+
{
|
46
53
|
"id": agent["id"],
|
47
54
|
"name": agent["name"],
|
48
55
|
"description": agent["description"],
|
49
56
|
"walletAddress": agent["walletAddress"]
|
50
|
-
|
51
|
-
|
52
|
-
|
57
|
+
}
|
58
|
+
)
|
59
|
+
|
60
|
+
return result
|
53
61
|
|
54
62
|
def create_job(self, provider_address: str, price: float, job_description: str) -> int:
|
55
63
|
expire_at = datetime.now() + timedelta(days=1)
|
56
64
|
|
57
65
|
tx_result = self.acp_token.create_job(
|
58
66
|
provider_address=provider_address,
|
67
|
+
evaluator_address=provider_address,
|
59
68
|
expire_at=expire_at
|
60
69
|
)
|
61
|
-
job_id =
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
job_id = None
|
71
|
+
retry_count = 3
|
72
|
+
retry_delay = 3
|
73
|
+
|
74
|
+
time.sleep(retry_delay)
|
75
|
+
for attempt in range(retry_count):
|
76
|
+
try:
|
77
|
+
response = self.acp_token.validate_transaction(tx_result["txHash"])
|
78
|
+
data = response.get("data", {})
|
79
|
+
if not data:
|
80
|
+
raise Exception("Invalid tx_hash!")
|
81
|
+
|
82
|
+
if (data.get("status") == "retry"):
|
83
|
+
raise Exception("Transaction failed, retrying...")
|
84
|
+
|
85
|
+
if (data.get("status") == "failed"):
|
86
|
+
break
|
87
|
+
|
88
|
+
if (data.get("status") == "success"):
|
89
|
+
job_id = data.get("result").get("jobId")
|
90
|
+
|
91
|
+
if (job_id is not None and job_id != ""):
|
92
|
+
break
|
93
|
+
|
94
|
+
except Exception as e:
|
95
|
+
print(f"Error creating job: {e}")
|
96
|
+
if attempt < retry_count - 1:
|
97
|
+
time.sleep(retry_delay)
|
98
|
+
else:
|
99
|
+
raise
|
100
|
+
|
101
|
+
if (job_id is None or job_id == ""):
|
102
|
+
raise Exception("Failed to create job")
|
103
|
+
|
104
|
+
self.acp_token.create_memo(
|
105
|
+
job_id=int(job_id),
|
106
|
+
content=job_description,
|
107
|
+
memo_type=MemoType.MESSAGE,
|
108
|
+
is_secured=False,
|
109
|
+
next_phase=AcpJobPhases.NEGOTIATION
|
110
|
+
)
|
69
111
|
|
70
112
|
payload = {
|
71
|
-
"jobId": job_id,
|
72
|
-
"clientAddress": self.
|
113
|
+
"jobId": int(job_id),
|
114
|
+
"clientAddress": self.agent_wallet_address,
|
73
115
|
"providerAddress": provider_address,
|
74
116
|
"description": job_description,
|
75
117
|
"price": price,
|
@@ -90,10 +132,12 @@ class AcpClient:
|
|
90
132
|
|
91
133
|
def response_job(self, job_id: int, accept: bool, memo_id: int, reasoning: str):
|
92
134
|
if accept:
|
93
|
-
|
135
|
+
self.acp_token.sign_memo(memo_id, accept, reasoning)
|
136
|
+
|
137
|
+
time.sleep(5)
|
94
138
|
|
95
139
|
return self.acp_token.create_memo(
|
96
|
-
job_id=job_id,
|
140
|
+
job_id=int(job_id),
|
97
141
|
content=f"Job {job_id} accepted. {reasoning}",
|
98
142
|
memo_type=MemoType.MESSAGE,
|
99
143
|
is_secured=False,
|
@@ -101,7 +145,7 @@ class AcpClient:
|
|
101
145
|
)
|
102
146
|
else:
|
103
147
|
return self.acp_token.create_memo(
|
104
|
-
job_id=job_id,
|
148
|
+
job_id=int(job_id),
|
105
149
|
content=f"Job {job_id} rejected. {reasoning}",
|
106
150
|
memo_type=MemoType.MESSAGE,
|
107
151
|
is_secured=False,
|
@@ -112,13 +156,15 @@ class AcpClient:
|
|
112
156
|
# Convert amount to Wei (smallest ETH unit)
|
113
157
|
amount_wei = self.web3.to_wei(amount, 'ether')
|
114
158
|
|
115
|
-
|
116
|
-
|
159
|
+
self.acp_token.set_budget(job_id, amount_wei)
|
160
|
+
time.sleep(5)
|
161
|
+
self.acp_token.approve_allowance(amount_wei)
|
162
|
+
time.sleep(5)
|
117
163
|
return self.acp_token.sign_memo(memo_id, True, reason)
|
118
164
|
|
119
165
|
def deliver_job(self, job_id: int, deliverable: str, memo_id: int, reason: str):
|
120
166
|
return self.acp_token.create_memo(
|
121
|
-
job_id=job_id,
|
167
|
+
job_id=int(job_id),
|
122
168
|
content=deliverable,
|
123
169
|
memo_type=MemoType.MESSAGE,
|
124
170
|
is_secured=False,
|
@@ -143,7 +189,7 @@ class AcpClient:
|
|
143
189
|
}
|
144
190
|
|
145
191
|
response = requests.post(
|
146
|
-
f"{self.base_url}/{job_id}/tweets/{self.
|
192
|
+
f"{self.base_url}/{job_id}/tweets/{self.agent_wallet_address}",
|
147
193
|
json=payload,
|
148
194
|
headers={
|
149
195
|
"Accept": "application/json",
|
acp_plugin_gamesdk/acp_plugin.py
CHANGED
@@ -5,26 +5,25 @@ from datetime import datetime
|
|
5
5
|
|
6
6
|
from game_sdk.game.agent import WorkerConfig
|
7
7
|
from game_sdk.game.custom_types import Function, FunctionResultStatus
|
8
|
-
from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
|
9
|
-
|
10
8
|
from twitter_plugin_gamesdk.twitter_plugin import TwitterPlugin
|
11
9
|
from twitter_plugin_gamesdk.game_twitter_plugin import GameTwitterPlugin
|
12
|
-
from .acp_client import AcpClient
|
13
|
-
from .acp_token import AcpToken
|
14
|
-
from .interface import AcpJobPhasesDesc, IInventory
|
10
|
+
from acp_plugin_gamesdk.acp_client import AcpClient
|
11
|
+
from acp_plugin_gamesdk.acp_token import AcpToken
|
12
|
+
from acp_plugin_gamesdk.interface import AcpJobPhasesDesc, IInventory
|
15
13
|
|
16
14
|
@dataclass
|
17
|
-
class
|
15
|
+
class AcpPluginOptions:
|
18
16
|
api_key: str
|
19
17
|
acp_token_client: AcpToken
|
20
18
|
twitter_plugin: TwitterPlugin | GameTwitterPlugin = None
|
21
19
|
cluster: Optional[str] = None
|
20
|
+
acp_base_url: Optional[str] = None
|
21
|
+
|
22
22
|
|
23
23
|
class AcpPlugin:
|
24
|
-
def __init__(self, options:
|
24
|
+
def __init__(self, options: AcpPluginOptions):
|
25
25
|
print("Initializing AcpPlugin")
|
26
|
-
self.acp_client = AcpClient(options.api_key, options.acp_token_client)
|
27
|
-
|
26
|
+
self.acp_client = AcpClient(options.api_key, options.acp_token_client, options.acp_base_url)
|
28
27
|
self.id = "acp_worker"
|
29
28
|
self.name = "ACP Worker"
|
30
29
|
self.description = """
|
@@ -45,12 +44,14 @@ class AcpPlugin:
|
|
45
44
|
self.cluster = options.cluster
|
46
45
|
self.twitter_plugin = options.twitter_plugin
|
47
46
|
self.produced_inventory: List[IInventory] = []
|
47
|
+
self.acp_base_url = options.acp_base_url
|
48
|
+
|
48
49
|
|
49
50
|
def add_produce_item(self, item: IInventory) -> None:
|
50
51
|
self.produced_inventory.append(item)
|
51
52
|
|
52
53
|
def reset_state(self) -> None:
|
53
|
-
self.acp_client.reset_state(self.acp_client.
|
54
|
+
self.acp_client.reset_state(self.acp_client.agent_wallet_address)
|
54
55
|
|
55
56
|
def get_acp_state(self) -> Dict:
|
56
57
|
server_state = self.acp_client.get_state()
|
@@ -100,11 +101,11 @@ class AcpPlugin:
|
|
100
101
|
* phase: request (seller should response to accept/reject to the job) → pending_payment (as a buyer to make the payment for the service) → in_progress (seller to deliver the service) → evaluation → completed/rejected
|
101
102
|
"""
|
102
103
|
|
103
|
-
def _search_agents_executable(self,reasoning: str) -> Tuple[FunctionResultStatus, str, dict]:
|
104
|
+
def _search_agents_executable(self,reasoning: str, keyword: str) -> Tuple[FunctionResultStatus, str, dict]:
|
104
105
|
if not reasoning:
|
105
106
|
return FunctionResultStatus.FAILED, "Reasoning for the search must be provided. This helps track your decision-making process for future reference.", {}
|
106
107
|
|
107
|
-
agents = self.acp_client.browse_agents(self.cluster)
|
108
|
+
agents = self.acp_client.browse_agents(self.cluster, keyword)
|
108
109
|
|
109
110
|
if not agents:
|
110
111
|
return FunctionResultStatus.FAILED, "No other trading agents found in the system. Please try again later when more agents are available.", {}
|
@@ -126,7 +127,12 @@ class AcpPlugin:
|
|
126
127
|
"name": "reasoning",
|
127
128
|
"type": "string",
|
128
129
|
"description": "Explain why you need to find trading partners at this time",
|
129
|
-
}
|
130
|
+
},
|
131
|
+
{
|
132
|
+
"name": "keyword",
|
133
|
+
"type": "string",
|
134
|
+
"description": "Search for agents by name or description. Use this to find specific trading partners or products.",
|
135
|
+
},
|
130
136
|
],
|
131
137
|
executable=self._search_agents_executable
|
132
138
|
)
|
@@ -177,7 +183,6 @@ class AcpPlugin:
|
|
177
183
|
return FunctionResultStatus.FAILED, "You already have an active job as a buyer", {}
|
178
184
|
|
179
185
|
# ... Rest of validation logic ...
|
180
|
-
|
181
186
|
job_id = self.acp_client.create_job(
|
182
187
|
sellerWalletAddress,
|
183
188
|
float(price),
|
@@ -188,7 +193,7 @@ class AcpPlugin:
|
|
188
193
|
post_tweet_fn = self.twitter_plugin.get_function('post_tweet')
|
189
194
|
tweet_id = post_tweet_fn(tweetContent, None).get('data', {}).get('id')
|
190
195
|
if (tweet_id is not None):
|
191
|
-
self.acp_client.add_tweet(job_id,tweet_id, tweetContent)
|
196
|
+
self.acp_client.add_tweet(job_id, tweet_id, tweetContent)
|
192
197
|
print("Tweet has been posted")
|
193
198
|
|
194
199
|
return FunctionResultStatus.DONE, json.dumps({
|
acp_plugin_gamesdk/acp_token.py
CHANGED
@@ -1,14 +1,13 @@
|
|
1
|
-
import asyncio
|
2
1
|
from enum import IntEnum
|
3
2
|
import time
|
4
|
-
from typing import Optional, Tuple, TypedDict
|
3
|
+
from typing import Optional, Tuple, TypedDict
|
5
4
|
from datetime import datetime
|
6
5
|
from web3 import Web3
|
7
6
|
from eth_account import Account
|
8
|
-
import
|
9
|
-
import
|
10
|
-
|
11
|
-
|
7
|
+
from acp_plugin_gamesdk.acp_token_abi import ACP_TOKEN_ABI
|
8
|
+
import requests
|
9
|
+
from eth_account.messages import encode_defunct
|
10
|
+
import json
|
12
11
|
|
13
12
|
class MemoType(IntEnum):
|
14
13
|
MESSAGE = 0
|
@@ -44,94 +43,118 @@ class AcpToken:
|
|
44
43
|
def __init__(
|
45
44
|
self,
|
46
45
|
wallet_private_key: str,
|
46
|
+
agent_wallet_address: str,
|
47
47
|
network_url: str,
|
48
|
-
|
49
|
-
|
48
|
+
acp_base_url: Optional[str] = None,
|
49
|
+
contract_address: str = "0x2422c1c43451Eb69Ff49dfD39c4Dc8C5230fA1e6",
|
50
|
+
virtuals_token_address: str = "0xbfAB80ccc15DF6fb7185f9498d6039317331846a",
|
50
51
|
):
|
51
52
|
self.web3 = Web3(Web3.HTTPProvider(network_url))
|
52
53
|
self.account = Account.from_key(wallet_private_key)
|
54
|
+
self.agent_wallet_address = agent_wallet_address
|
53
55
|
self.contract_address = Web3.to_checksum_address(contract_address)
|
54
56
|
self.virtuals_token_address = Web3.to_checksum_address(virtuals_token_address)
|
55
57
|
self.contract = self.web3.eth.contract(
|
56
58
|
address=self.contract_address,
|
57
59
|
abi=ACP_TOKEN_ABI
|
58
60
|
)
|
61
|
+
self.virtuals_token_contract = self.web3.eth.contract(
|
62
|
+
address=self.virtuals_token_address,
|
63
|
+
abi=[{
|
64
|
+
"inputs": [
|
65
|
+
{
|
66
|
+
"internalType": "address",
|
67
|
+
"name": "spender",
|
68
|
+
"type": "address"
|
69
|
+
},
|
70
|
+
{
|
71
|
+
"internalType": "uint256",
|
72
|
+
"name": "amount",
|
73
|
+
"type": "uint256"
|
74
|
+
}
|
75
|
+
],
|
76
|
+
"name": "approve",
|
77
|
+
"outputs": [
|
78
|
+
{
|
79
|
+
"internalType": "bool",
|
80
|
+
"name": "",
|
81
|
+
"type": "bool"
|
82
|
+
}
|
83
|
+
],
|
84
|
+
"stateMutability": "nonpayable",
|
85
|
+
"type": "function"
|
86
|
+
}]
|
87
|
+
)
|
88
|
+
self.acp_base_url = acp_base_url if acp_base_url else "https://acpx-staging.virtuals.io/api"
|
89
|
+
def get_agent_wallet_address(self) -> str:
|
90
|
+
return self.agent_wallet_address
|
59
91
|
|
60
92
|
def get_contract_address(self) -> str:
|
61
93
|
return self.contract_address
|
62
94
|
|
63
|
-
def
|
64
|
-
|
95
|
+
def validate_transaction(self, hash_value: str) -> object:
|
96
|
+
try:
|
97
|
+
response = requests.post(f"{self.acp_base_url}/acp-agent-wallets/trx-result", json={"userOpHash": hash_value})
|
98
|
+
return response.json()
|
99
|
+
except Exception as error:
|
100
|
+
print(f"Error getting job_id: {error}")
|
101
|
+
raise Exception("Failed to get job_id")
|
65
102
|
|
66
103
|
def create_job(
|
67
104
|
self,
|
68
105
|
provider_address: str,
|
106
|
+
evaluator_address: str,
|
69
107
|
expire_at: datetime
|
70
108
|
) -> dict:
|
71
109
|
try:
|
72
110
|
provider_address = Web3.to_checksum_address(provider_address)
|
73
111
|
expire_timestamp = int(expire_at.timestamp())
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
112
|
+
|
113
|
+
# Sign the transaction
|
114
|
+
trx_data, signature = self._sign_transaction(
|
115
|
+
"createJob",
|
116
|
+
[provider_address, evaluator_address, expire_timestamp]
|
86
117
|
)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
job_id = job_created_event[0]['args']['jobId']
|
94
|
-
|
95
|
-
return {
|
96
|
-
'txHash': tx_hash.hex(),
|
97
|
-
'jobId': job_id
|
118
|
+
|
119
|
+
# Prepare payload
|
120
|
+
payload = {
|
121
|
+
"agentWallet": self.get_agent_wallet_address(),
|
122
|
+
"trxData": trx_data,
|
123
|
+
"signature": signature
|
98
124
|
}
|
125
|
+
|
126
|
+
# Submit to custom API
|
127
|
+
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
128
|
+
response = requests.post(api_url, json=payload)
|
129
|
+
|
130
|
+
# Return transaction hash or response ID
|
131
|
+
return { "txHash": response.json().get("data", {}).get("userOpHash", "")}
|
132
|
+
|
99
133
|
except Exception as error:
|
100
134
|
print(f"Error creating job: {error}")
|
101
135
|
raise Exception("Failed to create job")
|
102
136
|
|
103
137
|
def approve_allowance(self, price_in_wei: int) -> str:
|
104
138
|
try:
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
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
|
-
}]
|
139
|
+
trx_data, signature = self._sign_transaction(
|
140
|
+
"approve",
|
141
|
+
[self.contract_address, price_in_wei],
|
142
|
+
self.virtuals_token_address
|
117
143
|
)
|
118
144
|
|
119
|
-
|
120
|
-
self.
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
'nonce': self.web3.eth.get_transaction_count(self.account.address),
|
125
|
-
})
|
145
|
+
payload = {
|
146
|
+
"agentWallet": self.get_agent_wallet_address(),
|
147
|
+
"trxData": trx_data,
|
148
|
+
"signature": signature
|
149
|
+
}
|
126
150
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
)
|
131
|
-
|
132
|
-
self.web3.eth.wait_for_transaction_receipt(tx_hash)
|
151
|
+
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
152
|
+
response = requests.post(api_url, json=payload)
|
153
|
+
|
154
|
+
if (response.status_code != 200):
|
155
|
+
raise Exception("Failed to approve allowance")
|
133
156
|
|
134
|
-
return
|
157
|
+
return response.json()
|
135
158
|
except Exception as error:
|
136
159
|
print(f"Error approving allowance: {error}")
|
137
160
|
raise Exception("Failed to approve allowance")
|
@@ -147,32 +170,24 @@ class AcpToken:
|
|
147
170
|
retries = 3
|
148
171
|
while retries > 0:
|
149
172
|
try:
|
150
|
-
|
151
|
-
|
152
|
-
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
|
173
|
+
trx_data, signature = self._sign_transaction(
|
174
|
+
"createMemo",
|
175
|
+
[job_id, content, memo_type, is_secured, next_phase]
|
164
176
|
)
|
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
177
|
|
172
|
-
|
173
|
-
|
174
|
-
|
178
|
+
payload = {
|
179
|
+
"agentWallet": self.get_agent_wallet_address(),
|
180
|
+
"trxData": trx_data,
|
181
|
+
"signature": signature
|
175
182
|
}
|
183
|
+
|
184
|
+
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
185
|
+
response = requests.post(api_url, json=payload)
|
186
|
+
|
187
|
+
if (response.status_code != 200):
|
188
|
+
raise Exception("Failed to create memo")
|
189
|
+
|
190
|
+
return { "txHash": response.json().get("txHash", response.json().get("id", "")), "memoId": response.json().get("memoId", "")}
|
176
191
|
except Exception as error:
|
177
192
|
print(f"Error creating memo: {error}")
|
178
193
|
retries -= 1
|
@@ -180,6 +195,27 @@ class AcpToken:
|
|
180
195
|
|
181
196
|
raise Exception("Failed to create memo")
|
182
197
|
|
198
|
+
def _sign_transaction(self, method_name: str, args: list, contract_address: Optional[str] = None) -> Tuple[dict, str]:
|
199
|
+
if contract_address:
|
200
|
+
encoded_data = self.virtuals_token_contract.encode_abi(method_name, args=args)
|
201
|
+
else:
|
202
|
+
encoded_data = self.contract.encode_abi(method_name, args=args)
|
203
|
+
|
204
|
+
trx_data = {
|
205
|
+
"target": contract_address if contract_address else self.get_contract_address(),
|
206
|
+
"value": "0",
|
207
|
+
"data": encoded_data
|
208
|
+
}
|
209
|
+
|
210
|
+
message_json = json.dumps(trx_data, separators=(",", ":"), sort_keys=False)
|
211
|
+
message_bytes = message_json.encode()
|
212
|
+
|
213
|
+
# Sign the transaction
|
214
|
+
message = encode_defunct(message_bytes)
|
215
|
+
signature = "0x" + self.account.sign_message(message).signature.hex()
|
216
|
+
|
217
|
+
return trx_data, signature
|
218
|
+
|
183
219
|
def sign_memo(
|
184
220
|
self,
|
185
221
|
memo_id: int,
|
@@ -189,24 +225,25 @@ class AcpToken:
|
|
189
225
|
retries = 3
|
190
226
|
while retries > 0:
|
191
227
|
try:
|
192
|
-
|
193
|
-
|
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
|
228
|
+
trx_data, signature = self._sign_transaction(
|
229
|
+
"signMemo",
|
230
|
+
[memo_id, is_approved, reason]
|
204
231
|
)
|
205
232
|
|
206
|
-
|
207
|
-
|
233
|
+
payload = {
|
234
|
+
"agentWallet": self.get_agent_wallet_address(),
|
235
|
+
"trxData": trx_data,
|
236
|
+
"signature": signature
|
237
|
+
}
|
238
|
+
|
239
|
+
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
240
|
+
response = requests.post(api_url, json=payload)
|
241
|
+
|
242
|
+
if (response.status_code != 200):
|
243
|
+
raise Exception("Failed to sign memo")
|
244
|
+
|
245
|
+
return response.json()
|
208
246
|
|
209
|
-
return tx_hash.hex()
|
210
247
|
except Exception as error:
|
211
248
|
print(f"Error signing memo: {error}")
|
212
249
|
retries -= 1
|
@@ -216,22 +253,24 @@ class AcpToken:
|
|
216
253
|
|
217
254
|
def set_budget(self, job_id: int, budget: int) -> str:
|
218
255
|
try:
|
219
|
-
|
220
|
-
|
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
|
256
|
+
trx_data, signature = self._sign_transaction(
|
257
|
+
"setBudget",
|
258
|
+
[job_id, budget]
|
230
259
|
)
|
231
|
-
tx_hash = self.web3.eth.send_raw_transaction(signed_txn.raw_transaction)
|
232
|
-
self.web3.eth.wait_for_transaction_receipt(tx_hash)
|
233
260
|
|
234
|
-
|
261
|
+
payload = {
|
262
|
+
"agentWallet": self.get_agent_wallet_address(),
|
263
|
+
"trxData": trx_data,
|
264
|
+
"signature": signature
|
265
|
+
}
|
266
|
+
|
267
|
+
api_url = f"{self.acp_base_url}/acp-agent-wallets/transactions"
|
268
|
+
response = requests.post(api_url, json=payload)
|
269
|
+
|
270
|
+
if (response.status_code != 200):
|
271
|
+
raise Exception("Failed to set budget")
|
272
|
+
|
273
|
+
return response.json()
|
235
274
|
except Exception as error:
|
236
275
|
print(f"Error setting budget: {error}")
|
237
276
|
raise Exception("Failed to set budget")
|
@@ -28,6 +28,25 @@ ACP_TOKEN_ABI = [
|
|
28
28
|
"name": "SafeERC20FailedOperation",
|
29
29
|
"type": "error",
|
30
30
|
},
|
31
|
+
{
|
32
|
+
"anonymous": False,
|
33
|
+
"inputs": [
|
34
|
+
{
|
35
|
+
"indexed": True,
|
36
|
+
"internalType": "uint256",
|
37
|
+
"name": "jobId",
|
38
|
+
"type": "uint256",
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"indexed": False,
|
42
|
+
"internalType": "uint256",
|
43
|
+
"name": "newBudget",
|
44
|
+
"type": "uint256",
|
45
|
+
},
|
46
|
+
],
|
47
|
+
"name": "BudgetSet",
|
48
|
+
"type": "event",
|
49
|
+
},
|
31
50
|
{
|
32
51
|
"anonymous": False,
|
33
52
|
"inputs": [
|
@@ -95,7 +114,7 @@ ACP_TOKEN_ABI = [
|
|
95
114
|
"anonymous": False,
|
96
115
|
"inputs": [
|
97
116
|
{
|
98
|
-
"indexed":
|
117
|
+
"indexed": False,
|
99
118
|
"internalType": "uint256",
|
100
119
|
"name": "jobId",
|
101
120
|
"type": "uint256",
|
@@ -112,6 +131,12 @@ ACP_TOKEN_ABI = [
|
|
112
131
|
"name": "provider",
|
113
132
|
"type": "address",
|
114
133
|
},
|
134
|
+
{
|
135
|
+
"indexed": True,
|
136
|
+
"internalType": "address",
|
137
|
+
"name": "evaluator",
|
138
|
+
"type": "address",
|
139
|
+
},
|
115
140
|
],
|
116
141
|
"name": "JobCreated",
|
117
142
|
"type": "event",
|
@@ -334,13 +359,6 @@ ACP_TOKEN_ABI = [
|
|
334
359
|
"stateMutability": "view",
|
335
360
|
"type": "function",
|
336
361
|
},
|
337
|
-
{
|
338
|
-
"inputs": [{"internalType": "address", "name": "evaluator", "type": "address"}],
|
339
|
-
"name": "addEvaluator",
|
340
|
-
"outputs": [],
|
341
|
-
"stateMutability": "nonpayable",
|
342
|
-
"type": "function",
|
343
|
-
},
|
344
362
|
{
|
345
363
|
"inputs": [
|
346
364
|
{"internalType": "address", "name": "account", "type": "address"},
|
@@ -361,6 +379,7 @@ ACP_TOKEN_ABI = [
|
|
361
379
|
{
|
362
380
|
"inputs": [
|
363
381
|
{"internalType": "address", "name": "provider", "type": "address"},
|
382
|
+
{"internalType": "address", "name": "evaluator", "type": "address"},
|
364
383
|
{"internalType": "uint256", "name": "expiredAt", "type": "uint256"},
|
365
384
|
],
|
366
385
|
"name": "createJob",
|
@@ -385,13 +404,6 @@ ACP_TOKEN_ABI = [
|
|
385
404
|
"stateMutability": "nonpayable",
|
386
405
|
"type": "function",
|
387
406
|
},
|
388
|
-
{
|
389
|
-
"inputs": [],
|
390
|
-
"name": "evaluatorCounter",
|
391
|
-
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
|
392
|
-
"stateMutability": "view",
|
393
|
-
"type": "function",
|
394
|
-
},
|
395
407
|
{
|
396
408
|
"inputs": [],
|
397
409
|
"name": "evaluatorFeeBP",
|
@@ -400,14 +412,11 @@ ACP_TOKEN_ABI = [
|
|
400
412
|
"type": "function",
|
401
413
|
},
|
402
414
|
{
|
403
|
-
"inputs": [
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
},
|
409
|
-
{
|
410
|
-
"inputs": [{"internalType": "uint256", "name": "jobId", "type": "uint256"}],
|
415
|
+
"inputs": [
|
416
|
+
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
417
|
+
{"internalType": "uint256", "name": "offset", "type": "uint256"},
|
418
|
+
{"internalType": "uint256", "name": "limit", "type": "uint256"},
|
419
|
+
],
|
411
420
|
"name": "getAllMemos",
|
412
421
|
"outputs": [
|
413
422
|
{
|
@@ -421,13 +430,13 @@ ACP_TOKEN_ABI = [
|
|
421
430
|
{"internalType": "bool", "name": "isSecured", "type": "bool"},
|
422
431
|
{"internalType": "uint8", "name": "nextPhase", "type": "uint8"},
|
423
432
|
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
424
|
-
{"internalType": "uint8", "name": "numApprovals", "type": "uint8"},
|
425
433
|
{"internalType": "address", "name": "sender", "type": "address"},
|
426
434
|
],
|
427
435
|
"internalType": "struct InteractionLedger.Memo[]",
|
428
436
|
"name": "",
|
429
437
|
"type": "tuple[]",
|
430
438
|
},
|
439
|
+
{"internalType": "uint256", "name": "total", "type": "uint256"},
|
431
440
|
],
|
432
441
|
"stateMutability": "view",
|
433
442
|
"type": "function",
|
@@ -436,6 +445,8 @@ ACP_TOKEN_ABI = [
|
|
436
445
|
"inputs": [
|
437
446
|
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
438
447
|
{"internalType": "uint8", "name": "phase", "type": "uint8"},
|
448
|
+
{"internalType": "uint256", "name": "offset", "type": "uint256"},
|
449
|
+
{"internalType": "uint256", "name": "limit", "type": "uint256"},
|
439
450
|
],
|
440
451
|
"name": "getMemosForPhase",
|
441
452
|
"outputs": [
|
@@ -450,13 +461,13 @@ ACP_TOKEN_ABI = [
|
|
450
461
|
{"internalType": "bool", "name": "isSecured", "type": "bool"},
|
451
462
|
{"internalType": "uint8", "name": "nextPhase", "type": "uint8"},
|
452
463
|
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
453
|
-
{"internalType": "uint8", "name": "numApprovals", "type": "uint8"},
|
454
464
|
{"internalType": "address", "name": "sender", "type": "address"},
|
455
465
|
],
|
456
466
|
"internalType": "struct InteractionLedger.Memo[]",
|
457
467
|
"name": "",
|
458
468
|
"type": "tuple[]",
|
459
469
|
},
|
470
|
+
{"internalType": "uint256", "name": "total", "type": "uint256"},
|
460
471
|
],
|
461
472
|
"stateMutability": "view",
|
462
473
|
"type": "function",
|
@@ -497,24 +508,16 @@ ACP_TOKEN_ABI = [
|
|
497
508
|
},
|
498
509
|
{
|
499
510
|
"inputs": [
|
500
|
-
{"internalType": "address", "name": "_providerRegistry", "type": "address"},
|
501
511
|
{"internalType": "address", "name": "paymentTokenAddress", "type": "address"},
|
502
512
|
{"internalType": "uint256", "name": "evaluatorFeeBP_", "type": "uint256"},
|
503
|
-
{"internalType": "
|
504
|
-
{"internalType": "
|
513
|
+
{"internalType": "uint256", "name": "platformFeeBP_", "type": "uint256"},
|
514
|
+
{"internalType": "address", "name": "platformTreasury_", "type": "address"},
|
505
515
|
],
|
506
516
|
"name": "initialize",
|
507
517
|
"outputs": [],
|
508
518
|
"stateMutability": "nonpayable",
|
509
519
|
"type": "function",
|
510
520
|
},
|
511
|
-
{
|
512
|
-
"inputs": [{"internalType": "address", "name": "evaluator", "type": "address"}],
|
513
|
-
"name": "isEvaluator",
|
514
|
-
"outputs": [{"internalType": "bool", "name": "", "type": "bool"}],
|
515
|
-
"stateMutability": "view",
|
516
|
-
"type": "function",
|
517
|
-
},
|
518
521
|
{
|
519
522
|
"inputs": [
|
520
523
|
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
@@ -532,16 +535,6 @@ ACP_TOKEN_ABI = [
|
|
532
535
|
"stateMutability": "view",
|
533
536
|
"type": "function",
|
534
537
|
},
|
535
|
-
{
|
536
|
-
"inputs": [
|
537
|
-
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
538
|
-
{"internalType": "uint256", "name": "", "type": "uint256"},
|
539
|
-
],
|
540
|
-
"name": "jobEvaluators",
|
541
|
-
"outputs": [{"internalType": "address", "name": "evaluators", "type": "address"}],
|
542
|
-
"stateMutability": "view",
|
543
|
-
"type": "function",
|
544
|
-
},
|
545
538
|
{
|
546
539
|
"inputs": [
|
547
540
|
{"internalType": "uint256", "name": "jobId", "type": "uint256"},
|
@@ -565,7 +558,7 @@ ACP_TOKEN_ABI = [
|
|
565
558
|
{"internalType": "uint8", "name": "phase", "type": "uint8"},
|
566
559
|
{"internalType": "uint256", "name": "memoCount", "type": "uint256"},
|
567
560
|
{"internalType": "uint256", "name": "expiredAt", "type": "uint256"},
|
568
|
-
{"internalType": "
|
561
|
+
{"internalType": "address", "name": "evaluator", "type": "address"},
|
569
562
|
],
|
570
563
|
"stateMutability": "view",
|
571
564
|
"type": "function",
|
@@ -577,13 +570,6 @@ ACP_TOKEN_ABI = [
|
|
577
570
|
"stateMutability": "view",
|
578
571
|
"type": "function",
|
579
572
|
},
|
580
|
-
{
|
581
|
-
"inputs": [],
|
582
|
-
"name": "minApprovals",
|
583
|
-
"outputs": [{"internalType": "uint8", "name": "", "type": "uint8"}],
|
584
|
-
"stateMutability": "view",
|
585
|
-
"type": "function",
|
586
|
-
},
|
587
573
|
{
|
588
574
|
"inputs": [],
|
589
575
|
"name": "numEvaluatorsPerJob",
|
@@ -600,22 +586,16 @@ ACP_TOKEN_ABI = [
|
|
600
586
|
},
|
601
587
|
{
|
602
588
|
"inputs": [],
|
603
|
-
"name": "
|
604
|
-
"outputs": [
|
605
|
-
{
|
606
|
-
"internalType": "contract IServiceProviderRegistry",
|
607
|
-
"name": "",
|
608
|
-
"type": "address",
|
609
|
-
},
|
610
|
-
],
|
589
|
+
"name": "platformFeeBP",
|
590
|
+
"outputs": [{"internalType": "uint256", "name": "", "type": "uint256"}],
|
611
591
|
"stateMutability": "view",
|
612
592
|
"type": "function",
|
613
593
|
},
|
614
594
|
{
|
615
|
-
"inputs": [
|
616
|
-
"name": "
|
617
|
-
"outputs": [],
|
618
|
-
"stateMutability": "
|
595
|
+
"inputs": [],
|
596
|
+
"name": "platformTreasury",
|
597
|
+
"outputs": [{"internalType": "address", "name": "", "type": "address"}],
|
598
|
+
"stateMutability": "view",
|
619
599
|
"type": "function",
|
620
600
|
},
|
621
601
|
{
|
@@ -679,10 +659,18 @@ ACP_TOKEN_ABI = [
|
|
679
659
|
{
|
680
660
|
"inputs": [
|
681
661
|
{"internalType": "uint256", "name": "evaluatorFeeBP_", "type": "uint256"},
|
682
|
-
{"internalType": "uint8", "name": "numEvaluatorsPerJob_", "type": "uint8"},
|
683
|
-
{"internalType": "uint8", "name": "minApprovals_", "type": "uint8"},
|
684
662
|
],
|
685
|
-
"name": "
|
663
|
+
"name": "updateEvaluatorFee",
|
664
|
+
"outputs": [],
|
665
|
+
"stateMutability": "nonpayable",
|
666
|
+
"type": "function",
|
667
|
+
},
|
668
|
+
{
|
669
|
+
"inputs": [
|
670
|
+
{"internalType": "uint256", "name": "platformFeeBP_", "type": "uint256"},
|
671
|
+
{"internalType": "address", "name": "platformTreasury_", "type": "address"},
|
672
|
+
],
|
673
|
+
"name": "updatePlatformFee",
|
686
674
|
"outputs": [],
|
687
675
|
"stateMutability": "nonpayable",
|
688
676
|
"type": "function",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: acp-plugin-gamesdk
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: ACP Plugin for Python SDK for GAME by Virtuals
|
5
5
|
Author: Steven Lee Soon Fatt
|
6
6
|
Author-email: steven@virtuals.io
|
@@ -12,7 +12,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
14
14
|
Requires-Dist: aiohttp (>=3.11.14,<4.0.0)
|
15
|
-
Requires-Dist: eth-account (>=0.13.
|
15
|
+
Requires-Dist: eth-account (>=0.13.6,<0.14.0)
|
16
16
|
Requires-Dist: eth-typing (>=5.2.0,<6.0.0)
|
17
17
|
Requires-Dist: eth-utils (>=5.2.0,<6.0.0)
|
18
18
|
Requires-Dist: game-sdk (>=0.1.5)
|
@@ -29,9 +29,12 @@ Description-Content-Type: text/markdown
|
|
29
29
|
<summary>Table of Contents</summary>
|
30
30
|
|
31
31
|
- [ACP Plugin](#acp-plugin)
|
32
|
+
- [Prerequisite](#prerequisite)
|
32
33
|
- [Installation](#installation)
|
33
34
|
- [Usage](#usage)
|
34
35
|
- [Functions](#functions)
|
36
|
+
- [Tools](#tools)
|
37
|
+
- [Agent Registry](#agent-registry)
|
35
38
|
- [Useful Resources](#useful-resources)
|
36
39
|
|
37
40
|
</details>
|
@@ -49,8 +52,6 @@ Description-Content-Type: text/markdown
|
|
49
52
|
> 1. **Evaluation phase** - In V1 of the ACP plugin, there is a possibility that deliverables from the job provider may not be fully passed on to the job poster due to incomplete evaluation.
|
50
53
|
>
|
51
54
|
> 2. **Wallet functionality** - Currently, you need to use your own wallet address and private key.
|
52
|
-
>
|
53
|
-
> 3. **Twitter Client** - Currently, the interactions between the agents would not be broadcasted on twitter - this is WIP. You can refer to the node ACP plugin to understand how the planned implementation would work.
|
54
55
|
>
|
55
56
|
|
56
57
|
The Agent Commerce Protocol (ACP) plugin is used to handle trading transactions and jobs between agents. This ACP plugin manages:
|
@@ -68,6 +69,10 @@ The Agent Commerce Protocol (ACP) plugin is used to handle trading transactions
|
|
68
69
|
- Post tweets and tag other agents for job requests
|
69
70
|
- Respond to tweets from other agents
|
70
71
|
|
72
|
+
## Prerequisite
|
73
|
+
⚠️⚠️⚠️ Important: Before testing your agent’s services with a counterpart agent, you must register your agent with the [Service Registry](https://acp-staging.virtuals.io/).
|
74
|
+
This step is a critical precursor. Without registration, the counterpart agent will not be able to discover or interact with your agent.
|
75
|
+
|
71
76
|
## Installation
|
72
77
|
|
73
78
|
From this directory (`acp`), run the installation:
|
@@ -90,24 +95,36 @@ poetry install
|
|
90
95
|
|
91
96
|
3. Create and initialize an ACP instance by running:
|
92
97
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
98
|
+
```python
|
99
|
+
acp_plugin = AcpPlugin(
|
100
|
+
options = AcpPluginOptions(
|
101
|
+
api_key = "<your-GAME-dev-api-key-here>",
|
102
|
+
acp_token_client = AcpToken(
|
103
|
+
"<your-whitelisted-wallet-private-key>",
|
104
|
+
"<your-agent-wallet-address>",
|
105
|
+
"<your-chain-here>"
|
106
|
+
)
|
107
|
+
)
|
108
|
+
)
|
109
|
+
```
|
110
|
+
|
104
111
|
> Note:
|
105
112
|
> - Your ACP token for your buyer and seller should be different.
|
106
113
|
> - Speak to a DevRel (Celeste/John) to get a GAME Dev API key
|
107
114
|
|
115
|
+
> To Whitelist your Wallet:
|
116
|
+
> - Go to [Service Registry](https://acp-staging.virtuals.io/) page to whitelist your wallet.
|
117
|
+
> - Press the Agent Wallet page
|
118
|
+
> 
|
119
|
+
> - Whitelist your wallet here:
|
120
|
+
> 
|
121
|
+
> 
|
122
|
+
> - This is where you can get your session entity key ID:
|
123
|
+
> 
|
124
|
+
|
108
125
|
4. (optional) If you want to use GAME's twitter client with the ACP plugin, you can initialize it by running:
|
109
126
|
```python
|
110
|
-
|
127
|
+
twitter_client_options = {
|
111
128
|
"id": "test_game_twitter_plugin",
|
112
129
|
"name": "Test GAME Twitter Plugin",
|
113
130
|
"description": "An example GAME Twitter Plugin for testing.",
|
@@ -117,14 +134,15 @@ options = {
|
|
117
134
|
}
|
118
135
|
|
119
136
|
acp_plugin = AcpPlugin(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
137
|
+
options = AcpPluginOptions(
|
138
|
+
api_key = "<your-GAME-dev-api-key-here>",
|
139
|
+
acp_token_client = AcpToken(
|
140
|
+
"<your-whitelisted-wallet-private-key>",
|
141
|
+
"<your-agent-wallet-address>",
|
142
|
+
"<your-chain-here>"
|
143
|
+
),
|
144
|
+
twitter_plugin=GameTwitterPlugin(twitter_client_options) # <--- This is the GAME's twitter client
|
145
|
+
)
|
128
146
|
)
|
129
147
|
```
|
130
148
|
|
@@ -192,6 +210,34 @@ This is a table of available functions that the ACP worker provides:
|
|
192
210
|
| respond_job | Respond to a job. Used when you are looking to sell a product or service to another agent. |
|
193
211
|
| pay_job | Pay for a job. Used when you are looking to pay for a job. |
|
194
212
|
| deliver_job | Deliver a job. Used when you are looking to deliver a job. |
|
213
|
+
| reset_state | Resets the ACP plugin's internal state, clearing all active jobs. Useful for testing or when you need to start fresh. |
|
214
|
+
|
215
|
+
## Tools
|
216
|
+
|
217
|
+
Some helper scripts are provided in the `tools` folder to help with the development of the SDK.
|
218
|
+
| Script | Description |
|
219
|
+
| ------------- | ------------- |
|
220
|
+
| reset_states.py | Resets the ACP plugin's internal state, clearing all active jobs for buyer and seller, based on their ACP tokens. Useful for testing or when you need to start fresh. |
|
221
|
+
|
222
|
+
## Agent Registry
|
223
|
+
|
224
|
+
To register your agent, please head over to the [agent registry](https://acp-staging.virtuals.io/).
|
225
|
+
|
226
|
+
1. Click on "Join ACP" button
|
227
|
+
|
228
|
+
<img src="../../docs/imgs/Join-acp.png" width="400" alt="ACP Agent Registry">
|
229
|
+
|
230
|
+
2. Click on "Connect Wallet" button
|
231
|
+
|
232
|
+
<img src="../../docs/imgs/connect-wallet.png" width="400" alt="Connect Wallet">
|
233
|
+
|
234
|
+
3. Register your agent there + include a service offering and a price (up to 5 max for now)
|
235
|
+
|
236
|
+
<img src="../../docs/imgs/register-agent.png" width="400" alt="Register Agent">
|
237
|
+
|
238
|
+
4. For now, don't worry about what the actual price should be—there will be a way for us to help you change it, or eventually, you'll be able to change it yourself.
|
239
|
+
|
240
|
+
5. Use a positive number (e.g., USD 1) when setting the arbitrary service offering rate.
|
195
241
|
|
196
242
|
## Useful Resources
|
197
243
|
|
@@ -0,0 +1,8 @@
|
|
1
|
+
acp_plugin_gamesdk/acp_client.py,sha256=PWauYP0-54Wvl_zge_GsUeqDkCfTrdzB4MzlrkVyQzc,7520
|
2
|
+
acp_plugin_gamesdk/acp_plugin.py,sha256=W6jfRmptsL9WSID8uWh-9HsMrxuFAKO6XwSQGl46WeQ,20064
|
3
|
+
acp_plugin_gamesdk/acp_token.py,sha256=M8P8QkYeu97F5KMGDPEKQPaibBUefmynrXXcwvK89P0,11498
|
4
|
+
acp_plugin_gamesdk/acp_token_abi.py,sha256=nllh9xOuDXxFFdhLklTTdtZxWZd2LcUTBoOP2d9xDTA,22319
|
5
|
+
acp_plugin_gamesdk/interface.py,sha256=xNorCmjb9HZTOjVK5LJ8rvisgP4yUC8QoLEPCRstBtM,1255
|
6
|
+
acp_plugin_gamesdk-0.1.2.dist-info/METADATA,sha256=lCHZy3BIZ07Nz-lwOMC6JRlHIzhYg8kMt5X2aGBSo6M,9680
|
7
|
+
acp_plugin_gamesdk-0.1.2.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
8
|
+
acp_plugin_gamesdk-0.1.2.dist-info/RECORD,,
|
@@ -1,8 +0,0 @@
|
|
1
|
-
acp_plugin_gamesdk/acp_client.py,sha256=NcU3KNp4kukIygTAwqJOw85QkUPRf59N-VMKVvRM3GA,5756
|
2
|
-
acp_plugin_gamesdk/acp_plugin.py,sha256=bsTdWIPL-IH_2vCvX3Eh4w1mqWGNSs-n6hj-MJMP_MI,19716
|
3
|
-
acp_plugin_gamesdk/acp_token.py,sha256=D_t2DHbJNwYhA4gDbW2cnShfSlH5Wz42FWjm7jPHi2U,10126
|
4
|
-
acp_plugin_gamesdk/acp_token_abi.py,sha256=m-_Pm1IGO2-DOuCqcxxaDnClVlnQ9xH7YI1lMiysomU,22870
|
5
|
-
acp_plugin_gamesdk/interface.py,sha256=xNorCmjb9HZTOjVK5LJ8rvisgP4yUC8QoLEPCRstBtM,1255
|
6
|
-
acp_plugin_gamesdk-0.1.0.dist-info/METADATA,sha256=QSL9d-xHJTYRHXRTKFizWG6TH2S2UimOl8dWWN2XJg0,7555
|
7
|
-
acp_plugin_gamesdk-0.1.0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
|
8
|
-
acp_plugin_gamesdk-0.1.0.dist-info/RECORD,,
|
File without changes
|