Armor Crypto MCP
by armorwallet
Verified
- armor-crypto-mcp
- armor_crypto_mcp
import json
import os
from typing_extensions import List, Optional, TypedDict, Literal
import httpx
from dotenv import load_dotenv
load_dotenv()
BASE_API_URL = os.getenv("BASE_API_URL")
# Pydantic data models for API client
class WalletTokenPairs(TypedDict):
wallet: str
token: str
class WalletTokenBalance(TypedDict):
wallet: str
token: str
balance: float
class ConversionRequest(TypedDict):
input_amount: float
input_token: str
output_token: str
class ConversionResponse(TypedDict):
input_amount: float
input_token: str
output_token: str
output_amount: float
class SwapQuoteRequest(TypedDict):
from_wallet: str
input_token: str
output_token: str
input_amount: float
class SwapQuoteResponse(TypedDict):
id: str
wallet_address: str
input_token_symbol: str
input_token_address: str
output_token_symbol: str
output_token_address: str
input_amount: float
output_amount: float
slippage: float
class SwapTransactionRequest(TypedDict):
transaction_ids: str
class SwapTransactionResponse(TypedDict):
id: str
transaction_error: Optional[str]
transaction_url: str
input_amount: float
output_amount: float
status: str
buying_price: float
class WalletBalance(TypedDict):
mint_address: str
name: str
symbol: str
decimals: int
amount: float
usd_price: str # Using str since the API returns price as string
usd_amount: float
class WalletInfo(TypedDict):
id: str
name: str
is_archived: bool
public_address: str
class Wallet(WalletInfo): # All fields required
balances: List[WalletBalance]
class TokenDetailsRequest(TypedDict):
query: str
include_details: bool
class TokenDetailsResponse(TypedDict):
name: str
symbol: str
mint_address: Optional[str] # if include_details is true
decimals: Optional[int]
image: Optional[str]
holders: Optional[int]
jupiter: Optional[bool]
verified: Optional[bool]
liquidityUsd: Optional[float]
marketCapUsd: Optional[float]
priceUsd: Optional[float]
lpBurn: Optional[float]
market: Optional[str]
freezeAuthority: Optional[str]
mintAuthority: Optional[str]
poolAddress: Optional[str]
totalBuys: Optional[int]
totalSells: Optional[int]
totalTransactions: Optional[int]
volume: Optional[float]
volume_5m: Optional[float]
volume_15m: Optional[float]
volume_30m: Optional[float]
volume_1h: Optional[float]
volume_6h: Optional[float]
volume_12h: Optional[float]
volume_24h: Optional[float]
class GroupInfo(TypedDict):
id: str
name: str
is_archived: bool
class SingleGroupInfo(GroupInfo): # All fields required
wallets: List[WalletInfo]
class WalletArchiveOrUnarchiveResponse(TypedDict):
wallet_name: str
message: str
class CreateGroupResponse(TypedDict):
id: str
name: str
is_archived: bool
class AddWalletToGroupResponse(TypedDict):
wallet_name: str
group_name: str
message: str
class GroupArchiveOrUnarchiveResponse(TypedDict):
group: str
class RemoveWalletFromGroupResponse(TypedDict):
wallet: str
group: str
class UserWalletsAndGroupsResponse(TypedDict):
id: str
email: str
first_name: str
last_name: str
slippage: float
wallet_groups: List[GroupInfo]
wallets: List[WalletInfo]
class TransferTokensRequest(TypedDict):
from_wallet: str
to_wallet_address: str
token: str
amount: float
class TransferTokenResponse(TypedDict):
amount: float
from_wallet_address: str
to_wallet_address: str
token_address: str
transaction_url: str
message: str
class DCAOrderRequest(TypedDict):
wallet: str
input_token: str
output_token: str
amount: float
cron_expression: str
strategy_duration: int
strategy_duration_unit: Literal["MINUTE", "HOUR", "DAY", "WEEK", "MONTH", "YEAR"]
watch_field: str
token_watcher: str
delta_type: Literal["INCREASE", "DECREASE", "MOVE", "MOVE_DAILY", "AVERAGE_MOVE"]
delta_percentage: float
time_zone: str
class DCAWatcher(TypedDict):
watch_field: str
delta_type: Literal["INCREASE", "DECREASE", "MOVE", "MOVE_DAILY", "AVERAGE_MOVE"]
initial_value: float
delta_percentage: float
class DCAOrderResponse(TypedDict):
id: str
amount: float
investment_per_cycle: float
cycles_completed: int
total_cycles: int
human_readable_expiry: str
status: str
input_token_address: str
output_token_address: str
wallet_name: str
watchers: List[DCAWatcher]
dca_transactions: List[dict] # Can be further typed if transaction structure is known
class CancelDCAOrderRequest(TypedDict):
dca_order_id: str
class CancelDCAOrderResponse(TypedDict):
dca_order_id: str
status: str
class ArmorWalletAPIClient:
def __init__(self, access_token: str, base_api_url:str='https://app.armorwallet.ai/api/v1'):
self.base_api_url = base_api_url
self.access_token = access_token
async def _api_call(self, method: str, endpoint: str, payload: str = None) -> dict:
"""Utility function for API calls to the wallet.
It sets common headers and raises errors on non-2xx responses.
"""
url = f"{self.base_api_url}/{endpoint}"
print(url)
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.access_token}'
}
async with httpx.AsyncClient(timeout=30) as client:
response = await client.request(method, url, headers=headers, data=payload)
if response.status_code >= 400:
raise Exception(f"API Error {response.status_code}: {response.text}")
try:
return response.json()
except Exception:
return {"text": response.text}
async def get_wallet_token_balance(self, wallet_token_pairs: List[WalletTokenPairs]) -> List[WalletTokenBalance]:
"""Get balances from a list of wallet and token pairs.
"""
payload = json.dumps(wallet_token_pairs)
return await self._api_call("POST", "tokens/wallet-token-balance/", payload)
async def conversion_api(self, conversion_request: List[ConversionRequest]) -> List[ConversionResponse]:
"""Perform a token conversion."""
payload = json.dumps(conversion_request)
return await self._api_call("POST", "tokens/token-price-conversion/", payload)
async def swap_quote(self, swap_quote_requests: List[SwapQuoteRequest]) -> List[SwapQuoteResponse]:
"""Obtain a swap quote."""
payload = json.dumps(swap_quote_requests)
return await self._api_call("POST", "transactions/quote/", payload)
async def swap_transaction(self, swap_transaction_requests: List[SwapTransactionRequest]) -> List[SwapTransactionResponse]:
"""Execute the swap transactions."""
payload = json.dumps(swap_transaction_requests)
return await self._api_call("POST", "transactions/swap/", payload)
async def get_wallets_from_group(self, group_name: str) -> list:
"""Return the list of wallet names from the specified group."""
result = await self._api_call("GET", f"wallets/groups/{group_name}")
try:
return [wallet['name'] for wallet in result['wallets']]
except Exception:
return []
async def get_all_wallets(self) -> List[Wallet]:
"""Return all wallets with balances."""
return await self._api_call("GET", "wallets/")
async def get_token_details(self, token_details_requests: List[TokenDetailsRequest]) -> List[TokenDetailsResponse]:
"""Retrieve token details."""
payload = json.dumps(token_details_requests)
return await self._api_call("POST", "tokens/search-token/", payload)
async def list_groups(self) -> List[GroupInfo]:
"""Return a list of wallet groups."""
return await self._api_call("GET", "wallets/groups/")
async def list_single_group(self, group_name: str) -> SingleGroupInfo:
"""Return details for a single wallet group."""
return await self._api_call("GET", f"wallets/groups/{group_name}")
async def create_wallet(self, wallet_names_list: list) -> List[WalletInfo]:
"""Create new wallets given a list of wallet names."""
payload = json.dumps([{"name": wallet_name} for wallet_name in wallet_names_list])
return await self._api_call("POST", "wallets/", payload)
async def archive_wallets(self, wallet_names_list: list) -> List[WalletArchiveOrUnarchiveResponse]:
"""Archive the wallets specified in the list."""
payload = json.dumps([{"wallet": wallet_name} for wallet_name in wallet_names_list])
return await self._api_call("POST", "wallets/archive/", payload)
async def unarchive_wallets(self, wallet_names_list: list) -> List[WalletArchiveOrUnarchiveResponse]:
"""Unarchive the wallets specified in the list."""
payload = json.dumps([{"wallet": wallet_name} for wallet_name in wallet_names_list])
return await self._api_call("POST", "wallets/unarchive/", payload)
async def create_groups(self, group_names_list: list) -> List[CreateGroupResponse]:
"""Create new wallet groups given a list of group names."""
payload = json.dumps([{"name": group_name} for group_name in group_names_list])
return await self._api_call("POST", "wallets/groups/", payload)
async def add_wallets_to_group(self, group_name: str, wallet_names_list: list) -> List[AddWalletToGroupResponse]:
"""Add wallets to a specific group."""
payload = json.dumps([{"wallet": wallet_name, "group": group_name} for wallet_name in wallet_names_list])
return await self._api_call("POST", "wallets/add-wallet-to-group/", payload)
async def archive_wallet_group(self, group_names_list: list) -> List[GroupArchiveOrUnarchiveResponse]:
"""Archive the specified wallet groups."""
payload = json.dumps([{"group": group_name} for group_name in group_names_list])
return await self._api_call("POST", "wallets/group-archive/", payload)
async def unarchive_wallet_group(self, group_names_list: list) -> List[GroupArchiveOrUnarchiveResponse]:
"""Unarchive the specified wallet groups."""
payload = json.dumps([{"group": group_name} for group_name in group_names_list])
return await self._api_call("POST", "wallets/group-unarchive/", payload)
async def remove_wallets_from_group(self, group_name: str, wallet_names_list: list) -> List[RemoveWalletFromGroupResponse]:
"""Remove wallets from a group."""
payload = json.dumps([{"wallet": wallet_name, "group": group_name} for wallet_name in wallet_names_list])
return await self._api_call("POST", "wallets/remove-wallet-from-group/", payload)
async def get_user_wallets_and_groups_list(self) -> UserWalletsAndGroupsResponse:
"""Return user wallets and groups."""
return await self._api_call("GET", "users/me/")
async def transfer_tokens(self, transfer_tokens_requests: List[TransferTokensRequest]) -> List[TransferTokenResponse]:
"""Transfer tokens from one wallet to another."""
payload = json.dumps(transfer_tokens_requests)
return await self._api_call("POST", "transfers/transfer/", payload)
async def create_dca_order(self, dca_order_requests: List[DCAOrderRequest]) -> List[DCAOrderResponse]:
"""Create a DCA order."""
payload = json.dumps(dca_order_requests)
return await self._api_call("POST", "transactions/dca-order/", payload)
async def list_dca_orders(self) -> List[DCAOrderResponse]:
"""List all DCA orders."""
return await self._api_call("GET", "transactions/dca-order/")
async def cancel_dca_order(self, cancel_dca_order_requests: List[CancelDCAOrderRequest]) -> List[CancelDCAOrderResponse]:
"""Cancel a DCA order."""
payload = json.dumps(cancel_dca_order_requests)
return await self._api_call("POST", "transactions/dca-order/cancel/", payload)