-
Notifications
You must be signed in to change notification settings - Fork 3
/
simple.py
135 lines (110 loc) · 4.21 KB
/
simple.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
from contextvars import ContextVar
from typing import Annotated, Optional
from eth_rpc import Account, PrivateKeyWallet
from eth_rpc.networks import Network, get_network_by_name
from eth_typing import HexAddress, HexStr
from typing_extensions import Doc
from emp_agents.implicits import Depends, inject
from emp_agents.models.protocol import SkillSet, onchain_action, tool_method
from ..network import NetworkOptions, NetworkSkill
# Context variable to store private key
_private_key: ContextVar[Optional[str]] = ContextVar("_private_key", default=None)
class SimpleWalletSkill(SkillSet):
"""A simple wallet tool that stores private key in memory using context vars"""
@staticmethod
def get_wallet() -> PrivateKeyWallet:
key = _private_key.get()
if key is None:
raise ValueError("No private key set")
return PrivateKeyWallet(private_key=HexStr(key))
@tool_method
@staticmethod
def create_wallet() -> str:
"""Create a new private key wallet"""
wallet = PrivateKeyWallet.create_new()
_private_key.set(wallet.private_key)
return (
f"Wallet created: {wallet.address} with private key: {wallet.private_key}"
)
@tool_method
@staticmethod
def set_private_key(
private_key: Annotated[str, Doc("The private key to set")]
) -> str:
"""Set the private key in the context"""
_private_key.set(private_key)
return "Private key set successfully"
@tool_method
@staticmethod
def get_private_key() -> str:
"""Get the private key from the context"""
key = _private_key.get()
if key is None:
return "No private key set"
return key
@tool_method
@staticmethod
def clear_private_key() -> str:
"""Clear the private key from the context"""
_private_key.set(None)
return "Private key cleared"
@tool_method
@staticmethod
def get_address() -> str:
"""Get the address of the wallet"""
key = _private_key.get()
if key is None:
return "No private key set"
wallet = PrivateKeyWallet(private_key=HexStr(key))
return wallet.address
@tool_method
@staticmethod
@inject
async def get_eth_balance(
address: Annotated[
HexAddress, Doc("The address to get the balance of")
] = Depends(get_address),
network: Annotated[
NetworkOptions | None,
Doc("The network to use"),
] = Depends(NetworkSkill.get_network_str),
) -> str:
"""Get the balance of the wallet in ETH"""
if network is None:
return "No network set, try setting the network first"
network_type: type[Network] = get_network_by_name(network)
balance = await Account[network_type].get_balance(address) # type: ignore[valid-type]
return f"Balance: {balance}"
@onchain_action
@staticmethod
@inject
async def transfer_eth(
recipient: Annotated[HexAddress, Doc("The address to transfer the ETH to")],
amount: Annotated[int, Doc("The amount of ETH to transfer, in wei")],
message: Annotated[
str | None,
Doc(
"Any data you want to include in the transfer. It will be utf-8 encoded"
),
] = None,
network: Annotated[
NetworkOptions | None,
Doc("The network to use"),
] = Depends(NetworkSkill.get_network_str),
) -> str:
"""Transfer ETH to a recipient"""
if network is None:
return "No network set, try setting the network first"
wallet = SimpleWalletSkill.get_wallet()
if wallet is None:
return "No wallet set, try creating a wallet first"
network_type: type[Network] = get_network_by_name(network)
balance = await Account[network_type].get_balance(wallet.address) # type: ignore[valid-type]
if message:
data = "0x" + message.encode("utf-8").hex()
else:
data = "0x"
if balance <= amount:
return "Insufficient balance"
tx = await wallet[network_type].transfer(recipient, amount, data=data)
return f"Transaction sent: {tx}"