| name | investment |
| description | Invest in and redeem from GRVT vaults. Use when the user wants to manage vault investments, check vault summaries, or view redemption queues. |
GRVT Vault Investment
When to Use
Use this skill when the user wants to:
- Invest funds into a GRVT vault
- Redeem (withdraw) from a vault
- Cancel a pending redemption
- Check vault investor summary (LP tokens, PnL, equity)
- View vault redemption queue (for vault managers)
- View vault investor history
- List available vaults
Prerequisites
pip install grvt-pysdk
GRVT has two types of API keys. Vault operations require both:
- Funding API Key — for invest, redeem, and vault queries
- Trading API Key — needed to initialize GrvtCcxt (provides trading_account_id)
export GRVT_TRADING_API_KEY="<Trading API Key from GRVT exchange UI>"
export GRVT_TRADING_PRIVATE_KEY="<private key associated with Trading API Key>"
export GRVT_FUNDING_API_KEY="<Funding API Key from GRVT exchange UI>"
export GRVT_FUNDING_PRIVATE_KEY="<private key associated with Funding API Key>"
export GRVT_ENV="testnet"
Both key types can be created at:
SDK Setup
import os
import requests
from pathlib import Path
from pysdk.grvt_ccxt import GrvtCcxt
from pysdk.grvt_ccxt_env import GrvtEnv
env_file = Path(".env")
if env_file.exists():
for line in env_file.read_text().splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, _, value = line.partition("=")
os.environ.setdefault(key.strip(), value.strip())
env = os.getenv("GRVT_ENV", "testnet")
base = "edge.testnet.grvt.io" if env == "testnet" else "edge.grvt.io"
trading_login = requests.post(
f"https://{base}/auth/api_key/login",
json={"api_key": os.getenv("GRVT_TRADING_API_KEY")},
).json()
funding_login = requests.post(
f"https://{base}/auth/api_key/login",
json={"api_key": os.getenv("GRVT_FUNDING_API_KEY")},
).json()
funding_account_address = funding_login["funding_account_address"]
api = GrvtCcxt(
env=GrvtEnv.TESTNET if env == "testnet" else GrvtEnv.PRODUCTION,
parameters={
"api_key": os.getenv("GRVT_FUNDING_API_KEY"),
"trading_account_id": trading_login["sub_account_id"],
"private_key": os.getenv("GRVT_FUNDING_PRIVATE_KEY"),
},
)
trades_base = "trades.testnet.grvt.io" if env == "testnet" else "trades.grvt.io"
Discover Vaults
Vault IDs are numeric (chain_vault_id). Discover available vaults via the REST API:
def list_vaults(env="testnet"):
"""List available investment strategies (vaults) with their numeric IDs."""
base = "edge.testnet.grvt.io" if env == "testnet" else "edge.grvt.io"
resp = requests.get(f"https://{base}/api/v1/investment/strategies").json()
return resp.get("strategies", [])
Each strategy includes:
chain_vault_id — numeric vault ID (use this for invest/redeem)
name, manager_name, status (active/closed/delisted)
type — "launchpad" or "standard"
management_fee, performance_fee (in basis points, divide by 1e6 for %)
valuation_cap — max vault size
min_redemption_period, max_redemption_period (seconds)
categories — strategy categories (e.g. "Directional", "Machine learning-driven")
estimated_stats — apr, max_drawdown, sharpe_ratio (if enabled)
Investor Summary
result = api._auth_and_post(
f"https://{trades_base}/full/v1/vault_investor_summary",
{"main_account_id": funding_account_address, "vault_id": "<numeric_vault_id>"},
)
Vault Manager Investor History
history = api.fetch_vault_manager_investor_history(only_own_investments=False)
Vault Redemption Queue
queue = api.fetch_vault_redemption_queue()
Invest in a Vault
The SDK does not natively support vault invest. Use this helper with EIP-712 signing:
import time, random
from pysdk.grvt_ccxt_utils import get_EIP712_domain_data
from eth_account import Account
from eth_account.messages import encode_typed_data
CURRENCY_MAP = {"USD": 1, "USDC": 2, "USDT": 3, "ETH": 4, "BTC": 5}
CURRENCY_DECIMALS = {"USD": 6, "USDC": 6, "USDT": 6, "ETH": 18, "BTC": 8}
def vault_invest(api, vault_id, currency, amount, funding_account_address, private_key):
"""Invest funds into a GRVT vault.
Args:
api: GrvtCcxt instance (authenticated with Funding API Key)
vault_id: numeric vault ID (chainVaultID from list_vaults)
currency: "USDT", "USDC", etc.
amount: human-readable amount (e.g. 100 for 100 USDT)
funding_account_address: from login response
private_key: Funding API Key private key
"""
decimals = CURRENCY_DECIMALS[currency]
num_tokens = int(amount * (10 ** decimals))
expiration_ns = int((time.time() + 86400) * 1_000_000_000)
nonce = random.randint(1, 2**32 - 1)
domain_data = get_EIP712_domain_data(api.env)
types = {
"VaultInvest": [
{"name": "vaultID", "type": "uint64"},
{"name": "accountID", "type": "address"},
{"name": "tokenCurrency", "type": "uint8"},
{"name": "numTokens", "type": "uint64"},
{"name": "nonce", "type": "uint32"},
{"name": "expiration", "type": "int64"},
]
}
message_data = {
"vaultID": int(vault_id),
"accountID": funding_account_address,
"tokenCurrency": CURRENCY_MAP[currency],
"numTokens": num_tokens,
"nonce": nonce,
"expiration": expiration_ns,
}
message = encode_typed_data(domain_data, types, message_data)
signed = Account.sign_message(message, private_key)
signer = Account.from_key(private_key)
payload = {
"main_account_id": funding_account_address,
"vault_id": str(vault_id),
"currency": currency,
"num_tokens": str(amount),
"signature": {
"signer": signer.address,
"r": hex(signed.r),
"s": hex(signed.s),
"v": signed.v,
"expiration": str(expiration_ns),
"nonce": nonce,
},
}
env_name = api.env.value
base = "trades.testnet.grvt.io" if "testnet" in env_name else "trades.grvt.io"
return api._auth_and_post(f"https://{base}/full/v1/vault_invest", payload)
Redeem from a Vault
def vault_redeem(api, vault_id, currency, num_lp_tokens, funding_account_address, private_key):
"""Redeem LP tokens from a GRVT vault.
Args:
api: GrvtCcxt instance (authenticated with Funding API Key)
vault_id: numeric vault ID (chainVaultID)
currency: "USDT", "USDC", etc.
num_lp_tokens: number of LP tokens to redeem (human-readable)
funding_account_address: from login response
private_key: Funding API Key private key
"""
decimals = CURRENCY_DECIMALS[currency]
num_tokens_raw = int(num_lp_tokens * (10 ** decimals))
expiration_ns = int((time.time() + 86400) * 1_000_000_000)
nonce = random.randint(1, 2**32 - 1)
domain_data = get_EIP712_domain_data(api.env)
types = {
"VaultRedeem": [
{"name": "vaultID", "type": "uint64"},
{"name": "tokenCurrency", "type": "uint8"},
{"name": "numLpTokens", "type": "uint64"},
{"name": "accountID", "type": "address"},
{"name": "nonce", "type": "uint32"},
{"name": "expiration", "type": "int64"},
]
}
message_data = {
"vaultID": int(vault_id),
"tokenCurrency": CURRENCY_MAP[currency],
"numLpTokens": num_tokens_raw,
"accountID": funding_account_address,
"nonce": nonce,
"expiration": expiration_ns,
}
message = encode_typed_data(domain_data, types, message_data)
signed = Account.sign_message(message, private_key)
signer = Account.from_key(private_key)
payload = {
"main_account_id": funding_account_address,
"vault_id": str(vault_id),
"currency": currency,
"num_tokens": str(num_lp_tokens),
"signature": {
"signer": signer.address,
"r": hex(signed.r),
"s": hex(signed.s),
"v": signed.v,
"expiration": str(expiration_ns),
"nonce": nonce,
},
}
env_name = api.env.value
base = "trades.testnet.grvt.io" if "testnet" in env_name else "trades.grvt.io"
return api._auth_and_post(f"https://{base}/full/v1/vault_redeem", payload)
Cancel a Pending Redemption
def vault_redeem_cancel(api, vault_id, funding_account_address):
"""Cancel a pending vault redemption."""
payload = {
"main_account_id": funding_account_address,
"vault_id": str(vault_id),
}
env_name = api.env.value
base = "trades.testnet.grvt.io" if "testnet" in env_name else "trades.grvt.io"
return api._auth_and_post(f"https://{base}/full/v1/vault_redeem_cancel", payload)
Important Notes
- Vault invest/redeem require a Funding API Key (not Trading)
vault_id must be the numeric chain_vault_id — discover it via list_vaults() REST endpoint
funding_account_address is returned from the Funding API Key login response
- Currency amounts use decimal precision: USDT/USDC = 6 decimals, ETH = 18, BTC = 8
- Redemptions may have a waiting period set by the vault manager
- Only one pending redemption per vault per investor at a time
- Some vaults may be closed/delisted or at capacity — check the error message