Skip to content

Commit 7a15d0b

Browse files
feat: cross chain transfer service
1 parent 728a58c commit 7a15d0b

14 files changed

Lines changed: 1724 additions & 209 deletions

File tree

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import logging
2+
import threading
3+
4+
import sys
5+
sys.path.append("../../../")
6+
7+
from typing import Optional
8+
from dotenv import load_dotenv
9+
10+
from virtuals_acp.client import VirtualsACP
11+
from virtuals_acp.configs.configs import BASE_SEPOLIA_ACP_X402_CONFIG_V2
12+
from virtuals_acp.contract_clients.contract_client_v2 import ACPContractClientV2
13+
from virtuals_acp.env import EnvSettings
14+
from virtuals_acp.fare import Fare, FareAmount
15+
from virtuals_acp.job import ACPJob
16+
from virtuals_acp.memo import ACPMemo
17+
from virtuals_acp.models import ACPJobPhase, ChainConfig, MemoType
18+
19+
# Configure logging
20+
logging.basicConfig(
21+
level=logging.INFO,
22+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
23+
)
24+
logger = logging.getLogger("SellerAgent")
25+
26+
load_dotenv(override=True)
27+
28+
REJECT_JOB_IN_REQUEST_PHASE = False
29+
REJECT_JOB_IN_OTHER_PHASE = False
30+
SOURCE_TOKEN_ADDRESS = ""
31+
TARGET_TOKEN_ADDRESS = ""
32+
TARGET_CHAIN_ID = 97
33+
34+
config = BASE_SEPOLIA_ACP_X402_CONFIG_V2
35+
config.chains = [
36+
ChainConfig(
37+
chain_id=TARGET_CHAIN_ID,
38+
rpc_url="https://bsc-testnet-dataseed.bnbchain.org"
39+
)
40+
]
41+
42+
def seller():
43+
env = EnvSettings()
44+
45+
def on_new_task(job: ACPJob, memo_to_sign: Optional[ACPMemo] = None):
46+
logger.info(f"[on_new_task] Received job {job.id} (phase: {job.phase})")
47+
48+
if (
49+
job.phase == ACPJobPhase.REQUEST
50+
and memo_to_sign is not None
51+
and memo_to_sign.next_phase == ACPJobPhase.NEGOTIATION
52+
):
53+
logger.info(f"Responding to job {job.id} with requirement: {job.requirement}")
54+
if REJECT_JOB_IN_REQUEST_PHASE:
55+
job.reject("Job requirement does not meet agent capability")
56+
else:
57+
job.accept("Job requirement matches agent capability")
58+
59+
swappedToken = FareAmount(
60+
1,
61+
Fare.from_contract_address(
62+
TARGET_TOKEN_ADDRESS,
63+
config,
64+
TARGET_CHAIN_ID
65+
)
66+
)
67+
68+
job.create_payable_requirement(
69+
"Requesting token from client on destination chain",
70+
MemoType.PAYABLE_REQUEST,
71+
swappedToken,
72+
job.client_address,
73+
)
74+
75+
logger.info(f"Job {job.id} responded with {'rejected' if REJECT_JOB_IN_REQUEST_PHASE else 'accepted'}")
76+
77+
elif (
78+
job.phase == ACPJobPhase.TRANSACTION
79+
and memo_to_sign is not None
80+
and memo_to_sign.next_phase == ACPJobPhase.EVALUATION
81+
):
82+
# to cater cases where agent decide to reject job after payment has been made
83+
if REJECT_JOB_IN_OTHER_PHASE: # conditional check for job rejection logic
84+
reason = "Job requirement does not meet agent capability"
85+
logger.info(f"Rejecting job {job.id} with reason: {reason}")
86+
job.reject(reason)
87+
logger.info(f"Job {job.id} rejected")
88+
return
89+
90+
deliverable = FareAmount(
91+
1,
92+
Fare.from_contract_address(
93+
TARGET_TOKEN_ADDRESS,
94+
config,
95+
TARGET_CHAIN_ID
96+
)
97+
)
98+
logger.info(f"Delivering job {job.id} with deliverable {deliverable}")
99+
# job.deliver(deliverable)
100+
job.deliver_payable("Deliver swapped token on destination chain", deliverable)
101+
logger.info(f"Job {job.id} delivered")
102+
return
103+
104+
elif job.phase == ACPJobPhase.COMPLETED:
105+
logger.info(f"Job {job.id} completed")
106+
107+
elif job.phase == ACPJobPhase.REJECTED:
108+
logger.info(f"Job {job.id} rejected")
109+
110+
VirtualsACP(
111+
acp_contract_clients=ACPContractClientV2(
112+
wallet_private_key=env.WHITELISTED_WALLET_PRIVATE_KEY,
113+
agent_wallet_address=env.SELLER_AGENT_WALLET_ADDRESS,
114+
entity_id=env.SELLER_ENTITY_ID,
115+
config=config
116+
),
117+
on_new_task=on_new_task
118+
)
119+
120+
logger.info("Seller agent is running, waiting for new tasks...")
121+
threading.Event().wait()
122+
123+
124+
if __name__ == "__main__":
125+
seller()

0 commit comments

Comments
 (0)