11from contextlib import asynccontextmanager
22from decimal import Decimal
3+ from typing import Annotated
34
45import click
56from ape .cli import ConnectedProviderCommand , account_option , network_option , verbosity_option
7+ from ape .types import AddressType
68from ape_tokens import Token , tokens
9+ from pydantic import Field
710
811from uniswap_sdk import Uniswap
912
@@ -168,8 +171,10 @@ async def lifespan(server):
168171
169172 # TODO: Move this to ape-tokens?
170173 @server .tool ()
171- async def get_token_balance (token : str ) -> Decimal :
172- """Get the token balance of the user's account."""
174+ async def get_token_balance (
175+ token : Annotated [str | AddressType , Field (description = "The token symbol or address" )],
176+ ) -> Decimal :
177+ """Get the balance of `token` in the user's account."""
173178
174179 if token in ("ether" , "ETH" ):
175180 return account .balance * Decimal ("1e-18" )
@@ -183,24 +188,94 @@ async def get_token_balance(token: str) -> Decimal:
183188 )
184189
185190 @server .tool ()
186- async def get_price (ctx : Context , base : str , quote : str ) -> Decimal :
187- """Get the current price of BASE in terms of QUOTE."""
188- uni = ctx .request_context .lifespan_context
191+ async def get_price (
192+ ctx : Context ,
193+ base : Annotated [
194+ str | AddressType ,
195+ Field (description = "The token symbol or address you want to know the price of" ),
196+ ],
197+ quote : Annotated [
198+ str | AddressType ,
199+ Field (description = "The token symbol or address which the price will be expressed" ),
200+ ],
201+ ) -> Decimal :
202+ """
203+ Returns the current exchange rate between two tokens, `base` and `quote`, as observed
204+ across all relevant markets in the Uniswap protocol. This price reflects the starting rate
205+ at which a trade on Uniswap will begin, and it does not include slippage or market impact
206+ from conducting an actual trade. due to the mechanics of the Uniswap AMM model.
207+
208+ **Important Notes**:
209+ 1. It is only intended to use this price as a reference.
210+ 2. The number will be returned as a decimal value, reflecting the precision of the market
211+ price. Do not scale or re-interpret this number.
212+ 3. The number should be interpretted as being the number of `quote` tokens that equals
213+ exactly 1 `base` token by the current market, or in the context of `quote` per `base`.
214+ """
189215
216+ uni = ctx .request_context .lifespan_context
190217 return uni .price (base , quote )
191218
192219 @server .tool ()
193220 async def swap (
194221 ctx : Context ,
195- have : str ,
196- want : str ,
197- amount_in : Decimal | None = None ,
198- max_amount_in : Decimal | None = None ,
199- amount_out : Decimal | None = None ,
200- min_amount_out : Decimal | None = None ,
201- slippage : Decimal | None = None ,
222+ have : Annotated [
223+ str | AddressType ,
224+ Field (description = "The token symbol or address you want to sell" ),
225+ ],
226+ want : Annotated [
227+ str | AddressType ,
228+ Field (description = "The token symbol or address you want to buy" ),
229+ ],
230+ amount_in : Annotated [
231+ Decimal | None ,
232+ Field (
233+ description = "The amount of `have` tokens you want to sell."
234+ " Leave empty if using `amount_out`."
235+ ),
236+ ] = None ,
237+ max_amount_in : Annotated [
238+ Decimal | None ,
239+ Field (
240+ description = "The maximum amount of `have` tokens you are willing to sell."
241+ " Leave empty to auto-compute this amount when `amount_out` is provided."
242+ ),
243+ ] = None ,
244+ amount_out : Annotated [
245+ Decimal | None ,
246+ Field (
247+ description = "The amount of `want` tokens you want to buy."
248+ " Leave empty if using `amount_in`."
249+ ),
250+ ] = None ,
251+ min_amount_out : Annotated [
252+ Decimal | None ,
253+ Field (
254+ description = "The minimum amount of `want` tokens you want to buy."
255+ " Leave empty to auto-compute this amount when `amount_in` is provided."
256+ ),
257+ ] = None ,
258+ slippage : Annotated [
259+ Decimal | None ,
260+ Field (
261+ description = """
262+ The maximum change in equilibrium price you are willing to accept.
263+ Quantity is a value-less ratio, convert to a ratio if user specifies a percent.
264+ Leave empty to use the default of `0.005` (0.5%),
265+ or when `max_amount_in`/`min_amount_out` are provided.
266+ """
267+ ),
268+ ] = None ,
202269 ) -> str :
203- """Swap HAVE for WANT using the configured swap options."""
270+ """
271+ Performs a token swap, converting an amount of have tokens into want tokens. This function
272+ is designed to execute trades on-chain and accounts for real-world dynamics such as
273+ slippage and market impact.
274+
275+ **Important Note**: This function will account for market shifts, which can be set by the
276+ `slippage`, `max_amount_in`, or `min_amount_out` parameters. Use these to protect against
277+ adverse market changes while executing the user's order.
278+ """
204279
205280 # NOTE: FastMCP doesn't actually support `Decimal` auto-casting yet
206281 if isinstance (amount_in , str ):
0 commit comments