
Ethena
- 206 installs
- 21 repo stars
- Updated August 3, 2026
- starchild-ai-agent/official-skills
Helps with ai & agent building tasks during AI-assisted development.
About
ethena is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- ethena
- AI & Agent Building
- AI-coding skill
Ethena by the numbers
- 206 all-time installs (skills.sh)
- +6 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #2,821 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/official-skills --skill ethenaAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 206 |
|---|---|
| repo stars | ★ 21 |
| Last updated | August 3, 2026 |
| Repository | starchild-ai-agent/official-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
⛔ HARD RULES (read first, always)
- ALWAYS call
ethena_ratebefore any on-chain operation — cooldown duration is live from chain, not a constant - NEVER call
wallet_transferdirectly — useethena_*native tools only (wallet_transfer triggers UI block in deny-all policy) - NEVER hardcode cooldown as "7 days" — actual value is fetched per request (currently ~24h)
- NEVER guess or assume the sUSDe/USDe exchange rate — call
ethena_rate - HARD LIMIT: max 5 tool calls per user request
- ON-CHAIN EXECUTION: after
ethena_*returns calldata, sign and broadcast viawallet_sign_transaction— NEVER skip or simulate
---
Ethena — USDe Staking
Ethena lets users stake USDe to receive sUSDe, an ERC4626 yield-bearing token. Rewards accrue from ETH staking + funding rate delta-hedging. sUSDe value only increases or stays flat — never decreases.
Key Contracts (Ethereum Mainnet)
| Contract | Address |
|---|---|
| USDe | 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3 |
| sUSDe (StakedUSDe) | 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497 |
| USDeSilo (cooldown) | 0x7FC7c91D556B400AFa565013E3F32055a0713425 |
Native Tools
| Tool | When to use | Returns |
|---|---|---|
ethena_rate() | Rate query, before any tx, cooldown check | rate, totalAssets, totalSupply, cooldown_seconds |
ethena_apy() | APY / yield / return question | apy_current, apy_7d, apy_30d, tvl_usd |
ethena_balance(wallet) | Balance check | usde_balance, susde_balance, cooldown_end_ts |
ethena_stake(amount, receiver) | Stake USDe | 2 tx calldata (approve + deposit) |
ethena_cooldown_start(amount) | Start unstake | cooldown tx calldata + live cooldown_hours |
ethena_unstake(receiver) | Claim after cooldown | unstake tx calldata |
End-to-End Execution — Sign & Broadcast
ethena_* tools return unsigned calldata. To actually execute on-chain:
ethena_stake / ethena_cooldown_start / ethena_unstake
↓ returns tx dict {to, data, value, chain_id}
wallet_sign_transaction(tx) ← wallet skill
↓ returns signed_tx hex
wallet_sign_transaction broadcast ← same call, auto-broadcastsStandard Flow (stake example)
Step 1 ethena_rate() # confirm rate + cooldown before tx
Step 2 ethena_stake("100", receiver) # returns [approve_tx, deposit_tx]
Step 3 wallet_sign_transaction(approve_tx, broadcast=True)
Step 4 wallet_sign_transaction(deposit_tx, broadcast=True)Standard Flow (unstake)
Step 1 ethena_rate() # get live cooldown duration
Step 2 ethena_cooldown_start("100") # returns cooldown_tx
Step 3 wallet_sign_transaction(cooldown_tx, broadcast=True)
Step 4 [wait cooldown_hours]
Step 5 ethena_unstake(receiver) # returns unstake_tx
Step 6 wallet_sign_transaction(unstake_tx, broadcast=True)Cross-skill dependency
Load wallet-policy skill first to confirm wildcard policy is active. wallet_sign_transaction is part of the wallet skill — it is available natively; no extra install needed.
Error handling
| Error | Cause | Fix |
|---|---|---|
| 403 on sign | Privy policy deny-all | Approve wildcard policy in Web UI |
| nonce conflict | Two txs broadcast too fast | Wait for Step 3 receipt before Step 4 |
| gas estimate fail | Approve not mined yet | Add 5s delay between approve and deposit |
Tool Routing — IF/THEN
IF "APY" OR "yield" OR "return" OR "收益率" OR "年化"
→ ethena_apy()
IF "rate" OR "price" OR "how much USDe per sUSDe" OR "汇率" OR "多少USDe"
→ ethena_rate()
IF "balance" OR "how much do I have" OR "余额"
→ ethena_balance(wallet_address)
IF "stake" OR "deposit" OR "质押" AND amount given
→ ethena_stake(amount, receiver)
IF "unstake" OR "redeem" OR "cooldown" OR "withdraw" OR "赎回" — starting flow
→ ethena_cooldown_start(amount)
IF "claim" OR "unstake" OR "withdraw" AND cooldown already done
→ ethena_unstake(receiver)
IF "how long" OR "cooldown duration" OR "等多久"
→ ethena_rate() ← returns cooldown_seconds live from chainFew-Shot Examples
ETH-01 — APY query
"sUSDe 现在的 APY 是多少?"
ethena_apy()
→ "当前 APY 3.72%,7日均值 3.68%"ETH-02 — Rate query
"1 sUSDe 能换多少 USDe?"
ethena_rate()
→ "1 sUSDe = 1.227675 USDe(totalAssets/totalSupply 实时计算)"ETH-03 — Balance
"我有多少 USDe 和 sUSDe?"
ethena_balance("0x...")
→ {usde_balance: 500.0, susde_balance: 81.46, susde_in_usde: 100.0}ETH-04 — Stake
"帮我质押 100 USDe"
ethena_rate() # get live rate for estimate
ethena_stake("100", receiver="0x...")
→ [{approve tx}, {deposit tx}] # execute in orderETH-04b — Stake end-to-end (sign & broadcast)
"帮我质押 100 USDe,直接执行到链上"
ethena_rate() # Step 1: confirm rate
ethena_stake("100", receiver="0x...") # Step 2: get calldata
→ [approve_tx, deposit_tx]
wallet_sign_transaction(approve_tx, broadcast=True) # Step 3
wallet_sign_transaction(deposit_tx, broadcast=True) # Step 4
→ tx_hash confirmedETH-06b — Unstake end-to-end (sign & broadcast)
"帮我开始赎回 50 sUSDe,直接发链上"
ethena_rate() # Step 1: get cooldown
ethena_cooldown_start("50") # Step 2: get calldata
→ {cooldown_tx, cooldown_hours: 24.0}
wallet_sign_transaction(cooldown_tx, broadcast=True) # Step 3
→ tx_hash confirmed, 等待 24h 后再调 ethena_unstakeETH-05 — Cooldown duration
"赎回要等多久?"
ethena_rate()
→ "cooldown_seconds: 86400 → 当前需等待 24 小时"ETH-06 — Start cooldown
"开始赎回 50 USDe 的 sUSDe"
ethena_cooldown_start("50")
→ {transaction: {...}, cooldown_hours: 24.0}ETH-07 — Claim after cooldown
"cooldown 到了,帮我提款"
ethena_unstake("0x...")
→ {transaction: {...}}ETH-08 — English yield query
"What's the current yield for staking USDe?"
ethena_apy()
→ "Current APY: 3.72%"How Staking Works
1. Stake: ethena_stake → approve USDe + deposit → receive sUSDe (ERC4626) 2. Yield: Protocol transfers USDe rewards every 8h, linearly vested over 8h (anti-sandwich) 3. Unstake: ethena_cooldown_start → wait cooldown (query live, currently ~24h) → ethena_unstake
sUSDe/USDe rate only goes up. Cooldown duration may change — always query ethena_rate.
After generating calldata via any ethena_* tool, always follow up with `wallet_sign_transaction(tx, broadcast=True)` to actually submit on-chain. Never stop at calldata generation.
Prerequisites — Wallet Policy
Before any on-chain operation, ensure a wildcard wallet policy is active (deny key export + allow *). Load the wallet-policy skill if needed.
Gotchas
- Cooldown is ~24h (not 7 days) — but always verify with
ethena_rate()as it can change via governance - No negative rewards — sUSDe value can only go up or stay flat
- Rewards every 8h — linearly vested, no sandwich opportunities
- sUSDe can be sold on DEXes without cooldown if liquidity exists
- EU/EEA restriction — sUSDe acquisition not offered in EU/EEA
"""
Ethena native tool exports.
Tools: ethena_rate, ethena_apy, ethena_balance, ethena_stake,
ethena_cooldown_start, ethena_unstake
"""
from __future__ import annotations
import json
import sys
import os
# ── stdlib path shim for skill tool runner ──────────────────────────────────
_SKILL_DIR = os.path.dirname(__file__)
if _SKILL_DIR not in sys.path:
sys.path.insert(0, _SKILL_DIR)
from core.http_client import proxied_get, proxied_post # noqa: E402
# ── Constants ────────────────────────────────────────────────────────────────
RPC_URL = "https://ethereum.publicnode.com"
SUSDE = "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"
USDE = "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3"
DEFILLAMA_POOL = "66985a81-9c51-46ca-9977-42b4fe7bc6df"
DEFILLAMA_CHART_URL = "https://yields.llama.fi/chart/66985a81-9c51-46ca-9977-42b4fe7bc6df"
DEFILLAMA_POOLS_URL = "https://yields.llama.fi/pools"
CALLER_ID = "chat:ethena-skill"
# Function selectors (keccak256 verified)
SEL_TOTAL_ASSETS = "0x01e1d114" # totalAssets()
SEL_TOTAL_SUPPLY = "0x18160ddd" # totalSupply()
SEL_COOLDOWN_DUR = "0x35269315" # cooldownDuration()
SEL_VESTING_AMT = "0x00728f76" # vestingAmount()
SEL_COOLDOWN_END = "0x525f3146" # cooldownEnd(address)
SEL_USDE_BALANCE = "0x70a08231" # balanceOf(address)
# ── Helpers ──────────────────────────────────────────────────────────────────
def _eth_call(to: str, data: str) -> str:
"""Single eth_call via sc-proxy RPC."""
resp = proxied_post(RPC_URL, json={
"jsonrpc": "2.0", "method": "eth_call",
"params": [{"to": to, "data": data}, "latest"], "id": 1
}, headers={"SC-CALLER-ID": CALLER_ID}, timeout=15)
return resp.json().get("result", "0x0")
def _to_int(hex_str: str) -> int:
if not hex_str or hex_str in ("0x", "0x0"):
return 0
return int(hex_str, 16)
def _encode_addr(addr: str) -> str:
return addr.lower().replace("0x", "").zfill(64)
# ── Tool: ethena_rate ────────────────────────────────────────────────────────
def ethena_rate() -> dict:
"""
Query real-time sUSDe/USDe exchange rate and cooldown duration from chain.
Returns:
rate (float): how many USDe per 1 sUSDe
total_assets (float): total USDe in vault
total_supply (float): total sUSDe minted
cooldown_seconds (int): current cooldown period in seconds
cooldown_hours (float): cooldown in hours
vesting_amount (float): USDe rewards currently vesting
"""
ta_hex = _eth_call(SUSDE, SEL_TOTAL_ASSETS)
ts_hex = _eth_call(SUSDE, SEL_TOTAL_SUPPLY)
cd_hex = _eth_call(SUSDE, SEL_COOLDOWN_DUR)
va_hex = _eth_call(SUSDE, SEL_VESTING_AMT)
ta = _to_int(ta_hex) / 1e18
ts = _to_int(ts_hex) / 1e18
cd = _to_int(cd_hex)
va = _to_int(va_hex) / 1e18
rate = ta / ts if ts else 0.0
return {
"rate": round(rate, 8),
"total_assets": round(ta, 4),
"total_supply": round(ts, 4),
"cooldown_seconds": cd,
"cooldown_hours": round(cd / 3600, 1),
"vesting_amount": round(va, 4),
"note": f"1 sUSDe = {rate:.6f} USDe | cooldown = {cd//3600}h"
}
# ── Tool: ethena_apy ─────────────────────────────────────────────────────────
def ethena_apy() -> dict:
"""
Fetch current sUSDe APY from DefiLlama (live pool data).
Returns:
apy_current (float): current APY %
apy_7d (float | None): 7-day average APY %
apy_30d (float | None): 30-day average APY %
tvl_usd (float): total value locked in USD
pool_id (str): DefiLlama pool identifier
"""
resp = proxied_get(
DEFILLAMA_CHART_URL,
headers={"SC-CALLER-ID": CALLER_ID}, timeout=15
)
data = resp.json().get("data", [])
if not data:
# Fallback: search pools list
resp2 = proxied_get(DEFILLAMA_POOLS_URL,
headers={"SC-CALLER-ID": CALLER_ID}, timeout=15)
pools = resp2.json().get("data", [])
pool = next((p for p in pools if p.get("pool") == DEFILLAMA_POOL), None)
if not pool:
pool = next((p for p in pools
if "susde" in p.get("symbol", "").lower()
and p.get("chain") == "Ethereum"), None)
if pool:
return {
"apy_current": round(pool.get("apy", 0), 4),
"apy_7d": None, # not available in pools endpoint
"apy_30d": round(pool.get("apyMean30d"), 4) if pool.get("apyMean30d") else None,
"tvl_usd": pool.get("tvlUsd"),
"pool_id": pool.get("pool"),
"source": "fallback:pools",
}
return {"error": "Could not fetch APY data from DefiLlama"}
# Use latest data point from chart
latest = data[-1]
# 7d/30d from rolling window of chart data
apy_7d = round(sum(d["apy"] for d in data[-7:]) / min(7, len(data)), 4) if len(data) >= 2 else None
apy_30d = round(sum(d["apy"] for d in data[-30:]) / min(30, len(data)), 4) if len(data) >= 7 else None
return {
"apy_current": round(latest.get("apy", 0), 4),
"apy_7d": apy_7d,
"apy_30d": apy_30d,
"tvl_usd": latest.get("tvlUsd"),
"pool_id": DEFILLAMA_POOL,
"data_date": latest.get("timestamp"),
}
# ── Tool: ethena_balance ─────────────────────────────────────────────────────
def ethena_balance(wallet_address: str) -> dict:
"""
Query USDe and sUSDe balances for a given wallet address.
Args:
wallet_address: EVM wallet address (0x...)
Returns:
usde_balance (float): USDe token balance
susde_balance (float): sUSDe token balance
susde_in_usde (float): sUSDe balance converted to USDe at current rate
cooldown_end (int): Unix timestamp when cooldown ends (0 = no active cooldown)
"""
enc = _encode_addr(wallet_address)
usde_hex = _eth_call(USDE, f"{SEL_USDE_BALANCE}{enc}")
susde_hex = _eth_call(SUSDE, f"{SEL_USDE_BALANCE}{enc}")
cd_end_hex = _eth_call(SUSDE, f"{SEL_COOLDOWN_END}{enc}")
usde_bal = _to_int(usde_hex) / 1e18
susde_bal = _to_int(susde_hex) / 1e18
cd_end = _to_int(cd_end_hex)
# Get rate for conversion
ta = _to_int(_eth_call(SUSDE, SEL_TOTAL_ASSETS)) / 1e18
ts = _to_int(_eth_call(SUSDE, SEL_TOTAL_SUPPLY)) / 1e18
rate = ta / ts if ts else 1.0
return {
"wallet": wallet_address,
"usde_balance": round(usde_bal, 6),
"susde_balance": round(susde_bal, 6),
"susde_in_usde": round(susde_bal * rate, 6),
"cooldown_end_ts": cd_end,
"has_active_cooldown": cd_end > 0,
"rate": round(rate, 8),
}
# ── Tool: ethena_stake ───────────────────────────────────────────────────────
def ethena_stake(amount_usde: str, receiver: str) -> dict:
"""
Generate approve + deposit calldata to stake USDe for sUSDe.
Returns two transactions that must be executed in order.
Args:
amount_usde: amount of USDe to stake (e.g. "100" or "100.5")
receiver: wallet address to receive sUSDe
Returns:
transactions: list of two tx dicts (approve, then deposit)
amount_wei: amount in wei
expected_susde: estimated sUSDe received at current rate
"""
from scripts.ethena_ops import approve_calldata, deposit_calldata, to_wei, validate_address
validate_address(receiver)
wei = to_wei(amount_usde)
# Get current rate for estimate
ta = _to_int(_eth_call(SUSDE, SEL_TOTAL_ASSETS)) / 1e18
ts = _to_int(_eth_call(SUSDE, SEL_TOTAL_SUPPLY)) / 1e18
rate = ta / ts if ts else 1.0
expected_susde = float(amount_usde) / rate if rate else 0.0
return {
"action": "stake",
"amount_usde": amount_usde,
"amount_wei": wei,
"receiver": receiver,
"expected_susde": round(expected_susde, 6),
"current_rate": round(rate, 8),
"transactions": [
approve_calldata(wei),
deposit_calldata(wei, receiver),
],
"note": "Execute tx[0] (approve) first, then tx[1] (deposit). Both on Ethereum mainnet."
}
# ── Tool: ethena_cooldown_start ──────────────────────────────────────────────
def ethena_cooldown_start(amount_usde: str) -> dict:
"""
Generate calldata to start the cooldown period for unstaking sUSDe.
The actual cooldown duration is fetched live from chain (currently ~24h).
Args:
amount_usde: USDe-denominated amount of sUSDe to redeem (e.g. "100")
Returns:
transaction: tx dict for cooldownAssets
cooldown_seconds: current cooldown duration
cooldown_hours: cooldown in hours
"""
from scripts.ethena_ops import cooldown_calldata, to_wei
wei = to_wei(amount_usde)
cd_sec = _to_int(_eth_call(SUSDE, SEL_COOLDOWN_DUR))
return {
"action": "cooldown_start",
"amount_usde": amount_usde,
"amount_wei": wei,
"cooldown_seconds": cd_sec,
"cooldown_hours": round(cd_sec / 3600, 1),
"transaction": cooldown_calldata(wei),
"note": f"After executing, wait {cd_sec//3600}h then call ethena_unstake."
}
# ── Tool: ethena_unstake ─────────────────────────────────────────────────────
def ethena_unstake(receiver: str) -> dict:
"""
Generate calldata to claim USDe after cooldown has completed.
Args:
receiver: wallet address to receive the USDe
Returns:
transaction: tx dict for unstake
"""
from scripts.ethena_ops import unstake_calldata, validate_address
validate_address(receiver)
cd_sec = _to_int(_eth_call(SUSDE, SEL_COOLDOWN_DUR))
return {
"action": "unstake",
"receiver": receiver,
"transaction": unstake_calldata(receiver),
"note": f"Only call after cooldown ({cd_sec//3600}h) has passed. USDe goes to {receiver}."
}
# ── Tool registry ─────────────────────────────────────────────────────────────
TOOLS = {
"ethena_rate": ethena_rate,
"ethena_apy": ethena_apy,
"ethena_balance": ethena_balance,
"ethena_stake": ethena_stake,
"ethena_cooldown_start": ethena_cooldown_start,
"ethena_unstake": ethena_unstake,
}
"""
Ethena USDe/sUSDe operations — generates calldata for on-chain transactions.
Usage: python scripts/ethena_ops.py <action> [args]
Actions:
approve <amount_usde> — Generate approve calldata for USDe → sUSDe
deposit <amount_usde> <receiver> — Generate deposit calldata (ERC4626)
cooldown <amount> — Generate cooldownAssets calldata
unstake <receiver> — Generate unstake calldata
"""
import sys
import json
import re
from decimal import Decimal, InvalidOperation
# Contract addresses (Ethereum Mainnet)
USDE = "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3"
SUSDE = "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"
USDE_SILO = "0x7FC7c91D556B400AFa565013E3F32055a0713425"
DECIMALS = 18
_ADDR_RE = re.compile(r'^0x[0-9a-fA-F]{40}$')
def to_wei(amount_str: str) -> int:
"""Convert human-readable amount to wei (18 decimals)."""
try:
d = Decimal(amount_str)
except InvalidOperation:
raise ValueError(f"Invalid amount: {amount_str!r}")
if d < 0:
raise ValueError(f"Amount must be non-negative, got {amount_str}")
return int(d * Decimal(10 ** DECIMALS))
def validate_address(addr: str) -> str:
if not _ADDR_RE.match(addr):
raise ValueError(f"Invalid EVM address: {addr!r}")
return addr
def encode_uint256(val: int) -> str:
return hex(val)[2:].zfill(64)
def encode_address(addr: str) -> str:
return addr.lower().replace("0x", "").zfill(64)
def approve_calldata(amount_wei: int) -> dict:
"""ERC20 approve(spender, amount) — approve sUSDe to spend USDe."""
# approve(address,uint256) = 0x095ea7b3
data = "0x095ea7b3" + encode_address(SUSDE) + encode_uint256(amount_wei)
return {
"to": USDE,
"amount": "0",
"chain_id": 1,
"data": data,
"description": f"Approve sUSDe contract to spend {amount_wei} USDe wei"
}
def deposit_calldata(amount_wei: int, receiver: str) -> dict:
"""ERC4626 deposit(uint256 assets, address receiver)."""
validate_address(receiver)
# deposit(uint256,address) = 0x6e553f65
data = "0x6e553f65" + encode_uint256(amount_wei) + encode_address(receiver)
return {
"to": SUSDE,
"amount": "0",
"chain_id": 1,
"data": data,
"description": f"Deposit {amount_wei} USDe wei into sUSDe vault for {receiver}"
}
def cooldown_calldata(amount_wei: int) -> dict:
"""cooldownAssets(uint256 assets) — start cooldown period."""
# keccak256("cooldownAssets(uint256)")[:4] = 0xcdac52ed
data = "0xcdac52ed" + encode_uint256(amount_wei)
return {
"to": SUSDE,
"amount": "0",
"chain_id": 1,
"data": data,
"description": f"Start cooldown for {amount_wei} USDe wei worth of sUSDe"
}
def unstake_calldata(receiver: str) -> dict:
"""unstake(address receiver) — claim USDe after cooldown."""
validate_address(receiver)
# keccak256("unstake(address)")[:4] = 0xf2888dbb
data = "0xf2888dbb" + encode_address(receiver)
return {
"to": SUSDE,
"amount": "0",
"chain_id": 1,
"data": data,
"description": f"Claim unstaked USDe to {receiver} (requires cooldown complete)"
}
def main():
if len(sys.argv) < 2:
print(__doc__)
sys.exit(1)
action = sys.argv[1]
try:
if action == "approve":
amount = sys.argv[2] if len(sys.argv) > 2 else "1000"
print(json.dumps(approve_calldata(to_wei(amount)), indent=2))
elif action == "deposit":
if len(sys.argv) < 4:
print("Usage: deposit <amount_usde> <receiver_address>", file=sys.stderr)
sys.exit(1)
print(json.dumps(deposit_calldata(to_wei(sys.argv[2]), sys.argv[3]), indent=2))
elif action == "cooldown":
amount = sys.argv[2] if len(sys.argv) > 2 else "1000"
print(json.dumps(cooldown_calldata(to_wei(amount)), indent=2))
elif action == "unstake":
if len(sys.argv) < 3:
print("Usage: unstake <receiver_address>", file=sys.stderr)
sys.exit(1)
print(json.dumps(unstake_calldata(sys.argv[2]), indent=2))
else:
print(f"Unknown action: {action}")
sys.exit(1)
except ValueError as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
"""
Ethena Remote Verification Tests
=================================
Tests that can be independently verified on-chain or via public dashboards.
Verification URLs (open in browser to cross-check):
Rate: https://etherscan.io/token/0x9D39A5DE30e57443BfF2A8307A4256c8797A3497#readContract
APY: https://app.ethena.fi/ (Dashboard → Current APY)
TVL: https://defillama.com/protocol/ethena
Cooldown: etherscan → sUSDe → Read → cooldownDuration()
Usage:
python tests/test_ethena_remote.py
python tests/test_ethena_remote.py --wallet 0xYOUR_WALLET
Requirements: requests, pycryptodome (for selector verification)
"""
import sys
import os
import json
import time
import argparse
import requests
# Allow running from skill root
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
RPC_URL = "https://ethereum.publicnode.com"
SUSDE = "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"
USDE = "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3"
DEFILLAMA = "https://yields.llama.fi/chart/66985a81-4b3f-417b-8e53-b6e9cee0d83a"
PASS = "✅ PASS"
FAIL = "❌ FAIL"
WARN = "⚠️ WARN"
def eth_call(to, data):
r = requests.post(RPC_URL, json={
"jsonrpc": "2.0", "method": "eth_call",
"params": [{"to": to, "data": data}, "latest"], "id": 1
}, timeout=15)
result = r.json().get("result", "0x0")
if not result or result == "0x":
return 0
return int(result, 16)
def encode_addr(addr):
return addr.lower().replace("0x", "").zfill(64)
def separator(label):
print(f"\n{'='*55}")
print(f" {label}")
print(f"{'='*55}")
# ─────────────────────────────────────────────────────────────
# T01: Exchange Rate
# Verify: Etherscan → sUSDe contract → Read → totalAssets / totalSupply
# ─────────────────────────────────────────────────────────────
def test_rate():
separator("T01 · sUSDe/USDe Exchange Rate")
ta = eth_call(SUSDE, "0x01e1d114") / 1e18
ts = eth_call(SUSDE, "0x18160ddd") / 1e18
rate = ta / ts if ts else 0
print(f" totalAssets : {ta:>18,.4f} USDe")
print(f" totalSupply : {ts:>18,.4f} sUSDe")
print(f" rate : {rate:.8f} (1 sUSDe = {rate:.6f} USDe)")
print(f" cumulative Δ : +{(rate-1)*100:.2f}%")
ok = 1.0 < rate < 2.0 and ta > 1e9 and ts > 1e9
print(f"\n {PASS if ok else FAIL} rate={rate:.6f}, TVL={ta/1e9:.2f}B USDe")
print(f" 🔍 Cross-check: https://etherscan.io/token/0x9D39A5DE30e57443BfF2A8307A4256c8797A3497#readContract")
return ok, {"rate": rate, "total_assets": ta, "total_supply": ts}
# ─────────────────────────────────────────────────────────────
# T02: Cooldown Duration
# Verify: Etherscan → sUSDe → Read → cooldownDuration()
# ─────────────────────────────────────────────────────────────
def test_cooldown():
separator("T02 · Cooldown Duration (on-chain)")
cd = eth_call(SUSDE, "0x35269315")
hours = cd / 3600
days = cd / 86400
print(f" cooldownDuration : {cd}s = {hours:.1f}h = {days:.1f}d")
print(f" (SKILL.md used to say '7 days' — now queries live)")
ok = 3600 <= cd <= 7 * 86400 # sanity: between 1h and 7d
status = PASS if ok else FAIL
if cd == 86400:
print(f"\n {PASS} Confirmed 24h cooldown (as of last check)")
elif ok:
print(f"\n {WARN} Cooldown changed to {hours:.1f}h — update docs")
else:
print(f"\n {FAIL} Unexpected cooldown: {cd}s")
print(f" 🔍 Cross-check: Etherscan → sUSDe Read → cooldownDuration()")
return ok, {"cooldown_seconds": cd, "cooldown_hours": hours}
# ─────────────────────────────────────────────────────────────
# T03: APY from DefiLlama
# Verify: https://defillama.com/protocol/ethena or app.ethena.fi
# ─────────────────────────────────────────────────────────────
def test_apy():
separator("T03 · sUSDe APY (DefiLlama)")
# Try chart endpoint first, fallback to pools list
r = requests.get(DEFILLAMA, timeout=15)
data = r.json().get("data", [])
if data:
latest = data[-1]
apy = latest.get("apy", 0)
apy_7d = sum(d["apy"] for d in data[-7:]) / min(7, len(data))
apy_30d = sum(d["apy"] for d in data[-30:]) / min(30, len(data))
tvl = latest.get("tvlUsd", 0)
else:
# Fallback: scan pools list for sUSDe on Ethereum
r2 = requests.get("https://yields.llama.fi/pools", timeout=15)
pools = r2.json().get("data", [])
pool = next((p for p in pools
if "susde" in p.get("symbol","").lower()
and p.get("chain","") == "Ethereum"), None)
if not pool:
print(f" {FAIL} No sUSDe pool found in DefiLlama")
return False, {}
apy = pool.get("apy", 0)
apy_7d = pool.get("apy7d") or apy
apy_30d = pool.get("apy30d") or apy
tvl = pool.get("tvlUsd", 0)
latest = {"timestamp": "pools-list"}
print(f" APY (current) : {apy:.2f}%")
print(f" APY (7d avg) : {apy_7d:.2f}%")
print(f" APY (30d avg) : {apy_30d:.2f}%")
print(f" TVL : ${tvl/1e9:.2f}B")
print(f" date : {latest.get('timestamp','')}")
ok = 0.0 < apy < 50.0 and tvl > 1e9
print(f"\n {PASS if ok else FAIL} APY={apy:.2f}%, TVL=${tvl/1e9:.2f}B")
print(f" 🔍 Cross-check: https://app.ethena.fi or https://defillama.com/protocol/ethena")
return ok, {"apy": apy, "apy_7d": apy_7d, "apy_30d": apy_30d, "tvl": tvl}
# ─────────────────────────────────────────────────────────────
# T04: Calldata Correctness
# Verify: paste calldata into https://calldata.swiss or https://abi.hashex.org
# ─────────────────────────────────────────────────────────────
def test_calldata():
separator("T04 · Calldata Generation Integrity")
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from scripts.ethena_ops import approve_calldata, deposit_calldata, cooldown_calldata, unstake_calldata, to_wei
DUMMY_RECEIVER = "0x95df79E2c8Cc11Cd3B759D055A1896F3882D38E8"
amount_wei = to_wei("100")
approve = approve_calldata(amount_wei)
deposit = deposit_calldata(amount_wei, DUMMY_RECEIVER)
cooldown = cooldown_calldata(amount_wei)
unstake = unstake_calldata(DUMMY_RECEIVER)
checks = [
("approve selector", approve["data"][:10], "0x095ea7b3"),
("approve to", approve["to"], "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3"),
("deposit selector", deposit["data"][:10], "0x6e553f65"),
("deposit to", deposit["to"], "0x9D39A5DE30e57443BfF2A8307A4256c8797A3497"),
("cooldown selector", cooldown["data"][:10], "0xcdac52ed"),
("unstake selector", unstake["data"][:10], "0xf2888dbb"),
]
all_ok = True
for label, got, expected in checks:
ok = got.lower() == expected.lower()
all_ok = all_ok and ok
print(f" {'✅' if ok else '❌'} {label:22s} got={got} expected={expected}")
# Print sample calldata for manual verification
print(f"\n Sample: approve 100 USDe")
print(f" to: {approve['to']}")
print(f" data: {approve['data'][:50]}...")
print(f" 🔍 Cross-check: https://calldata.swiss → paste data field")
return all_ok, {}
# ─────────────────────────────────────────────────────────────
# T05: Wallet Balance Query (optional — requires --wallet flag)
# Verify: check Etherscan token balance for the address
# ─────────────────────────────────────────────────────────────
def test_wallet_balance(wallet: str):
separator(f"T05 · Wallet Balance for {wallet[:10]}...")
enc = encode_addr(wallet)
usde_raw = eth_call(USDE, f"0x70a08231{enc}")
susde_raw = eth_call(SUSDE, f"0x70a08231{enc}")
cd_end = eth_call(SUSDE, f"0x525f3146{enc}")
usde_bal = usde_raw / 1e18
susde_bal = susde_raw / 1e18
ta = eth_call(SUSDE, "0x01e1d114") / 1e18
ts = eth_call(SUSDE, "0x18160ddd") / 1e18
rate = ta / ts if ts else 1.0
susde_in_usde = susde_bal * rate
print(f" USDe balance : {usde_bal:,.6f}")
print(f" sUSDe balance : {susde_bal:,.6f}")
print(f" sUSDe→USDe : {susde_in_usde:,.6f} (rate={rate:.6f})")
print(f" cooldown_end : {cd_end} {'(active)' if cd_end > 0 else '(none)'}")
print(f"\n 🔍 Cross-check: https://etherscan.io/address/{wallet}#tokentxns")
print(f" USDe: https://etherscan.io/token/0x4c9EDD5852cd905f086C759E8383e09bff1E68B3?a={wallet}")
print(f" sUSDe: https://etherscan.io/token/0x9D39A5DE30e57443BfF2A8307A4256c8797A3497?a={wallet}")
return True, {"usde": usde_bal, "susde": susde_bal, "cooldown_end": cd_end}
# ─────────────────────────────────────────────────────────────
# T06: Vesting Amount (rewards in transit)
# Verify: Etherscan → sUSDe → Read → vestingAmount()
# ─────────────────────────────────────────────────────────────
def test_vesting():
separator("T06 · Vesting Amount (rewards in transit)")
va = eth_call(SUSDE, "0x00728f76") / 1e18
print(f" vestingAmount : {va:,.4f} USDe")
print(f" (rewards being linearly distributed over 8h window)")
print(f"\n {PASS} vestingAmount queried")
print(f" 🔍 Cross-check: Etherscan → sUSDe → Read → vestingAmount()")
return True, {"vesting_amount": va}
# ─────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────
def main():
parser = argparse.ArgumentParser(description="Ethena remote verification tests")
parser.add_argument("--wallet", default=None, help="Wallet address for T05 balance test")
parser.add_argument("--json", action="store_true", help="Output results as JSON")
args = parser.parse_args()
print("\n🟣 Ethena Skill — Remote Verification Tests")
print(f" RPC: {RPC_URL}")
print(f" sUSDe: {SUSDE}")
results = {}
total = ok_count = 0
for name, fn, kwargs in [
("T01_rate", test_rate, {}),
("T02_cooldown", test_cooldown, {}),
("T03_apy", test_apy, {}),
("T04_calldata", test_calldata, {}),
("T06_vesting", test_vesting, {}),
]:
ok, data = fn(**kwargs)
results[name] = {"ok": ok, **data}
total += 1
if ok:
ok_count += 1
time.sleep(0.3) # be polite to public RPC
if args.wallet:
ok, data = test_wallet_balance(args.wallet)
results["T05_wallet"] = {"ok": ok, **data}
total += 1
if ok:
ok_count += 1
separator(f"Summary: {ok_count}/{total} passed")
for name, r in results.items():
status = PASS if r["ok"] else FAIL
print(f" {status} {name}")
if args.json:
print("\n" + json.dumps(results, indent=2))
sys.exit(0 if ok_count == total else 1)
if __name__ == "__main__":
main()