mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-09 08:18:07 +00:00
Compare commits
164
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73d34434b0 | ||
|
|
d0154e5721 | ||
|
|
52b6a8a076 | ||
|
|
db6a9cfc68 | ||
|
|
03b8e53d30 | ||
|
|
8d79ace8b3 | ||
|
|
1fb6d3d62d | ||
|
|
c4559cb0d7 | ||
|
|
1d88ddefb5 | ||
|
|
683352f737 | ||
|
|
a629bc940c | ||
|
|
fe7b8eb6f3 | ||
|
|
24339929dc | ||
|
|
ed855f6859 | ||
|
|
e144c1822f | ||
|
|
69271d33ca | ||
|
|
f1140f106a | ||
|
|
3b935b7979 | ||
|
|
00388f9166 | ||
|
|
a32efa2ba0 | ||
|
|
76704b6bdd | ||
|
|
168d8cbb08 | ||
|
|
9629c22496 | ||
|
|
a34d06f9b4 | ||
|
|
2ba3e80ad9 | ||
|
|
12e8e3e064 | ||
|
|
aa24995d28 | ||
|
|
d493642935 | ||
|
|
86670486a6 | ||
|
|
6496011d8f | ||
|
|
792351ab8a | ||
|
|
4915dc574e | ||
|
|
9866e8068f | ||
|
|
099af3ce01 | ||
|
|
445e634aa1 | ||
|
|
4bb1fee995 | ||
|
|
aad14395e0 | ||
|
|
598f2a0eb6 | ||
|
|
cb1cef6f1b | ||
|
|
fd034d493f | ||
|
|
2551670874 | ||
|
|
3ec7e9b8d6 | ||
|
|
499ee692da | ||
|
|
de13142950 | ||
|
|
597e41f053 | ||
|
|
b0a33a58d1 | ||
|
|
65b9f21981 | ||
|
|
33b5407245 | ||
|
|
7aafc3b69d | ||
|
|
9a093459bc | ||
|
|
aa36b0cfdc | ||
|
|
93c6409688 | ||
|
|
84d5e1f3d7 | ||
|
|
6e5413ec1e | ||
|
|
84e8ce1d43 | ||
|
|
c69ea72860 | ||
|
|
624fecfa70 | ||
|
|
6dcf13481d | ||
|
|
3fd0f715a1 | ||
|
|
85e7f245c0 | ||
|
|
03df1006cc | ||
|
|
3099cb1319 | ||
|
|
49b8f0bcf1 | ||
|
|
a23eb91f04 | ||
|
|
974d3017e0 | ||
|
|
668c3a57d3 | ||
|
|
819ce93a90 | ||
|
|
061a8dd8f1 | ||
|
|
49df78c075 | ||
|
|
6a6b61c491 | ||
|
|
7612e25dd1 | ||
|
|
705e489c17 | ||
|
|
9a418aba41 | ||
|
|
2cb6c4fef1 | ||
|
|
180f47b6e0 | ||
|
|
5f79b134f2 | ||
|
|
85705dd27c | ||
|
|
59ccd1fea8 | ||
|
|
a9fa7f2e19 | ||
|
|
7529de334f | ||
|
|
1bfe4d58c0 | ||
|
|
4ce2b07e21 | ||
|
|
6bb0994cae | ||
|
|
486b911bed | ||
|
|
f48371ccd8 | ||
|
|
9ca35584b5 | ||
|
|
01e5ab9997 | ||
|
|
28bc1d5210 | ||
|
|
870fe2b8d7 | ||
|
|
e7f6341961 | ||
|
|
1baee3a207 | ||
|
|
e94cf1bda2 | ||
|
|
274e2f3d75 | ||
|
|
a91e87534b | ||
|
|
d0d1afa0ea | ||
|
|
0f70c6b6aa | ||
|
|
078b201d15 | ||
|
|
1832c4c13e | ||
|
|
73a88a8a0f | ||
|
|
265cb6df50 | ||
|
|
5bba8169b5 | ||
|
|
295c6a47b7 | ||
|
|
e7dedbba2c | ||
|
|
6196912cf4 | ||
|
|
f1ebfacaa2 | ||
|
|
ea5da20311 | ||
|
|
fabc8af679 | ||
|
|
189fed0f6e | ||
|
|
09ff4dc296 | ||
|
|
2627fa9db8 | ||
|
|
8b20e60753 | ||
|
|
f991215d86 | ||
|
|
6a3b842a3b | ||
|
|
b36c1dce0f | ||
|
|
dbe8df6934 | ||
|
|
400c7ad4b7 | ||
|
|
e96cbb6ff4 | ||
|
|
aa3d5e8c6c | ||
|
|
96cf4ea47b | ||
|
|
45061ed748 | ||
|
|
da3dacb549 | ||
|
|
51e0ce2bea | ||
|
|
8c13d20cb7 | ||
|
|
3d959ae579 | ||
|
|
4240cc6901 | ||
|
|
8d0e9578c4 | ||
|
|
95f07145a9 | ||
|
|
e3411c6791 | ||
|
|
640f1bfb4e | ||
|
|
f973fe433d | ||
|
|
730a75df93 | ||
|
|
de8a793bd5 | ||
|
|
bb072b2d1d | ||
|
|
95426e222b | ||
|
|
4365d73562 | ||
|
|
6067f759a0 | ||
|
|
b31b98f816 | ||
|
|
801543488e | ||
|
|
20c73eba21 | ||
|
|
ed49dca2a6 | ||
|
|
ca559d0f82 | ||
|
|
fef7604490 | ||
|
|
e524c225f2 | ||
|
|
39a97110dd | ||
|
|
8ad19b007b | ||
|
|
2e2990369b | ||
|
|
24f93fa8a7 | ||
|
|
d778e20c23 | ||
|
|
58f5de4a48 | ||
|
|
ad46635e20 | ||
|
|
6dde78f799 | ||
|
|
6b70aa0936 | ||
|
|
66e1b3e6f5 | ||
|
|
c6f279c51b | ||
|
|
9bad7f180e | ||
|
|
88292c9c49 | ||
|
|
5878a3dc0b | ||
|
|
ccb23ecf44 | ||
|
|
b6a6515677 | ||
|
|
5e65c7025d | ||
|
|
d7a95ceb36 | ||
|
|
1bd3e7edc6 | ||
|
|
28ba0613ca | ||
|
|
1dce3dfcfb |
+73
-4
@@ -1,14 +1,36 @@
|
||||
# UI language (zh | en)
|
||||
LANG=zh
|
||||
|
||||
# Exchange selection
|
||||
EXCHANGE=aster # Pick aster (default) or grvt/lighter/backpack/paradex
|
||||
EXCHANGE=aster # Pick aster (default) or standx/grvt/lighter/backpack/paradex/nado
|
||||
|
||||
# Aster API credentials
|
||||
ASTER_API_KEY=
|
||||
ASTER_API_SECRET=
|
||||
|
||||
# StandX authentication (set when EXCHANGE=standx)
|
||||
STANDX_TOKEN=
|
||||
STANDX_SYMBOL=BTC-USD
|
||||
# STANDX_BASE_URL=https://perps.standx.com
|
||||
# STANDX_WS_URL=wss://perps.standx.com/ws-stream/v1
|
||||
# STANDX_SESSION_ID=
|
||||
# Optional: request signing key (ed25519 private key, supports hex or base58 format)
|
||||
# STANDX_REQUEST_PRIVATE_KEY=
|
||||
# Token expiry configuration (recommended method: creation date + validity days)
|
||||
# Get these values when generating API token at https://standx.com/user/session
|
||||
# STANDX_TOKEN_CREATE_DATE=2026-01-15 # Token creation date (YYYY-MM-DD format)
|
||||
# STANDX_TOKEN_VALIDITY_DAYS=30 # Token validity period in days
|
||||
# Legacy method: direct expiry timestamp (Unix seconds)
|
||||
# STANDX_TOKEN_EXPIRY=1737092800
|
||||
|
||||
# Core trading symbol and sizing
|
||||
TRADE_SYMBOL=BTCUSDT # Trading pair symbol
|
||||
TRADE_AMOUNT=0.001 # Base order quantity (base asset, e.g. BTC)
|
||||
|
||||
# Swing Trading
|
||||
SWING_DIRECTION=short # short | long | both
|
||||
SWING_STOP_LOSS_PCT=0.05 # 0.05 = 5%
|
||||
|
||||
# Risk management (USD amounts unless noted)
|
||||
LOSS_LIMIT=0.04 # Max loss per trade in USDT before forced close
|
||||
TRAILING_PROFIT=0.2 # Trailing stop activation profit (USDT)
|
||||
@@ -37,6 +59,22 @@ MAKER_REFRESH_INTERVAL_MS=500 # Maker refresh cadence (ms)
|
||||
MAKER_MAX_CLOSE_SLIPPAGE_PCT=0.05 # Maker close slippage guard (fallbacks to MAX_CLOSE_SLIPPAGE_PCT)
|
||||
MAKER_PRICE_TICK=0.1 # Maker price tick size (defaults to PRICE_TICK)
|
||||
|
||||
# Grid strategy defaults
|
||||
GRID_LOWER_PRICE=25000 # Grid lower bound price (quote currency)
|
||||
GRID_UPPER_PRICE=35000 # Grid upper bound price
|
||||
GRID_LEVELS=10 # Number of grid levels between bounds (>=2)
|
||||
GRID_ORDER_SIZE=0.001 # Quantity per grid order (base asset units)
|
||||
GRID_MAX_POSITION_SIZE=0.01 # Max inventory the grid may hold (base units)
|
||||
GRID_REFRESH_INTERVAL_MS=1000 # Grid evaluation cadence (ms)
|
||||
GRID_MAX_LOG_ENTRIES=200 # Grid trade log length (defaults to MAX_LOG_ENTRIES when unset)
|
||||
GRID_DIRECTION=both # Order direction: both | long | short
|
||||
GRID_STOP_LOSS_PCT=0.01 # Stop loss trigger percentage beyond bounds (0.01 => 1%)
|
||||
GRID_RESTART_TRIGGER_PCT=0.01 # Restart buffer percentage inside bounds
|
||||
GRID_AUTO_RESTART_ENABLED=true # Automatically resume grid when price re-enters range
|
||||
GRID_MAX_CLOSE_SLIPPAGE_PCT=0.05 # Close-order slippage guard relative to mark price
|
||||
# GRID_PRICE_TICK=0.1 # Optional override for grid price tick (falls back to PRICE_TICK)
|
||||
# GRID_QTY_STEP=0.001 # Optional override for grid quantity step (falls back to QTY_STEP)
|
||||
|
||||
# GRVT authentication (set when EXCHANGE=grvt)
|
||||
GRVT_API_KEY=
|
||||
GRVT_API_SECRET=
|
||||
@@ -83,12 +121,10 @@ BACKPACK_DEBUG=false
|
||||
# Provide the EVM private key & wallet address for onboarded accounts.
|
||||
# When EXCHANGE=paradex these values are used automatically.
|
||||
|
||||
PARADEX_SYMBOL=BTC-USD-PERP
|
||||
PARADEX_PRIVATE_KEY=
|
||||
PARADEX_WALLET_ADDRESS=
|
||||
|
||||
# Symbol defaults to TRADE_SYMBOL if omitted. Use ccxt unified format like BTC-USD-PERP.
|
||||
# PARADEX_SYMBOL=BTC-USD-PERP
|
||||
|
||||
# Enable testnet endpoints by setting to "true"; defaults to false (mainnet).
|
||||
# PARADEX_SANDBOX=false
|
||||
|
||||
@@ -100,3 +136,36 @@ PARADEX_WALLET_ADDRESS=
|
||||
|
||||
# Enable verbose adapter logging: set to "1" or "true"
|
||||
# PARADEX_DEBUG=false
|
||||
|
||||
# Nado exchange configuration (Ink mainnet)
|
||||
# Requires a linked signer private key + your original subaccount owner EVM address.
|
||||
# When EXCHANGE=nado these values are used automatically.
|
||||
|
||||
NADO_ENV=inkMainnet # inkMainnet | inkTestnet
|
||||
NADO_SYMBOL=BTC-PERP # Trading product symbol (e.g., BTC-PERP / ETH-PERP)
|
||||
NADO_SIGNER_PRIVATE_KEY= # 32-byte 0x-prefixed private key (0x...)
|
||||
NADO_SUBACCOUNT_OWNER= # EVM address of the subaccount owner (0x...)
|
||||
NADO_SUBACCOUNT_NAME=default # Subaccount name (bytes12, default "default")
|
||||
|
||||
# Optional: market-order slippage buffer (used for IOC limit-as-market, e.g. 0.01 => 1%)
|
||||
NADO_MARKET_SLIPPAGE_PCT=0.01
|
||||
|
||||
# Optional: stop trigger source for STOP_MARKET orders (oracle | last | mid)
|
||||
NADO_STOP_TRIGGER_SOURCE=oracle
|
||||
|
||||
# Optional: how to handle orders smaller than Nado min_size (USDT0 notional)
|
||||
# - adjust: round quantity up to the minimum allowed size (default)
|
||||
# - reject: throw an error instead of auto-adjusting
|
||||
NADO_MIN_SIZE_POLICY=adjust
|
||||
|
||||
# Optional endpoint overrides
|
||||
# NADO_GATEWAY_WS_URL=wss://gateway.prod.nado.xyz/v1/ws
|
||||
# NADO_SUBSCRIPTIONS_WS_URL=wss://gateway.prod.nado.xyz/v1/subscribe
|
||||
# NADO_ARCHIVE_URL=https://archive.prod.nado.xyz/v1
|
||||
# NADO_TRIGGER_URL=https://trigger.prod.nado.xyz/v1
|
||||
# NADO_DEBUG=false
|
||||
|
||||
# Telegram notification configuration
|
||||
# TELEGRAM_BOT_TOKEN= # Telegram bot token from @BotFather
|
||||
# TELEGRAM_CHAT_ID= # Chat ID to receive notifications
|
||||
# TELEGRAM_ACCOUNT_LABEL= # Account label to distinguish multiple bot instances (e.g., "Account-A")
|
||||
|
||||
@@ -33,3 +33,6 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
.tmp
|
||||
.tmp/*
|
||||
@@ -0,0 +1,12 @@
|
||||
# RitMEX Bot - Claude Instructions
|
||||
|
||||
## Package Manager
|
||||
|
||||
**必须使用 Bun** - 这个项目使用 Bun 作为包管理器和运行时。所有能用 bun 执行的命令都必须使用 bun:
|
||||
|
||||
- 安装依赖: `bun install`
|
||||
- 运行脚本: `bun run <script>`
|
||||
- 执行测试: `bun test`
|
||||
- 类型检查: `bun run typecheck`
|
||||
|
||||
**不要使用 npm、yarn 或 npx**
|
||||
@@ -1,42 +1,71 @@
|
||||
# ritmex-bot
|
||||
|
||||
基于 Bun 的 Aster 永续合约量化终端,内置趋势跟随(SMA30)与做市策略,支持快速恢复、实时行情订阅与日志追踪。
|
||||
> For English users, please see [README_en.md](README_en.md).
|
||||
|
||||
* [Aster 30% 手续费优惠注册链接](https://www.asterdex.com/zh-CN/referral/4665f3)
|
||||
Please set `LANG=en` in `.env` for English interface.
|
||||
|
||||
A Bun-powered multi-exchange perpetuals workstation that ships an SMA30 trend engine, a Guardian stop sentinel, and two market-making modes. It offers instant restarts, realtime market data, structured logging, and an Ink-based CLI dashboard.
|
||||
|
||||
基于 Bun 的多交易所永续合约量化终端,内置趋势跟随(SMA30)、Guardian 防守与做市策略,支持快速恢复、实时行情订阅、日志追踪与 CLI 仪表盘。
|
||||
|
||||
如果您希望获取优惠并支持本项目,请考虑使用以下注册链接:
|
||||
|
||||
* [Lighter 手续费优惠注册链接](https://app.lighter.xyz/?referral=111909FA)
|
||||
* [Aster 手续费优惠注册链接](https://www.asterdex.com/zh-CN/referral/4665f3)
|
||||
* [StandX 手续费优惠注册链接](https://standx.com/referral?code=xingxingjun)
|
||||
* [Binance 手续费优惠注册链接](https://www.binance.com/join?ref=KNKCA9XC)
|
||||
* [GRVT 手续费优惠注册链接](https://grvt.io/exchange/sign-up?ref=sea)
|
||||
* [Nado 手续费优惠注册链接](https://app.nado.xyz?join=LKbIUs5)
|
||||
* [Backpack 手续费优惠注册链接](https://backpack.exchange/join/ritmex)
|
||||
* [edgex 手续费优惠注册链接](https://pro.edgex.exchange/referral/BULL)
|
||||
* [Paradex 手续费优惠注册链接](https://paradex.io/ref/xingxingjun)
|
||||
* [Apex 手续费优惠注册链接](https://join.omni.apex.exchange/SEA)
|
||||
|
||||
## 文档索引
|
||||
- [English README](README_en.md)
|
||||
- [简明上手指南(零基础)](simple-readme.md)
|
||||
- [基础网格策略使用教程](grid-trading.md)
|
||||
|
||||
## 项目亮点
|
||||
- **实时行情与风控**:Websocket + REST 自动同步账户、挂单与仓位。
|
||||
## 核心特性
|
||||
- **实时行情与风控**:Websocket + REST 自动同步账户、挂单与仓位,断线后自动恢复。
|
||||
- **趋势策略**:SMA30 穿越入场,内置止损、移动止盈、布林带带宽过滤与步进锁盈。
|
||||
- **做市策略**:支持双边追价、风险阈值与订单自愈。
|
||||
- **模块化设计**:适配器、策略引擎与 CLI 解耦,方便扩展新交易所或策略。
|
||||
- **Guardian 策略**:不主动开单,实时监听账户仓位并强制补挂/移动止损与动态止盈,防止裸奔。
|
||||
- **做市策略**:支持双边追价、风险阈值控制与订单自愈。
|
||||
- **模块化架构**:策略引擎、交易所适配器与 Ink CLI 相互解耦,新增交易所或策略更容易。
|
||||
|
||||
## 环境要求
|
||||
- Bun ≥ 1.2(含 `bun`、`bunx` 命令)
|
||||
- macOS、Linux 或 Windows (WSL 推荐)
|
||||
- Node.js 仅在某些安装路径需要,可选
|
||||
## 支持的交易所
|
||||
| 交易所 | 合约类型 | 必填环境变量 | 备注 |
|
||||
| --- | --- | --- | --- |
|
||||
| Aster | USDT 永续 | `ASTER_API_KEY`, `ASTER_API_SECRET` | 默认交易所;兼容脚本引导
|
||||
| StandX | USD 永续 | `STANDX_TOKEN` | 使用 JWT Token 登录,优先走 WebSocket 推送
|
||||
| GRVT | USDT 永续 | `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID` | `GRVT_ENV` 可切换 `prod`/`testnet`
|
||||
| Lighter | zkLighter 永续 | `LIGHTER_ACCOUNT_INDEX`, `LIGHTER_API_PRIVATE_KEY` | 默认 `LIGHTER_ENV=testnet`
|
||||
| Backpack | USDC 永续 | `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, `BACKPACK_PASSWORD` | `BACKPACK_SANDBOX=true` 启用沙盒
|
||||
| Paradex | StarkEx 永续 | `PARADEX_PRIVATE_KEY`, `PARADEX_WALLET_ADDRESS` | `PARADEX_SANDBOX=true` 使用测试网
|
||||
| Nado | USDC 永续 | `NADO_SIGNER_PRIVATE_KEY`, `NADO_SUBACCOUNT_OWNER` | `NADO_ENV` 可切换 `inkMainnet`/`inkTestnet`
|
||||
|
||||
## 快速启动脚本(macOS / Linux / WSL)
|
||||
## 系统要求
|
||||
- Bun ≥ 1.2(需同时包含 `bun`、`bunx` 命令)
|
||||
- macOS、Linux 或 Windows (推荐 WSL)
|
||||
- Node.js 仅在部分工具链场景需要,可选
|
||||
|
||||
## 快速上手
|
||||
### 一键脚本(macOS / Linux / WSL)
|
||||
```bash
|
||||
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
||||
```
|
||||
脚本会安装 Bun、依赖,收集 Aster API Key/Secret,生成 `.env` 并启动 CLI。运行前请准备好 API 凭证。
|
||||
脚本会安装 Bun、项目依赖,收集 Aster API 凭证,生成 `.env` 并启动 CLI。运行前请准备好对应交易所的 API Key/Secret。
|
||||
|
||||
## 手动安装步骤
|
||||
### 手动安装
|
||||
1. **获取代码**
|
||||
```bash
|
||||
git clone https://github.com/discountry/ritmex-bot.git
|
||||
cd ritmex-bot
|
||||
```
|
||||
不方便使用 Git 时,可在仓库页面下载 ZIP 并手动解压。
|
||||
不便使用 Git 时,可在仓库页面下载 ZIP 后手动解压。
|
||||
2. **安装 Bun**
|
||||
- macOS / Linux:`curl -fsSL https://bun.sh/install | bash`
|
||||
- Windows PowerShell:`powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||
安装后重新打开终端,确认 `bun -v` 正常输出版本号。
|
||||
安装完成后重新打开终端,确认 `bun -v` 正常输出版本号。
|
||||
3. **安装依赖**
|
||||
```bash
|
||||
bun install
|
||||
@@ -45,67 +74,124 @@ curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
按下文说明修改 `.env`,至少需要正确配置 Aster 或 GRVT 的 API。
|
||||
按下文指南修改 `.env`,至少需要正确配置一个交易所的凭证。
|
||||
5. **运行 CLI**
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
方向键选择策略,回车启动;`Esc` 返回菜单,`Ctrl+C` 退出。
|
||||
方向键选择策略并回车启动;`Esc` 返回菜单,`Ctrl+C` 退出。
|
||||
|
||||
## 环境变量配置指南
|
||||
核心变量在 `.env.example` 中给出默认值:
|
||||
## 通用环境变量
|
||||
`.env.example` 提供了所有默认键值,下表概括最常用参数:
|
||||
|
||||
| 变量 | 说明 |
|
||||
| --- | --- |
|
||||
| `ASTER_API_KEY` / `ASTER_API_SECRET` | Aster API 凭证,运行策略必填 |
|
||||
| `EXCHANGE` | 选择交易所(`aster`/`standx`/`grvt`/`lighter`/`backpack`/`paradex`/`nado`) |
|
||||
| `TRADE_SYMBOL` | 交易对(默认 `BTCUSDT`) |
|
||||
| `TRADE_AMOUNT` | 单笔下单数量(标的资产计) |
|
||||
| `LOSS_LIMIT` | 单笔最大亏损触发的强平额度(USDT) |
|
||||
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | 动态止盈触发值(USDT)与回撤百分比 |
|
||||
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | 浮盈超过阈值后上调止损的触发金额与偏移 |
|
||||
| `BOLLINGER_LENGTH` / `BOLLINGER_STD_MULTIPLIER` | 布林带宽度判定的窗口长度与标准差倍数 |
|
||||
| `MIN_BOLLINGER_BANDWIDTH` | 仅当带宽 ≥ 此比例时才触发入场信号 |
|
||||
| `BOLLINGER_*` | 趋势策略布林带过滤参数 |
|
||||
| `PRICE_TICK` / `QTY_STEP` | 交易所要求的最小报价与数量精度 |
|
||||
| `POLL_INTERVAL_MS` | 趋势策略循环间隔(毫秒) |
|
||||
| `MAX_CLOSE_SLIPPAGE_PCT` | 平仓时相对标记价允许的最大偏差 |
|
||||
| `MAKER_*` 系列 | 做市策略独有参数(追价阈值、报价偏移、刷新频率等) |
|
||||
| `MAKER_*` | 做市策略专属参数(追价阈值、报价偏移、刷新频率等) |
|
||||
|
||||
切换到 GRVT 时,将 `EXCHANGE=grvt` 并补齐 `GRVT_API_KEY`、`GRVT_API_SECRET`、`GRVT_SUB_ACCOUNT_ID` 等变量;详情见 `.env.example`。
|
||||
|
||||
> 提示:你也可以通过命令行参数临时指定交易所(优先级高于环境变量):
|
||||
> 可通过命令行临时覆盖交易所与策略(优先级高于 `.env`):
|
||||
> ```bash
|
||||
> bun run index.ts --exchange grvt
|
||||
> bun run index.ts -e lighter
|
||||
> bun run index.ts --exchange grvt --strategy maker
|
||||
> bun run index.ts -e lighter -s offset-maker --silent
|
||||
> ```
|
||||
|
||||
## 常用命令
|
||||
## 交易所配置指南
|
||||
### Aster
|
||||
1. 将 `EXCHANGE` 保持为 `aster`(默认值)。
|
||||
2. 填写 `ASTER_API_KEY` 与 `ASTER_API_SECRET`。
|
||||
3. 根据交易对调整 `TRADE_SYMBOL`、`PRICE_TICK`、`QTY_STEP` 等精度参数。
|
||||
4. 一键脚本会自动写入这些变量,手动部署时需自行维护。
|
||||
|
||||
### StandX
|
||||
|
||||
* [StandX 做市策略教程](docs/standx/maker-points-guide.md)
|
||||
|
||||
策略需要 StandX 的 API Token 和签名私钥才能下单。
|
||||
|
||||
**获取方式(使用 StandX 官方 API 生成功能):**
|
||||
1. 打开 StandX 官方 API 创建页面:https://standx.com/user/session
|
||||
2. 连接钱包并登录
|
||||
3. 点击 **"Generate API Token"** 按钮
|
||||
4. 页面会显示以下信息:
|
||||
- **Token**(以 `eyJ` 开头的 JWT 字符串)→ 填入 `STANDX_TOKEN`
|
||||
- **Ed25519 Private Key**(Base58 格式私钥,类似 `HdsyJD7oWgT...`)→ 填入 `STANDX_REQUEST_PRIVATE_KEY`
|
||||
- **创建日期** 和 **有效期天数** → 用于配置 Token 过期提醒
|
||||
|
||||
> Ed25519 Private Key 是系统自动生成的签名私钥,仅用于交易请求签名,你的资产仍在主钱包中,非常安全。
|
||||
|
||||
请妥善保存这些凭证,不要分享给他人。
|
||||
|
||||
**配置步骤:**
|
||||
1. 设置 `EXCHANGE=standx`。
|
||||
2. 填写 `STANDX_TOKEN`(Perps API 的 JWT Token)。
|
||||
3. 填写 `STANDX_REQUEST_PRIVATE_KEY`(Ed25519 签名私钥,Base58 格式)。
|
||||
4. 设置 `STANDX_SYMBOL`(默认 `BTC-USD`),并校准 `PRICE_TICK` / `QTY_STEP`。
|
||||
5. 推荐配置 Token 过期时间:
|
||||
- `STANDX_TOKEN_CREATE_DATE`(创建日期,格式 `YYYY-MM-DD`)
|
||||
- `STANDX_TOKEN_VALIDITY_DAYS`(有效期天数)
|
||||
6. 可选:`STANDX_BASE_URL`、`STANDX_WS_URL`、`STANDX_SESSION_ID` 用于自定义环境。
|
||||
|
||||
|
||||
### GRVT
|
||||
1. 在 `.env` 中设置 `EXCHANGE=grvt`。
|
||||
2. 填写 `GRVT_API_KEY`、`GRVT_API_SECRET`、`GRVT_SUB_ACCOUNT_ID`。
|
||||
3. 若使用测试网,可将 `GRVT_ENV=testnet` 并调整 `GRVT_INSTRUMENT`/`GRVT_SYMBOL`。
|
||||
4. 可选:提供 `GRVT_COOKIE` 或自定义 `GRVT_SIGNER_PATH` 以复用已有登录态。
|
||||
|
||||
### Lighter
|
||||
1. 设置 `EXCHANGE=lighter`。
|
||||
2. 填写 `LIGHTER_ACCOUNT_INDEX` 与 `LIGHTER_API_PRIVATE_KEY`(40 字节十六进制私钥),其中`LIGHTER_ACCOUNT_INDEX`是你的账户索引,需要你在官网按F12观察接口请求获取,`LIGHTER_API_PRIVATE_KEY`是你的API私钥。
|
||||
3. 如需切换环境,将 `LIGHTER_ENV` 改为 `mainnet`/`staging`/`dev`;必要时指定 `LIGHTER_BASE_URL`。
|
||||
4. 交易对默认为 `LIGHTER_SYMBOL=BTCUSDT`,也可按需重写价格与数量小数位。
|
||||
|
||||
### Backpack
|
||||
1. 设置 `EXCHANGE=backpack`。
|
||||
2. 填写 `BACKPACK_API_KEY`、`BACKPACK_API_SECRET`、`BACKPACK_PASSWORD`;如有分账户,补充 `BACKPACK_SUBACCOUNT`,默认填写主账户ID。
|
||||
3. 使用测试环境时将 `BACKPACK_SANDBOX=true`,并确认 `BACKPACK_SYMBOL` 与实际符号一致(默认 `BTC_USD_PERP`)。
|
||||
4. 可通过 `BACKPACK_DEBUG=true` 观察适配器详细日志。
|
||||
|
||||
### Paradex
|
||||
1. 设置 `EXCHANGE=paradex`。
|
||||
2. 提供 `PARADEX_PRIVATE_KEY`(EVM 私钥)与 `PARADEX_WALLET_ADDRESS` 注意这是你EVM钱包的地址和私钥,建议创建全新钱包,不要放置无关资产。
|
||||
3. 默认连接主网,若需测试网,将 `PARADEX_SANDBOX=true` 并根据需要调整 `PARADEX_SYMBOL`。
|
||||
4. 复杂环境可额外设置 `PARADEX_USE_PRO`、`PARADEX_RECONNECT_DELAY_MS` 或调试开关。
|
||||
|
||||
### Nado
|
||||
1. 设置 `EXCHANGE=nado`。
|
||||
2. 在 Nado 官网(交易界面)打开开发者工具(F12)→ 切换到 `Application` → `Local Storage`,找到 `nado.userSettings`,在其内容中取出 `privateKey` 字段并填入 `.env` 的 `NADO_SIGNER_PRIVATE_KEY`。
|
||||
3. 提供 `NADO_SUBACCOUNT_OWNER`(或 `NADO_EVM_ADDRESS`)。
|
||||
4. 选择网络 `NADO_ENV=inkMainnet`(主网)或 `inkTestnet`(测试网)。
|
||||
5. 设置交易品种 `NADO_SYMBOL`(交易对格式类似 `BTC-PERP`;也支持输入 `BTCUSDT0`,会自动映射为 `BTC-PERP`)。
|
||||
|
||||
## 命令速查
|
||||
```bash
|
||||
bun run index.ts # 启动 CLI(默认)
|
||||
bun run start # 同上
|
||||
bun run dev # 调试模式,等价于运行 index.ts
|
||||
bun x vitest run # 执行单元测试
|
||||
bun run index.ts # 启动 CLI(默认入口)
|
||||
bun run start # 等价于运行 index.ts
|
||||
bun run dev # 调试模式
|
||||
bun x vitest run # 执行全部测试
|
||||
```
|
||||
|
||||
## 静默启动与后台运行
|
||||
### 直接静默启动
|
||||
无需进入 Ink 菜单,可用命令行直接拉起指定策略:
|
||||
|
||||
```bash
|
||||
bun run index.ts --strategy trend --silent # 启动趋势策略
|
||||
bun run index.ts --strategy maker --silent # 启动做市策略
|
||||
bun run index.ts --strategy offset-maker --silent # 启动偏移做市策略
|
||||
```
|
||||
|
||||
如需同时指定交易所,可叠加 `--exchange/-e`(将覆盖 `.env` 中的 `EXCHANGE`/`TRADE_EXCHANGE`):
|
||||
|
||||
```bash
|
||||
bun run index.ts --exchange grvt --strategy maker --silent
|
||||
bun run index.ts -e lighter -s offset-maker --silent
|
||||
bun run index.ts --strategy trend --silent
|
||||
bun run index.ts --strategy maker --silent
|
||||
bun run index.ts --strategy offset-maker --silent
|
||||
```
|
||||
如需同时指定交易所,可叠加 `--exchange/-e` 参数。
|
||||
|
||||
### 项目内置脚本
|
||||
`package.json` 提供了便捷脚本:
|
||||
|
||||
```bash
|
||||
bun run start:trend:silent
|
||||
bun run start:maker:silent
|
||||
@@ -113,43 +199,38 @@ bun run start:offset:silent
|
||||
```
|
||||
|
||||
### 使用 pm2 守护并自动重启
|
||||
将 `pm2` 安装到项目中(示例:`bun add -d pm2`),之后即可在不安装全局 pm2 的情况下运行:
|
||||
|
||||
安装 `pm2`(示例:`bun add -d pm2`)后,可在项目内直接运行:
|
||||
```bash
|
||||
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||
```
|
||||
|
||||
亦可直接调用脚本:
|
||||
|
||||
或调用预置脚本:
|
||||
```bash
|
||||
bun run pm2:start:trend
|
||||
bun run pm2:start:maker
|
||||
bun run pm2:start:offset
|
||||
```
|
||||
|
||||
根据需要调整 `--name`、`--cwd`、`--restart-delay` 等参数,完成后可执行 `pm2 save` 持久化进程列表。
|
||||
完成配置后可执行 `pm2 save` 持久化进程列表。
|
||||
|
||||
## 测试
|
||||
项目使用 Vitest:
|
||||
```bash
|
||||
bun run test # 运行全部测试
|
||||
bun run test
|
||||
bun x vitest --watch
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
- 你需要至少 50-100 USDT 的资金才能运行策略
|
||||
- 请在交易所自行设置 50 倍左右的杠杆,本策略不包含杠杆设置
|
||||
- 请确保你电脑/服务器的时间是准确的真实世界时间
|
||||
- 持仓方式需要保持单向持仓
|
||||
- 至少准备 50–100 USDT 资金以覆盖策略运行需求。
|
||||
- 杠杆需在交易所提前设置(建议 ~50 倍),程序不会自动调整。
|
||||
- 请确保服务器/电脑时间同步真实世界时间,避免签名过期。
|
||||
- 账户需保持单向持仓模式。
|
||||
- `.env` 未读取:确认文件位于项目根目录且变量名无误。
|
||||
- API 拒绝访问:检查交易所后台权限,确保开启合约读写。
|
||||
- 精度错误:同步交易对的最小价格与数量步长。
|
||||
更多排查步骤可参考 [简明上手指南](simple-readme.md)。
|
||||
更多排查细节可参见 [简明上手指南](simple-readme.md)。
|
||||
|
||||
## 社区与支持
|
||||
- Telegram 交流群:[https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||
- 反馈或新特性建议请提交 Issue 或 PR
|
||||
- 欢迎通过 Issue 或 PR 提交反馈、特性建议
|
||||
|
||||
## 风险提示
|
||||
量化交易具备风险。建议在仿真或小额账户中验证策略表现,妥善保管 API 密钥,仅开启必要权限。
|
||||
量化交易具备风险。请先在仿真或小额账户中验证策略表现,妥善保管 API 密钥,仅开启必要权限。
|
||||
|
||||
+148
-67
@@ -1,39 +1,67 @@
|
||||
# ritmex-bot
|
||||
|
||||
A Bun-powered trading workstation for Aster perpetual contracts that ships two production-ready agents: an SMA30 trend follower and a dual-sided market maker. The CLI is built with Ink, synchronises risk state from the exchange, and automatically recovers from restarts or disconnects.
|
||||
**Language Setting**: Set `LANG=en` in your `.env` file to display the CLI interface in English.
|
||||
|
||||
A Bun-powered multi-exchange perpetuals workstation that ships an SMA30 trend engine, a Guardian stop sentinel, and two market-making modes. It offers instant restarts, realtime market data, structured logging, and an Ink-based CLI dashboard.
|
||||
|
||||
If you'd like to support this project and get fee discounts, please consider using these referral links:
|
||||
|
||||
* [Lighter referral link](https://app.lighter.xyz/?referral=111909FA)
|
||||
* [Aster referral link](https://www.asterdex.com/en/referral/4665f3)
|
||||
* [StandX referral link](https://standx.com/referral?code=xingxingjun)
|
||||
* [Binance referral link](https://www.binance.com/join?ref=KNKCA9XC)
|
||||
* [GRVT referral link](https://grvt.io/exchange/sign-up?ref=sea)
|
||||
* [Nado referral link](https://app.nado.xyz?join=LKbIUs5)
|
||||
* [Backpack referral link](https://backpack.exchange/join/ritmex)
|
||||
* [edgex referral link](https://pro.edgex.exchange/referral/BULL)
|
||||
* [Paradex referral link](https://paradex.io/ref/xingxingjun)
|
||||
* [Apex referral link](https://join.omni.apex.exchange/SEA)
|
||||
|
||||
## Documentation Map
|
||||
- [中文 README](README.md)
|
||||
- [Beginner-friendly Quick Start](simple-readme.md)
|
||||
- [Grid Trading Strategy Guide](grid-trading.md)
|
||||
|
||||
## Highlights
|
||||
- **Live market data & risk sync** via websocket feeds with REST fallbacks, full reconciliation on restart.
|
||||
- **Trend engine** featuring SMA30 entries, fixed stop loss, trailing stop, Bollinger bandwidth gate, and profit-lock stepping.
|
||||
- **Market-making loop** with adaptive quote chasing, loss caps, and automatic order healing.
|
||||
- **Extensible architecture** decoupling exchange adapters, engines, and the Ink CLI for easy venue or strategy additions.
|
||||
- **Live data & risk sync** via websockets with REST fallbacks and full reconciliation on restart.
|
||||
- **Trend strategy** featuring SMA30 entries, fixed stop loss, trailing stop, Bollinger bandwidth gate, and profit-lock stepping.
|
||||
- **Guardian strategy** that never opens trades but mirrors your live exposure, ensuring every position has a synced stop loss and trailing stop.
|
||||
- **Market-making loop** with dual-sided quote chasing, loss caps, and automatic order healing.
|
||||
- **Modular architecture** decoupling engines, exchange adapters, and the Ink CLI for easy venue or strategy extensions.
|
||||
|
||||
## Supported Exchanges
|
||||
| Exchange | Contract Type | Required Environment Variables | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| Aster | USDT perpetuals | `ASTER_API_KEY`, `ASTER_API_SECRET` | Default venue; works with the bootstrap script |
|
||||
| StandX | USD perpetuals | `STANDX_TOKEN` | Uses JWT token auth; prefer websocket streams |
|
||||
| GRVT | USDT perpetuals | `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID` | Switch `GRVT_ENV` between `prod` and `testnet` |
|
||||
| Lighter | zkLighter perpetuals | `LIGHTER_ACCOUNT_INDEX`, `LIGHTER_API_PRIVATE_KEY` | Defaults to `LIGHTER_ENV=testnet` |
|
||||
| Backpack | USDC perpetuals | `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, `BACKPACK_PASSWORD` | Set `BACKPACK_SANDBOX=true` for the sandbox |
|
||||
| Paradex | StarkEx perpetuals | `PARADEX_PRIVATE_KEY`, `PARADEX_WALLET_ADDRESS` | Toggle `PARADEX_SANDBOX=true` for the testnet |
|
||||
| Nado | USDC perpetuals | `NADO_SIGNER_PRIVATE_KEY`, `NADO_SUBACCOUNT_OWNER` | Switch `NADO_ENV` between `inkMainnet` and `inkTestnet` |
|
||||
|
||||
## Requirements
|
||||
- Bun ≥ 1.2 (`bun`, `bunx` available on PATH)
|
||||
- Bun >= 1.2 (both `bun` and `bunx` on PATH)
|
||||
- macOS, Linux, or Windows via WSL (native Windows works but WSL is recommended)
|
||||
- Node.js is optional unless your environment requires it for tooling
|
||||
- Node.js is optional unless your tooling requires it
|
||||
|
||||
## One-Line Bootstrap (macOS / Linux / WSL)
|
||||
## Quick Start
|
||||
### One-line bootstrap (macOS / Linux / WSL)
|
||||
```bash
|
||||
curl -fsSL https://github.com/discountry/ritmex-bot/raw/refs/heads/main/setup.sh | bash
|
||||
```
|
||||
The script installs Bun, project dependencies, collects Aster API credentials, generates `.env`, and launches the CLI. Prepare your API Key/Secret before running.
|
||||
The script installs Bun, project dependencies, collects Aster API credentials, generates `.env`, and launches the CLI. Prepare the relevant exchange API keys before running it.
|
||||
|
||||
## Manual Installation
|
||||
### Manual installation
|
||||
1. **Clone the repository**
|
||||
```bash
|
||||
git clone https://github.com/discountry/ritmex-bot.git
|
||||
cd ritmex-bot
|
||||
```
|
||||
Alternatively download the ZIP from GitHub and extract it manually.
|
||||
Alternatively, download the ZIP from GitHub and extract it manually.
|
||||
2. **Install Bun**
|
||||
- macOS / Linux: `curl -fsSL https://bun.sh/install | bash`
|
||||
- Windows PowerShell: `powershell -c "irm bun.sh/install.ps1 | iex"`
|
||||
Re-open the terminal and confirm `bun -v` prints a version.
|
||||
Re-open the terminal and verify `bun -v` prints a version.
|
||||
3. **Install dependencies**
|
||||
```bash
|
||||
bun install
|
||||
@@ -42,67 +70,124 @@ The script installs Bun, project dependencies, collects Aster API credentials, g
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
Edit `.env` with your exchange credentials and overrides.
|
||||
Edit `.env` with the exchange credentials and overrides you plan to use.
|
||||
5. **Launch the CLI**
|
||||
```bash
|
||||
bun run index.ts
|
||||
```
|
||||
Use the arrow keys to pick a strategy, `Enter` to start, `Esc` to return to the menu, and `Ctrl+C` to exit.
|
||||
Use the arrow keys to pick a strategy, `Enter` to start, `Esc` to go back, and `Ctrl+C` to exit.
|
||||
|
||||
## Environment Variables
|
||||
The most important settings shipped in `.env.example` are summarised below:
|
||||
## Shared Configuration
|
||||
`.env.example` captures all defaults; the most common settings are summarised below.
|
||||
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `ASTER_API_KEY` / `ASTER_API_SECRET` | Required Aster exchange credentials |
|
||||
| `TRADE_SYMBOL` | Contract symbol, defaults to `BTCUSDT` |
|
||||
| `EXCHANGE` | Choose the venue (`aster` / `standx` / `grvt` / `lighter` / `backpack` / `paradex` / `nado`) |
|
||||
| `TRADE_SYMBOL` | Contract symbol (defaults to `BTCUSDT`) |
|
||||
| `TRADE_AMOUNT` | Order size in base asset units |
|
||||
| `LOSS_LIMIT` | Max per-trade loss (USDT) before forced close |
|
||||
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | Trailing stop trigger amount (USDT) and pullback percentage |
|
||||
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | Move the base stop once unrealised PnL exceeds this trigger |
|
||||
| `BOLLINGER_LENGTH` / `BOLLINGER_STD_MULTIPLIER` | Window size and std-dev multiplier for bandwidth filtering |
|
||||
| `MIN_BOLLINGER_BANDWIDTH` | Minimum bandwidth ratio required before opening a new position |
|
||||
| `LOSS_LIMIT` | Max per-trade loss in USDT before forced close |
|
||||
| `TRAILING_PROFIT` / `TRAILING_CALLBACK_RATE` | Trailing stop trigger (USDT) and pullback percentage |
|
||||
| `PROFIT_LOCK_TRIGGER_USD` / `PROFIT_LOCK_OFFSET_USD` | Profit lock trigger and offset thresholds |
|
||||
| `BOLLINGER_*` | Bollinger bandwidth filters for the trend engine |
|
||||
| `PRICE_TICK` / `QTY_STEP` | Exchange precision filters for price and quantity |
|
||||
| `POLL_INTERVAL_MS` | Trend engine polling cadence in milliseconds |
|
||||
| `MAX_CLOSE_SLIPPAGE_PCT` | Allowed deviation vs mark price when closing |
|
||||
| `MAKER_*` | Maker strategy knobs: chase threshold, quote offsets, refresh cadence, etc. |
|
||||
| `MAKER_*` | Maker-specific knobs (quote offsets, refresh cadence, slippage guard, etc.) |
|
||||
|
||||
To trade on GRVT, set `EXCHANGE=grvt` and populate `GRVT_API_KEY`, `GRVT_API_SECRET`, `GRVT_SUB_ACCOUNT_ID`, plus any optional overrides documented in `.env.example`.
|
||||
|
||||
> Tip: you can temporarily override the exchange via CLI flags (takes precedence over environment):
|
||||
> CLI flags override environment variables at runtime:
|
||||
> ```bash
|
||||
> bun run index.ts --exchange grvt
|
||||
> bun run index.ts -e lighter
|
||||
> bun run index.ts --exchange grvt --strategy maker
|
||||
> bun run index.ts -e lighter -s offset-maker --silent
|
||||
> ```
|
||||
|
||||
## Common Commands
|
||||
## Exchange Setup Guides
|
||||
|
||||
### Aster
|
||||
1. Keep `EXCHANGE=aster` (default value).
|
||||
2. Supply `ASTER_API_KEY` and `ASTER_API_SECRET`.
|
||||
3. Adjust `TRADE_SYMBOL`, `PRICE_TICK`, and `QTY_STEP` to match the requested market.
|
||||
4. The bootstrap script auto-populates these variables; manual installs must maintain them.
|
||||
|
||||
### StandX
|
||||
|
||||
* [StandX Maker Points Strategy Guide](docs/standx/maker-points-guide.md)
|
||||
|
||||
The strategy requires a StandX API Token and signing private key to place orders.
|
||||
|
||||
**How to obtain (using StandX's official API generation feature):**
|
||||
1. Open the StandX official API creation page: https://standx.com/user/session
|
||||
2. Connect your wallet and log in
|
||||
3. Click the **"Generate API Token"** button
|
||||
4. The page will display the following information:
|
||||
- **Token** (JWT string starting with `eyJ`) → Fill in `STANDX_TOKEN`
|
||||
- **Ed25519 Private Key** (Base58 format, like `HdsyJD7oWgT...`) → Fill in `STANDX_REQUEST_PRIVATE_KEY`
|
||||
- **Creation date** and **Validity days** → Used to configure token expiry reminders
|
||||
|
||||
> The Ed25519 Private Key is an auto-generated signing key used only for trade request signatures. Your assets remain in your main wallet and are completely safe.
|
||||
|
||||
Please keep these credentials safe and do not share them with anyone.
|
||||
|
||||
**Configuration steps:**
|
||||
1. Set `EXCHANGE=standx`.
|
||||
2. Provide `STANDX_TOKEN` (JWT token for perps API).
|
||||
3. Provide `STANDX_REQUEST_PRIVATE_KEY` (Ed25519 signing private key, Base58 format).
|
||||
4. Set `STANDX_SYMBOL` (defaults to `BTC-USD`) and align `PRICE_TICK` / `QTY_STEP`.
|
||||
5. Recommended: configure token expiry settings:
|
||||
- `STANDX_TOKEN_CREATE_DATE` (creation date, format `YYYY-MM-DD`)
|
||||
- `STANDX_TOKEN_VALIDITY_DAYS` (validity days)
|
||||
6. Optional: `STANDX_BASE_URL`, `STANDX_WS_URL`, or `STANDX_SESSION_ID` for custom endpoints.
|
||||
|
||||
### GRVT
|
||||
1. Set `EXCHANGE=grvt` inside `.env`.
|
||||
2. Fill `GRVT_API_KEY`, `GRVT_API_SECRET`, and `GRVT_SUB_ACCOUNT_ID`.
|
||||
3. Use `GRVT_ENV=testnet` when targeting the test environment, and align `GRVT_INSTRUMENT` / `GRVT_SYMBOL`.
|
||||
4. Optional: provide `GRVT_COOKIE` or a custom `GRVT_SIGNER_PATH` when reusing an existing session.
|
||||
|
||||
### Lighter
|
||||
1. Set `EXCHANGE=lighter`.
|
||||
2. Provide `LIGHTER_ACCOUNT_INDEX` and `LIGHTER_API_PRIVATE_KEY` (40-byte hex private key). `LIGHTER_ACCOUNT_INDEX` is your account index, which you can find by opening DevTools (F12) on the official website and observing API requests. `LIGHTER_API_PRIVATE_KEY` is your API private key.
|
||||
3. Switch `LIGHTER_ENV` to `mainnet`, `staging`, or `dev` when necessary; override `LIGHTER_BASE_URL` if endpoints differ.
|
||||
4. `LIGHTER_SYMBOL` defaults to `BTCUSDT`; override price/size decimals when markets differ.
|
||||
|
||||
### Backpack
|
||||
1. Set `EXCHANGE=backpack`.
|
||||
2. Populate `BACKPACK_API_KEY`, `BACKPACK_API_SECRET`, and `BACKPACK_PASSWORD`; add `BACKPACK_SUBACCOUNT` if you trade from a subaccount (defaults to main account ID).
|
||||
3. Toggle `BACKPACK_SANDBOX=true` for the sandbox environment and verify `BACKPACK_SYMBOL` matches the contract (defaults to `BTC_USD_PERP`).
|
||||
4. Enable `BACKPACK_DEBUG=true` for verbose adapter logging.
|
||||
|
||||
### Paradex
|
||||
1. Set `EXCHANGE=paradex`.
|
||||
2. Provide `PARADEX_PRIVATE_KEY` (EVM private key) and `PARADEX_WALLET_ADDRESS`. Note: These are your EVM wallet address and private key. It is recommended to create a brand new wallet and avoid storing unrelated assets in it.
|
||||
3. The adapter connects to mainnet by default; enable `PARADEX_SANDBOX=true` and adjust `PARADEX_SYMBOL` for testnet usage.
|
||||
4. Advanced tuning: use `PARADEX_USE_PRO`, `PARADEX_RECONNECT_DELAY_MS`, or debug flags as needed.
|
||||
|
||||
### Nado
|
||||
1. Set `EXCHANGE=nado`.
|
||||
2. On the Nado web app (trading interface), open DevTools (F12) -> switch to the `Application` tab -> `Local Storage`, locate `nado.userSettings`, then grab the `privateKey` field from its JSON value and paste it into `.env` as `NADO_SIGNER_PRIVATE_KEY`.
|
||||
3. Provide `NADO_SUBACCOUNT_OWNER` (or `NADO_EVM_ADDRESS`).
|
||||
4. Select network via `NADO_ENV=inkMainnet` (mainnet) or `inkTestnet` (testnet).
|
||||
5. Set `NADO_SYMBOL` using Nado product symbols like `BTC-PERP` (it also accepts `BTCUSDT0` and maps it to `BTC-PERP`).
|
||||
|
||||
## Command Cheatsheet
|
||||
```bash
|
||||
bun run index.ts # Launch the CLI
|
||||
bun run start # Same as above
|
||||
bun run dev # Development entry point
|
||||
bun x vitest run # Execute the Vitest suite
|
||||
bun run index.ts # Launch the CLI (default entrypoint)
|
||||
bun run start # Alias for bun run index.ts
|
||||
bun run dev # Development entrypoint
|
||||
bun x vitest run # Execute the full Vitest suite
|
||||
```
|
||||
|
||||
## Silent & Background Execution
|
||||
### Direct silent launch
|
||||
Skip the Ink menu and start a strategy straight from the CLI:
|
||||
|
||||
Skip the Ink menu and start a strategy directly:
|
||||
```bash
|
||||
bun run index.ts --strategy trend --silent # Trend engine
|
||||
bun run index.ts --strategy maker --silent # Maker engine
|
||||
bun run index.ts --strategy offset-maker --silent # Offset maker engine
|
||||
```
|
||||
|
||||
Combine with `--exchange/-e` to explicitly choose the venue (overrides `EXCHANGE`/`TRADE_EXCHANGE` from `.env`):
|
||||
|
||||
```bash
|
||||
bun run index.ts --exchange grvt --strategy maker --silent
|
||||
bun run index.ts -e lighter -s offset-maker --silent
|
||||
bun run index.ts --strategy trend --silent
|
||||
bun run index.ts --strategy maker --silent
|
||||
bun run index.ts --strategy offset-maker --silent
|
||||
```
|
||||
Combine with `--exchange/-e` to pin the venue for that run.
|
||||
|
||||
### Package scripts
|
||||
Convenience aliases are exposed in `package.json`:
|
||||
|
||||
Convenience aliases exposed via `package.json`:
|
||||
```bash
|
||||
bun run start:trend:silent
|
||||
bun run start:maker:silent
|
||||
@@ -110,42 +195,38 @@ bun run start:offset:silent
|
||||
```
|
||||
|
||||
### Daemonising with pm2
|
||||
Install `pm2` locally (e.g. `bun add -d pm2`) and launch without a global install:
|
||||
|
||||
Install `pm2` locally (e.g. `bun add -d pm2`) and launch the process:
|
||||
```bash
|
||||
bunx pm2 start bun --name ritmex-trend --cwd . --restart-delay 5000 -- run index.ts --strategy trend --silent
|
||||
```
|
||||
|
||||
You can also reuse the bundled scripts:
|
||||
|
||||
You can also call the bundled scripts:
|
||||
```bash
|
||||
bun run pm2:start:trend
|
||||
bun run pm2:start:maker
|
||||
bun run pm2:start:offset
|
||||
```
|
||||
|
||||
Adjust `--name`, `--cwd`, or `--restart-delay` to suit your environment and run `pm2 save` if you want the process to auto-start after reboot.
|
||||
Run `pm2 save` afterwards if you want the process list to survive reboots.
|
||||
|
||||
## Testing
|
||||
Vitest powers the unit tests:
|
||||
Powered by Vitest:
|
||||
```bash
|
||||
bun run test
|
||||
bun x vitest --watch
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
- You need at least 50–100 USDT of capital before deploying a live strategy.
|
||||
- Set leverage on the exchange beforehand (around 50x is recommended); the bot does not change it for you.
|
||||
- Keep server/desktop time in sync with real-world time to avoid signature errors.
|
||||
- Make sure the exchange account is in one-way position mode.
|
||||
- **Env not loading**: ensure `.env` resides in the repository root and variable names are spelled correctly.
|
||||
- **Order rejected for precision**: align `PRICE_TICK`, `QTY_STEP`, and `TRADE_SYMBOL` with the exchange filters.
|
||||
- **Permission or auth errors**: double-check exchange API scopes.
|
||||
More step-by-step guidance is available in [simple-readme.md](simple-readme.md).
|
||||
- Keep at least 50-100 USDT in the account before deploying a live strategy.
|
||||
- Configure leverage on the exchange manually (~50x is recommended); the bot will not change it.
|
||||
- Ensure your server or workstation clock is in sync to avoid signature errors.
|
||||
- Accounts must run in one-way position mode.
|
||||
- **Env not loading**: make sure `.env` lives in the repo root and variable names are spelled correctly.
|
||||
- **Permission rejected**: confirm the API key has perpetual trading scopes enabled.
|
||||
- **Precision errors**: align `PRICE_TICK`, `QTY_STEP`, and `TRADE_SYMBOL` with the exchange filters.
|
||||
See [simple-readme.md](simple-readme.md) for more detailed walkthroughs.
|
||||
|
||||
## Community & Support
|
||||
- Telegram: [https://t.me/+4fdo0quY87o4Mjhh](https://t.me/+4fdo0quY87o4Mjhh)
|
||||
- Issues and PRs are welcome for bug reports and feature ideas
|
||||
- Issues and PRs are welcome for bug reports and feature requests
|
||||
|
||||
## Disclaimer
|
||||
Algorithmic trading carries risk. Validate strategies with paper accounts or small capital first, safeguard your API keys, and only grant the minimum required permissions.
|
||||
Algorithmic trading carries risk. Validate strategies with paper trading or small capital first, safeguard your API keys, and only grant the minimum required permissions.
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 0,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "ritmex-bot",
|
||||
"dependencies": {
|
||||
"@grvt/client": "^1.6.4",
|
||||
"@nadohq/client": "^0.1.0-alpha.41",
|
||||
"@noble/ed25519": "^3.0.0",
|
||||
"axios": "^1.12.2",
|
||||
"ccxt": "^4.5.5",
|
||||
"bignumber.js": "^9.3.1",
|
||||
"ccxt": "^4.5.12",
|
||||
"dotenv": "^17.2.2",
|
||||
"ethereum-cryptography": "^2.1.3",
|
||||
"ink": "^6.3.1",
|
||||
"react": "^19.1.1",
|
||||
"trading-signals": "^7.4.3",
|
||||
"viem": "^2.43.1",
|
||||
"ws": "^8.18.3",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -23,7 +29,9 @@
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-qI/5TaaaCZE4yeSZ83lu0+xi1r88JSxUjnH4OP/iZF7+KKZ75u3ee5isd0LxX+6N8U0npL61YrpbthILHB6BnA=="],
|
||||
"@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="],
|
||||
|
||||
"@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.4", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-HTgrrTgZ9Jgeo6Z3oqbQ7lifOVvRR14vaDuBGPPUxk9Thm+vObaO4QfYYYWw4Zo5CWQDBEfsinFA6Gre+AqwNQ=="],
|
||||
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.10", "", { "os": "aix", "cpu": "ppc64" }, "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw=="],
|
||||
|
||||
@@ -77,12 +85,26 @@
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.10", "", { "os": "win32", "cpu": "x64" }, "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw=="],
|
||||
|
||||
"@grvt/client": ["@grvt/client@1.6.4", "", { "dependencies": { "axios": "^1.12.2" } }, "sha512-yZfEvsC/BtkcdMjoB7Eh6gshBraz/9810qCllvNXDx6OmdxR+NfxPO+ucNlPbyjEt1HfSVzHBWB1yGnVl1usNA=="],
|
||||
"@grvt/client": ["@grvt/client@1.6.25", "", { "dependencies": { "axios": "^1.13.2" } }, "sha512-LG3oZSJDq1Qz6mLiHDmg+v/Hp+Nd+yOp8GIvkBVyIdOcPVzuzC8UQD7IxvS+g5EfQQKLiKzXSjKLc39a11Uj1A=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@nadohq/client": ["@nadohq/client@0.1.0-alpha.45", "", { "dependencies": { "@nadohq/engine-client": "^0.1.0-alpha.45", "@nadohq/indexer-client": "^0.1.0-alpha.45", "@nadohq/shared": "^0.1.0-alpha.45", "@nadohq/trigger-client": "^0.1.0-alpha.45", "ts-mixer": "*" }, "peerDependencies": { "viem": "*" } }, "sha512-63S7eT5xSk3hxEXRLGDI94mPeu/zd1Nu4oUOZrY8wzhaT7Gl7PVP2snb8JH/PduVQzgs7+qblr4aTBI/j6QvLQ=="],
|
||||
|
||||
"@nadohq/engine-client": ["@nadohq/engine-client@0.1.0-alpha.45", "", { "dependencies": { "@nadohq/shared": "^0.1.0-alpha.45", "axios": "*", "ts-mixer": "*" }, "peerDependencies": { "viem": "*" } }, "sha512-GqaRrsB0Z2EgNY6wu4nG5OTc5hF+buNRMc0+jPLlOQonlPJAC4OiMCgCigm4nJecYwsdiQD05W3Z4YUqggxbyw=="],
|
||||
|
||||
"@nadohq/indexer-client": ["@nadohq/indexer-client@0.1.0-alpha.45", "", { "dependencies": { "@nadohq/engine-client": "^0.1.0-alpha.45", "@nadohq/shared": "^0.1.0-alpha.45", "axios": "*", "ts-mixer": "*" }, "peerDependencies": { "viem": "*" } }, "sha512-8PzG9taYh380lsKT8i4UvGfhZiPpt+zPisx+ErVqMifyhcJAhgMeYNcfijwMQFNJdgTaBvtI0R5JK98l4Y0YQw=="],
|
||||
|
||||
"@nadohq/shared": ["@nadohq/shared@0.1.0-alpha.45", "", { "peerDependencies": { "bignumber.js": "^9.3.0", "viem": "*" } }, "sha512-4Ml8dO9mkwnIidflpunwVMLX2KJA79b53T9xTBkH1Yzy0iOnbEE27r25wvgJgyszU0aznbPBv2++FZ0d0B5Zhg=="],
|
||||
|
||||
"@nadohq/trigger-client": ["@nadohq/trigger-client@0.1.0-alpha.45", "", { "dependencies": { "@nadohq/engine-client": "^0.1.0-alpha.45", "@nadohq/shared": "^0.1.0-alpha.45", "axios": "*", "ts-mixer": "*" }, "peerDependencies": { "viem": "*" } }, "sha512-Xt8W9zxOZhMvoXSRnx9kUR26wWuHc3EgTVTBbUXt7SlI9v3IP9I3idS6SU3j2f08flcvNqRmC2EbXyYDC2XeyQ=="],
|
||||
|
||||
"@noble/ciphers": ["@noble/ciphers@1.3.0", "", {}, "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw=="],
|
||||
|
||||
"@noble/curves": ["@noble/curves@1.4.2", "", { "dependencies": { "@noble/hashes": "1.4.0" } }, "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw=="],
|
||||
|
||||
"@noble/ed25519": ["@noble/ed25519@3.0.0", "", {}, "sha512-QyteqMNm0GLqfa5SoYbSC3+Pvykwpn95Zgth4MFVSMKBB75ELl9tX1LAVsN4c3HXOrakHsF2gL4zWDAYCcsnzg=="],
|
||||
|
||||
"@noble/hashes": ["@noble/hashes@1.4.0", "", {}, "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.0", "", { "os": "android", "cpu": "arm" }, "sha512-VxDYCDqOaR7NXzAtvRx7G1u54d2kEHopb28YH/pKzY6y0qmogP3gG7CSiWsq9WvDFxOQMpNEyjVAHZFXfH3o/A=="],
|
||||
@@ -135,7 +157,7 @@
|
||||
|
||||
"@scure/bip39": ["@scure/bip39@1.3.0", "", { "dependencies": { "@noble/hashes": "~1.4.0", "@scure/base": "~1.1.6" } }, "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="],
|
||||
"@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="],
|
||||
|
||||
"@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="],
|
||||
|
||||
@@ -149,8 +171,6 @@
|
||||
|
||||
"@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="],
|
||||
|
||||
"@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="],
|
||||
|
||||
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
|
||||
|
||||
"@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="],
|
||||
@@ -165,7 +185,9 @@
|
||||
|
||||
"@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
|
||||
|
||||
"ansi-escapes": ["ansi-escapes@7.1.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-YdhtCd19sKRKfAAUsrcC1wzm4JuzJoiX4pOJqIoW2qmKj5WzG/dL8uUJ0361zaXtHqK7gEhOwtAtz7t3Yq3X5g=="],
|
||||
"abitype": ["abitype@1.2.3", "", { "peerDependencies": { "typescript": ">=5.0.4", "zod": "^3.22.0 || ^4.0.0" }, "optionalPeers": ["typescript", "zod"] }, "sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg=="],
|
||||
|
||||
"ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="],
|
||||
|
||||
"ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
@@ -177,15 +199,17 @@
|
||||
|
||||
"auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="],
|
||||
|
||||
"axios": ["axios@1.12.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw=="],
|
||||
"axios": ["axios@1.13.4", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="],
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="],
|
||||
|
||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"ccxt": ["ccxt@4.5.5", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-AyhwTFLkx4sO985ImIOfumEBox7AHD/iqk5tPGICObUSZG6wTXg0aRzU8Hjz974aCMG4msFwLk3A/iXPKAU4wA=="],
|
||||
"ccxt": ["ccxt@4.5.35", "", { "dependencies": { "ws": "^8.8.1" } }, "sha512-aFn1jq/vR9wD3Vxu/2UFoS8snqlvfYn/iYrNhyZZfzB3N+kAHhP5+sAgO6ZwNHkHiUT9IPPahC1ZSFdPtjpIYA=="],
|
||||
|
||||
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
|
||||
|
||||
@@ -197,7 +221,7 @@
|
||||
|
||||
"cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="],
|
||||
|
||||
"cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="],
|
||||
"cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="],
|
||||
|
||||
"code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="],
|
||||
|
||||
@@ -205,15 +229,13 @@
|
||||
|
||||
"convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="],
|
||||
|
||||
"csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"dotenv": ["dotenv@17.2.2", "", {}, "sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q=="],
|
||||
"dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
@@ -241,6 +263,8 @@
|
||||
|
||||
"ethereum-cryptography": ["ethereum-cryptography@2.2.1", "", { "dependencies": { "@noble/curves": "1.4.2", "@noble/hashes": "1.4.0", "@scure/bip32": "1.4.0", "@scure/bip39": "1.3.0" } }, "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="],
|
||||
|
||||
"expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
@@ -269,12 +293,14 @@
|
||||
|
||||
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
|
||||
|
||||
"ink": ["ink@6.3.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.0", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.32.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-3wGwITGrzL6rkWsi2gEKzgwdafGn4ZYd3u4oRp+sOPvfoxEHlnoB5Vnk9Uy5dMRUhDOqF3hqr4rLQ4lEzBc2sQ=="],
|
||||
"ink": ["ink@6.6.0", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
|
||||
|
||||
"is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="],
|
||||
|
||||
"isows": ["isows@1.0.7", "", { "peerDependencies": { "ws": "*" } }, "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg=="],
|
||||
|
||||
"js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="],
|
||||
|
||||
"loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="],
|
||||
@@ -295,6 +321,8 @@
|
||||
|
||||
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
|
||||
|
||||
"ox": ["ox@0.11.3", "", { "dependencies": { "@adraffy/ens-normalize": "^1.11.0", "@noble/ciphers": "^1.3.0", "@noble/curves": "1.9.1", "@noble/hashes": "^1.8.0", "@scure/bip32": "^1.7.0", "@scure/bip39": "^1.6.0", "abitype": "^1.2.3", "eventemitter3": "5.0.1" }, "peerDependencies": { "typescript": ">=5.4.0" }, "optionalPeers": ["typescript"] }, "sha512-1bWYGk/xZel3xro3l8WGg6eq4YEKlaqvyMtVhfMFpbJzK2F6rj4EDRtqDCWVEJMkzcmEi9uW2QxsqELokOlarw=="],
|
||||
|
||||
"patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
@@ -309,15 +337,15 @@
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||
|
||||
"react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="],
|
||||
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
|
||||
|
||||
"react-reconciler": ["react-reconciler@0.32.0", "", { "dependencies": { "scheduler": "^0.26.0" }, "peerDependencies": { "react": "^19.1.0" } }, "sha512-2NPMOzgTlG0ZWdIf3qG+dcbLSoAc/uLfOwckc3ofy5sSK0pLJqnQLpUFxvGcN2rlXSjnVtGeeFLNimCQEj5gOQ=="],
|
||||
"react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="],
|
||||
|
||||
"restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="],
|
||||
|
||||
"rollup": ["rollup@4.52.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.0", "@rollup/rollup-android-arm64": "4.52.0", "@rollup/rollup-darwin-arm64": "4.52.0", "@rollup/rollup-darwin-x64": "4.52.0", "@rollup/rollup-freebsd-arm64": "4.52.0", "@rollup/rollup-freebsd-x64": "4.52.0", "@rollup/rollup-linux-arm-gnueabihf": "4.52.0", "@rollup/rollup-linux-arm-musleabihf": "4.52.0", "@rollup/rollup-linux-arm64-gnu": "4.52.0", "@rollup/rollup-linux-arm64-musl": "4.52.0", "@rollup/rollup-linux-loong64-gnu": "4.52.0", "@rollup/rollup-linux-ppc64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-gnu": "4.52.0", "@rollup/rollup-linux-riscv64-musl": "4.52.0", "@rollup/rollup-linux-s390x-gnu": "4.52.0", "@rollup/rollup-linux-x64-gnu": "4.52.0", "@rollup/rollup-linux-x64-musl": "4.52.0", "@rollup/rollup-openharmony-arm64": "4.52.0", "@rollup/rollup-win32-arm64-msvc": "4.52.0", "@rollup/rollup-win32-ia32-msvc": "4.52.0", "@rollup/rollup-win32-x64-gnu": "4.52.0", "@rollup/rollup-win32-x64-msvc": "4.52.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-+IuescNkTJQgX7AkIDtITipZdIGcWF0pnVvZTWStiazUmcGA2ag8dfg0urest2XlXUi9kuhfQ+qmdc5Stc3z7g=="],
|
||||
|
||||
"scheduler": ["scheduler@0.26.0", "", {}, "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="],
|
||||
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||
|
||||
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
||||
|
||||
@@ -333,7 +361,7 @@
|
||||
|
||||
"std-env": ["std-env@3.9.0", "", {}, "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw=="],
|
||||
|
||||
"string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
"string-width": ["string-width@8.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-KpqHIdDL9KwYk22wEOg/VIqYbrnLeSApsKT/bSj6Ez7pn3CftUiLAv2Lccpq1ALcpLV9UX1Ppn92npZWu2w/aw=="],
|
||||
|
||||
"strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="],
|
||||
|
||||
@@ -351,12 +379,18 @@
|
||||
|
||||
"tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="],
|
||||
|
||||
"trading-signals": ["trading-signals@7.4.3", "", {}, "sha512-kOyzd85qhuhU7yGkB5z74MVP6j30xBCfZy5+bgBnvfxY1ZdvaQsdD+C5j+CoIRtCAgSOuxB7jmPpBrqO6h17sQ=="],
|
||||
|
||||
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||
|
||||
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
|
||||
|
||||
"typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="],
|
||||
|
||||
"undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="],
|
||||
|
||||
"viem": ["viem@2.45.1", "", { "dependencies": { "@noble/curves": "1.9.1", "@noble/hashes": "1.8.0", "@scure/bip32": "1.7.0", "@scure/bip39": "1.6.0", "abitype": "1.2.3", "isows": "1.0.7", "ox": "0.11.3", "ws": "8.18.3" }, "peerDependencies": { "typescript": ">=5.0.4" }, "optionalPeers": ["typescript"] }, "sha512-LN6Pp7vSfv50LgwhkfSbIXftAM5J89lP9x8TeDa8QM7o41IxlHrDh0F9X+FfnCWtsz11pEVV5sn+yBUoOHNqYA=="],
|
||||
|
||||
"vite": ["vite@7.1.7", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA=="],
|
||||
|
||||
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
|
||||
@@ -369,12 +403,38 @@
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
|
||||
|
||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||
"ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="],
|
||||
|
||||
"yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="],
|
||||
|
||||
"cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="],
|
||||
"ox/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="],
|
||||
|
||||
"cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="],
|
||||
"ox/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
|
||||
"ox/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="],
|
||||
|
||||
"ox/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="],
|
||||
|
||||
"viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="],
|
||||
|
||||
"viem/@noble/hashes": ["@noble/hashes@1.8.0", "", {}, "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A=="],
|
||||
|
||||
"viem/@scure/bip32": ["@scure/bip32@1.7.0", "", { "dependencies": { "@noble/curves": "~1.9.0", "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw=="],
|
||||
|
||||
"viem/@scure/bip39": ["@scure/bip39@1.6.0", "", { "dependencies": { "@noble/hashes": "~1.8.0", "@scure/base": "~1.2.5" } }, "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A=="],
|
||||
|
||||
"viem/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||
|
||||
"widest-line/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="],
|
||||
|
||||
"ox/@scure/bip32/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="],
|
||||
|
||||
"ox/@scure/bip39/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="],
|
||||
|
||||
"viem/@scure/bip32/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="],
|
||||
|
||||
"viem/@scure/bip39/@scure/base": ["@scure/base@1.2.6", "", {}, "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg=="],
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
|
||||
*.go text eol=lf
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.idea
|
||||
vendor
|
||||
build/*
|
||||
!build/.keep
|
||||
!build/.keep
|
||||
/build
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,9 +1,94 @@
|
||||
# lighter-go
|
||||
|
||||
In its current form, this repo serves as a starting point for anyone who wants to trade on Lighter using GO.
|
||||
It covers all the signing procedures in order to trade on Lighter with an API key.
|
||||
Minimal HTTP calls are implemented
|
||||
On chain support, like depositing on Ethereum or modifying an API key directly with an Ethereum Tx are not supported yet.
|
||||
This repository serves as the reference implementation of signing & hashing of Lighter transactions.
|
||||
The sharedlib is compiled for a variety of platforms.
|
||||
- macOS (darwin) dynamic library (.dylib) for arm architecture (M processor, not Intel)
|
||||
- linux shared object (.so) for both amd64 and arm architectures
|
||||
- windows .ddl (dynamic-link library) for amd64 architecture
|
||||
|
||||
At the moment, its main purpose is to offer visibility on the code behind the precompiled libraries used by the Python SDK.
|
||||
If you'd like to compile your own binaries, the commands are in the `justfile`
|
||||
The go SDK implements just the core signing, as well as a small HTTP client so that users can:
|
||||
- not specify the nonce of the transaction (this will result in an HTTP call, so beware)
|
||||
- check that a client was initialized correctly, by verifying that the given API key matches the one on the server
|
||||
|
||||
The [Python SDK](https://github.com/elliottech/lighter-python) offers support for HTTP and WebSocket functionality as well as [examples](https://github.com/elliottech/lighter-python/tree/main/examples) on how to generate the API keys, how to create and cancel orders, generate AUTH tokens for various HTTP/WS endpoints which require them.
|
||||
|
||||
All generated shared libraries follow the naming convention `lighter_signer_{os}_{arch}` where os is linux/windows/darwin and arch is amd64(x86) or arm64.\
|
||||
The build & accompanying `.h` files can be found in the release notes [here](https://github.com/elliottech/lighter-go/releases).\
|
||||
If you'd like to compile your own binaries, the commands are in the `justfile`.
|
||||
|
||||
|
||||
## Transactions
|
||||
```
|
||||
=== Client ===
|
||||
CreateClient
|
||||
CheckClient
|
||||
|
||||
=== API Key ===
|
||||
CreateAuthToken
|
||||
SignChangePubKey
|
||||
GenerateAPIKey
|
||||
|
||||
=== Order ===
|
||||
SignCreateOrder
|
||||
SignCreateGroupedOrders
|
||||
SignCancelOrder
|
||||
SignCancelAllOrders
|
||||
SignModifyOrder
|
||||
|
||||
=== Leverage & Margin ===
|
||||
SignUpdateLeverage
|
||||
SignUpdateMargin
|
||||
|
||||
=== Transfers ===
|
||||
SignWithdraw
|
||||
SignTransfer
|
||||
|
||||
=== Sub account & pools ===
|
||||
SignCreateSubAccount
|
||||
SignCreatePublicPool
|
||||
SignUpdatePublicPool
|
||||
SignMintShares
|
||||
SignBurnShares
|
||||
```
|
||||
|
||||
## How to specify an account
|
||||
Accounts are loaded into the signer by calling the `CreateClient` method. If you wish to load multiple API keys in the signer, you need to call the method multiple times, each time with the correct private key.
|
||||
|
||||
By default, signer will work out of the box with 1 client and no need to manage nonces in any specific way. Just pass `-1, 255, 0` for all methods (more explanations below).
|
||||
|
||||
You can call `CheckClient` to verify that the provided Private key & (apiKeyIndex, accountIndex) are configured correctly.
|
||||
This checks that the public key associated with the pair (apiKey,account) matches the one from the exchange.
|
||||
|
||||
The majority of methods receive 3 arguments at the end:
|
||||
- `nonce`
|
||||
- default `-1`
|
||||
- required to be strictly incremental
|
||||
- you can fetch the next nonce using `nextNonce` HTTP call
|
||||
- if default is passed, signer will do the HTTP call automatically
|
||||
- ideally the caller should manage nonces locally to avoid latency
|
||||
- `apiKeyIndex`
|
||||
- default `255`
|
||||
- specified which API key will be used for the specific transaction
|
||||
- all API keys are equal. Orders created by one API key can be canceled by others, for example
|
||||
- each API key has its own nonce
|
||||
- if default is passed, signer will use the default txClient
|
||||
- `accountIndex`
|
||||
- default `0`
|
||||
- specified which account will be used for the specific transaction
|
||||
- this can be a subaccount or a different main account all together
|
||||
- if default is passed, signer will use the default txClient
|
||||
|
||||
**Note:** in order to use the default client, you need to bash both the default values for `apiKeyIndex` and `accountIndex`
|
||||
|
||||
## Auth tokens
|
||||
|
||||
Auth tokens are used to call various HTTP & WS endpoints which hold sensitive information, like open orders.
|
||||
An auth token is valid for 8 hours.
|
||||
|
||||
`CreateAuthToken(deadline=0)` will result in a token that's valid for 7 hours from now.
|
||||
|
||||
Calling `CreateAuthToken` with an expiry 20 hours in the future will work, but the token will start to be valid in 12 hours, because the max accepted deadline duration by the server is 8 hours. \
|
||||
This still allows you to generate all the tokens ahead of time and use them accordingly. \
|
||||
Such an approach (both implementation & how to manage them) can be found in great details in the [python-sdk](https://github.com/elliottech/lighter-python/tree/main/examples/read-only-auth).
|
||||
|
||||
**Note:** auth tokens are bound to an API key. Changing the API key to something else **will invalidate** all generated auth tokens.
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
curve "github.com/elliottech/poseidon_crypto/curve/ecgfp5"
|
||||
schnorr "github.com/elliottech/poseidon_crypto/signature/schnorr"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
// SharedClientManager holds the global txClient and backupTxClients
|
||||
// This will be managed by both sharedlib and wasm builds
|
||||
// Supports multiple accounts and API keys with thread safety
|
||||
var (
|
||||
txClientMu sync.Mutex
|
||||
defaultTxClient *TxClient
|
||||
defaultClientPerAccount = make(map[int64]*TxClient)
|
||||
allTxClients map[int64]map[uint8]*TxClient // accountIndex -> apiKeyIndex -> client
|
||||
)
|
||||
|
||||
// GenerateAPIKey generates a new API key pair from a seed
|
||||
func GenerateAPIKey(seed string) (string, string, error) {
|
||||
var seedP *string
|
||||
if seed != "" {
|
||||
seedP = &seed
|
||||
}
|
||||
|
||||
key := curve.SampleScalar(seedP)
|
||||
publicKeyStr := hexutil.Encode(schnorr.SchnorrPkFromSk(key).ToLittleEndianBytes())
|
||||
privateKeyStr := hexutil.Encode(key.ToLittleEndianBytes())
|
||||
|
||||
return privateKeyStr, publicKeyStr, nil
|
||||
}
|
||||
|
||||
// GetClient retrieves a client for specific account and API key
|
||||
// If apiKeyIndex==255 && accountIndex==-1, returns default client
|
||||
func GetClient(apiKeyIndex uint8, accountIndex int64) (*TxClient, error) {
|
||||
txClientMu.Lock()
|
||||
defer txClientMu.Unlock()
|
||||
|
||||
if apiKeyIndex == 255 && accountIndex != -1 {
|
||||
client := defaultClientPerAccount[accountIndex]
|
||||
if client != nil {
|
||||
return client, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Special case: return default client
|
||||
if apiKeyIndex == 255 && accountIndex == -1 {
|
||||
if defaultTxClient == nil {
|
||||
return nil, fmt.Errorf("client is not created, call CreateClient() first")
|
||||
}
|
||||
return defaultTxClient, nil
|
||||
}
|
||||
|
||||
// Look up client in double map
|
||||
var c *TxClient
|
||||
if allTxClients[accountIndex] != nil {
|
||||
c = allTxClients[accountIndex][apiKeyIndex]
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("client is not created for apiKeyIndex: %v accountIndex: %v", apiKeyIndex, accountIndex)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// CreateClient creates a new TxClient and stores it
|
||||
// httpClientFactory is a function that creates an HTTP client from a URL string
|
||||
func CreateClient(httpClient MinimalHTTPClient, privateKey string, chainId uint32, apiKeyIndex uint8, accountIndex int64) (*TxClient, error) {
|
||||
if accountIndex <= 0 {
|
||||
return nil, fmt.Errorf("invalid account index")
|
||||
}
|
||||
|
||||
txClientInstance, err := NewTxClient(httpClient, privateKey, accountIndex, apiKeyIndex, chainId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error occurred when creating TxClient. err: %v", err)
|
||||
}
|
||||
|
||||
txClientMu.Lock()
|
||||
if allTxClients == nil {
|
||||
allTxClients = make(map[int64]map[uint8]*TxClient)
|
||||
}
|
||||
if allTxClients[accountIndex] == nil {
|
||||
allTxClients[accountIndex] = make(map[uint8]*TxClient)
|
||||
}
|
||||
allTxClients[accountIndex][apiKeyIndex] = txClientInstance
|
||||
|
||||
// Update default client (most recently created becomes default)
|
||||
defaultTxClient = txClientInstance
|
||||
defaultClientPerAccount[accountIndex] = txClientInstance
|
||||
txClientMu.Unlock()
|
||||
|
||||
return txClientInstance, nil
|
||||
}
|
||||
|
||||
// Check validates that the client exists and the API key matches the one on the server
|
||||
func (c *TxClient) Check() error {
|
||||
// check that the API key registered on Lighter matches this one
|
||||
publicKey, err := c.HTTP().GetApiKey(c.accountIndex, c.apiKeyIndex)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Api Keys. err: %v", err)
|
||||
}
|
||||
|
||||
pubKeyBytes := c.GetKeyManager().PubKeyBytes()
|
||||
pubKeyStr := hexutil.Encode(pubKeyBytes[:])
|
||||
pubKeyStr = strings.Replace(pubKeyStr, "0x", "", 1)
|
||||
|
||||
if publicKey != pubKeyStr {
|
||||
return fmt.Errorf("private key does not match the one on Lighter. ownPubKey: %s response: %+v", pubKeyStr, publicKey)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# HTTP
|
||||
The HTTP module is a simple implementation that aims to provide just 2 things:
|
||||
- `GetNextNonce` so that users can send transactions w/out calling managing nonces on their side
|
||||
- `GetApiKey` so that users can call `CheckClient` from other sources, which makes sure that the client was configured correctly.
|
||||
|
||||
Other usages, like sending trades, fetching open orders or any WebSocket operations should happen outside the core SDK.
|
||||
@@ -0,0 +1,45 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
core "github.com/elliottech/lighter-go/client"
|
||||
)
|
||||
|
||||
var (
|
||||
dialer = &net.Dialer{
|
||||
Timeout: 10 * time.Second,
|
||||
KeepAlive: 60 * time.Second,
|
||||
}
|
||||
transport = &http.Transport{
|
||||
DialContext: dialer.DialContext,
|
||||
MaxConnsPerHost: 1000,
|
||||
MaxIdleConnsPerHost: 100,
|
||||
IdleConnTimeout: 10 * time.Second,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: false},
|
||||
}
|
||||
|
||||
httpClient = &http.Client{
|
||||
Timeout: time.Second * 30,
|
||||
Transport: transport,
|
||||
}
|
||||
)
|
||||
|
||||
var _ core.MinimalHTTPClient = (*client)(nil)
|
||||
|
||||
type client struct {
|
||||
endpoint string
|
||||
}
|
||||
|
||||
func NewClient(baseUrl string) core.MinimalHTTPClient {
|
||||
if baseUrl == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &client{
|
||||
endpoint: baseUrl,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package http
|
||||
|
||||
const (
|
||||
CodeOK = 200
|
||||
)
|
||||
|
||||
type ResultCode struct {
|
||||
Code int32 `json:"code,example=200"`
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
type NextNonce struct {
|
||||
ResultCode
|
||||
Nonce int64 `json:"nonce,example=722"`
|
||||
}
|
||||
|
||||
type ApiKey struct {
|
||||
AccountIndex int64 `json:"account_index,example=3"`
|
||||
ApiKeyIndex uint8 `json:"api_key_index,example=0"`
|
||||
Nonce int64 `json:"nonce,example=722"`
|
||||
PublicKey string `json:"public_key"`
|
||||
}
|
||||
|
||||
type AccountApiKeys struct {
|
||||
ResultCode
|
||||
ApiKeys []*ApiKey `json:"api_keys"`
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func (c *client) parseResultStatus(respBody []byte) error {
|
||||
resultStatus := &ResultCode{}
|
||||
if err := json.Unmarshal(respBody, resultStatus); err != nil {
|
||||
return err
|
||||
}
|
||||
if resultStatus.Code != CodeOK {
|
||||
return errors.New(resultStatus.Message)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) getAndParseL2HTTPResponse(path string, params map[string]any, result interface{}) error {
|
||||
u, err := url.Parse(c.endpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.Path = path
|
||||
|
||||
q := u.Query()
|
||||
for k, v := range params {
|
||||
q.Set(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
u.RawQuery = q.Encode()
|
||||
resp, err := httpClient.Get(u.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return errors.New(string(body))
|
||||
}
|
||||
if err = c.parseResultStatus(body); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(body, result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) GetNextNonce(accountIndex int64, apiKeyIndex uint8) (int64, error) {
|
||||
result := &NextNonce{}
|
||||
err := c.getAndParseL2HTTPResponse("api/v1/nextNonce", map[string]any{"account_index": accountIndex, "api_key_index": apiKeyIndex}, result)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return result.Nonce, nil
|
||||
}
|
||||
|
||||
func (c *client) GetApiKey(accountIndex int64, apiKeyIndex uint8) (string, error) {
|
||||
result := &AccountApiKeys{}
|
||||
err := c.getAndParseL2HTTPResponse("api/v1/apikeys", map[string]any{"account_index": accountIndex, "api_key_index": apiKeyIndex}, result)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(result.ApiKeys) == 0 {
|
||||
return "", fmt.Errorf("no api keys returned")
|
||||
}
|
||||
return result.ApiKeys[0].PublicKey, nil
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package client
|
||||
|
||||
type MinimalHTTPClient interface {
|
||||
GetNextNonce(accountIndex int64, apiKeyIndex uint8) (int64, error)
|
||||
GetApiKey(accountIndex int64, apiKeyIndex uint8) (string, error)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/elliottech/lighter-go/signer"
|
||||
"github.com/elliottech/lighter-go/types"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultExpireTime is a public var, so it can be changed directly in the SDK if required.
|
||||
// The encouraged behaviour is the manually specify the TX deadline in types.TransactOpts.ExpiredAt
|
||||
DefaultExpireTime = time.Minute*10 - time.Second // we need to give a second margin, to eliminate millisecond differences
|
||||
)
|
||||
|
||||
type TxClient struct {
|
||||
apiClient MinimalHTTPClient
|
||||
chainId uint32
|
||||
keyManager signer.KeyManager
|
||||
accountIndex int64
|
||||
apiKeyIndex uint8
|
||||
}
|
||||
|
||||
// NewTxClient is linked to a specific (account, apiKey) pair
|
||||
// apiKeyPrivateKey should be hex-encoded bytes generated using `hexutil.Encode(TxClient.GetKeyManager().PrvKeyBytes())`
|
||||
func NewTxClient(apiClient MinimalHTTPClient, apiKeyPrivateKey string, accountIndex int64, apiKeyIndex uint8, chainId uint32) (*TxClient, error) {
|
||||
// remove 0x from private key, if any, and parse to bytes
|
||||
if len(apiKeyPrivateKey) < 2 {
|
||||
return nil, fmt.Errorf("empty private key")
|
||||
}
|
||||
if apiKeyPrivateKey[:2] == "0x" {
|
||||
apiKeyPrivateKey = apiKeyPrivateKey[2:]
|
||||
}
|
||||
|
||||
b, err := hex.DecodeString(apiKeyPrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyManager, err := signer.NewKeyManager(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &TxClient{
|
||||
apiClient: apiClient,
|
||||
apiKeyIndex: apiKeyIndex,
|
||||
accountIndex: accountIndex,
|
||||
chainId: chainId,
|
||||
keyManager: keyManager,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FullFillDefaultOps returns a usable TransactOpts object if none was provided.
|
||||
// This should not the be case for sharedlib, except for the nonce, which is optional.
|
||||
// Still, the behaviour is implemented, so it can be extended easily by extending the code GO SDK.
|
||||
func (c *TxClient) FullFillDefaultOps(ops *types.TransactOpts) (*types.TransactOpts, error) {
|
||||
if ops == nil {
|
||||
ops = new(types.TransactOpts)
|
||||
}
|
||||
if ops.ExpiredAt == 0 {
|
||||
ops.ExpiredAt = time.Now().Add(DefaultExpireTime).UnixMilli()
|
||||
}
|
||||
if ops.FromAccountIndex == nil {
|
||||
ops.FromAccountIndex = &c.accountIndex
|
||||
}
|
||||
if ops.ApiKeyIndex == nil {
|
||||
ops.ApiKeyIndex = &c.apiKeyIndex
|
||||
}
|
||||
if ops.Nonce == nil || *ops.Nonce == -1 {
|
||||
if c.apiClient == nil {
|
||||
return nil, fmt.Errorf("nonce was not provided & HTTPClient is nil. Either provide the nonce or enable HTTPClient to get the nonce from Lighter")
|
||||
}
|
||||
nonce, err := c.apiClient.GetNextNonce(*ops.FromAccountIndex, *ops.ApiKeyIndex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
return ops, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetChainId() uint32 {
|
||||
return c.chainId
|
||||
}
|
||||
|
||||
func (c *TxClient) GetKeyManager() signer.KeyManager {
|
||||
return c.keyManager
|
||||
}
|
||||
|
||||
func (c *TxClient) GetAccountIndex() int64 {
|
||||
return c.accountIndex
|
||||
}
|
||||
|
||||
func (c *TxClient) GetApiKeyIndex() uint8 {
|
||||
return c.apiKeyIndex
|
||||
}
|
||||
|
||||
func (c *TxClient) HTTP() MinimalHTTPClient {
|
||||
return c.apiClient
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
schnorr "github.com/elliottech/poseidon_crypto/signature/schnorr"
|
||||
|
||||
"github.com/elliottech/lighter-go/types"
|
||||
"github.com/elliottech/lighter-go/types/txtypes"
|
||||
)
|
||||
|
||||
func (c *TxClient) GetAuthToken(deadline time.Time) (string, error) {
|
||||
return types.ConstructAuthToken(c.keyManager, deadline, &types.TransactOpts{
|
||||
ApiKeyIndex: &c.apiKeyIndex,
|
||||
FromAccountIndex: &c.accountIndex,
|
||||
})
|
||||
}
|
||||
|
||||
func (c *TxClient) GetChangePubKeyTransaction(tx *types.ChangePubKeyReq, ops *types.TransactOpts) (*txtypes.L2ChangePubKeyTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructChangePubKeyTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk := c.keyManager.PubKeyBytes()
|
||||
msgHash, _ := txInfo.Hash(c.chainId)
|
||||
|
||||
if err := schnorr.Validate(pk[:], msgHash, txInfo.Sig); err != nil {
|
||||
return nil, fmt.Errorf("failed to validate signature. error: %v", err)
|
||||
}
|
||||
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCreateSubAccountTransaction(ops *types.TransactOpts) (*txtypes.L2CreateSubAccountTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructCreateSubAccountTx(c.keyManager, c.chainId, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCreatePublicPoolTransaction(tx *types.CreatePublicPoolTxReq, ops *types.TransactOpts) (*txtypes.L2CreatePublicPoolTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructCreatePublicPoolTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetUpdatePublicPoolTransaction(tx *types.UpdatePublicPoolTxReq, ops *types.TransactOpts) (*txtypes.L2UpdatePublicPoolTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructUpdatePublicPoolTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetTransferTransaction(tx *types.TransferTxReq, ops *types.TransactOpts) (*txtypes.L2TransferTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructTransferTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetWithdrawTransaction(tx *types.WithdrawTxReq, ops *types.TransactOpts) (*txtypes.L2WithdrawTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructWithdrawTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCreateOrderTransaction(tx *types.CreateOrderTxReq, ops *types.TransactOpts) (*txtypes.L2CreateOrderTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructCreateOrderTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCreateGroupedOrdersTransaction(tx *types.CreateGroupedOrdersTxReq, ops *types.TransactOpts) (*txtypes.L2CreateGroupedOrdersTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructL2CreateGroupedOrdersTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCancelOrderTransaction(tx *types.CancelOrderTxReq, ops *types.TransactOpts) (*txtypes.L2CancelOrderTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructL2CancelOrderTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetModifyOrderTransaction(tx *types.ModifyOrderTxReq, ops *types.TransactOpts) (*txtypes.L2ModifyOrderTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txInfo, err := types.ConstructL2ModifyOrderTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetCancelAllOrdersTransaction(tx *types.CancelAllOrdersTxReq, ops *types.TransactOpts) (*txtypes.L2CancelAllOrdersTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructL2CancelAllOrdersTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetMintSharesTransaction(tx *types.MintSharesTxReq, ops *types.TransactOpts) (*txtypes.L2MintSharesTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructMintSharesTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetBurnSharesTransaction(tx *types.BurnSharesTxReq, ops *types.TransactOpts) (*txtypes.L2BurnSharesTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructBurnSharesTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetUpdateLeverageTransaction(tx *types.UpdateLeverageTxReq, ops *types.TransactOpts) (*txtypes.L2UpdateLeverageTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructUpdateLeverageTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
|
||||
func (c *TxClient) GetUpdateMarginTransaction(tx *types.UpdateMarginTxReq, ops *types.TransactOpts) (*txtypes.L2UpdateMarginTxInfo, error) {
|
||||
ops, err := c.FullFillDefaultOps(ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
txInfo, err := types.ConstructUpdateMarginTx(c.keyManager, c.chainId, tx, ops)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return txInfo, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
module github.com/elliottech/lighter-go
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/elliottech/poseidon_crypto v0.0.11
|
||||
github.com/ethereum/go-ethereum v1.15.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bits-and-blooms/bitset v1.17.0 // indirect
|
||||
github.com/consensys/bavard v0.1.22 // indirect
|
||||
github.com/consensys/gnark-crypto v0.14.0 // indirect
|
||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
|
||||
github.com/ethereum/go-verkle v0.2.2 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/mmcloughlin/addchain v0.4.0 // indirect
|
||||
github.com/supranational/blst v0.3.14 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
rsc.io/tmplfunc v0.0.3 // indirect
|
||||
)
|
||||
@@ -0,0 +1,76 @@
|
||||
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
|
||||
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
|
||||
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
|
||||
github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
|
||||
github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
|
||||
github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
|
||||
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
|
||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
|
||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
|
||||
github.com/crate-crypto/go-kzg-4844 v1.1.0/go.mod h1:JolLjpSff1tCCJKaJx4psrlEdlXuJEC996PL3tTAFks=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/elliottech/poseidon_crypto v0.0.11 h1:iX4rCg0m1XIX/7mhXVUEYUJIdQD57zNGNLeb6RZRl7g=
|
||||
github.com/elliottech/poseidon_crypto v0.0.11/go.mod h1:NhWxSjPGr5JXRuB2Aepl/+ZrbmUG3hvku/GarB1JR8c=
|
||||
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
|
||||
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||
github.com/ethereum/go-ethereum v1.15.6 h1:jgLoUM6/pNjp0uEnXyWcWikDwa4j1wZlcqkX8Pm8A+I=
|
||||
github.com/ethereum/go-ethereum v1.15.6/go.mod h1:+S9k+jFzlyVTNcYGvqFhzN/SFhI6vA+aOY4T5tLSPL0=
|
||||
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
|
||||
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
|
||||
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk=
|
||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
|
||||
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
|
||||
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
|
||||
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo=
|
||||
github.com/supranational/blst v0.3.14/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
|
||||
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
|
||||
@@ -0,0 +1,49 @@
|
||||
### Local builds
|
||||
|
||||
build-darwin-local:
|
||||
go mod vendor
|
||||
go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-darwin-arm64.dylib ./sharedlib/main.go
|
||||
|
||||
# Note: build-linux-local does not append -arm or amd64 at end
|
||||
build-linux-local:
|
||||
go mod vendor
|
||||
CGO_ENABLED=1 go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-linux.so ./sharedlib/main.go
|
||||
|
||||
# Note: build-windows-local does not append -arm or amd64 at end
|
||||
# Windows build (requires gcc from msys2: choco install msys2)
|
||||
# CMD: set PATH=C:\msys64\mingw64\bin;%PATH% && set CGO_ENABLED=1 && go mod vendor && go build -buildmode=c-shared -trimpath -o ./build/signer-amd64.dll ./sharedlib/main.go
|
||||
# PowerShell: $env:Path='C:\msys64\mingw64\bin;'+$env:Path; $env:CGO_ENABLED='1'; go mod vendor; go build -buildmode=c-shared -trimpath -o ./build/signer-amd64.dll ./sharedlib/main.go
|
||||
build-windows-local:
|
||||
go mod vendor
|
||||
$env:Path='C:\msys64\mingw64\bin;'+$env:Path; $env:CGO_ENABLED='1'; go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-windows.dll ./sharedlib/main.go
|
||||
|
||||
### Docker builds
|
||||
|
||||
# Note: I don't think this works TBH
|
||||
#build-darwin-arm64-docker:
|
||||
# docker run --rm -v ${PWD}:/go/src/sdk -w /go/src/sdk golang:1.23.2-bullseye bash -c " \
|
||||
# cd /go/src/sdk && \
|
||||
# go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-darwin-arm64.dylib ./sharedlib"
|
||||
|
||||
build-linux-amd64-docker:
|
||||
go mod vendor
|
||||
docker run --rm --platform linux/amd64 -v ${PWD}:/go/src/sdk -w /go/src/sdk golang:1.23.2-bullseye /bin/sh -c " \
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-linux-amd64.so ./sharedlib"
|
||||
|
||||
build-linux-arm64-docker:
|
||||
go mod vendor
|
||||
docker run --rm --platform linux/arm64 -v ${PWD}:/go/src/sdk -w /go/src/sdk golang:1.23.2-bullseye /bin/sh -c " \
|
||||
CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-linux-arm64.so ./sharedlib"
|
||||
|
||||
build-windows-amd64-docker:
|
||||
go mod vendor
|
||||
docker run --rm --platform linux/amd64 -v ${PWD}:/go/src/sdk -w /go/src/sdk golang:1.23.2-bullseye bash -c " \
|
||||
apt-get update && \
|
||||
apt-get install -y gcc-mingw-w64-x86-64 && \
|
||||
CGO_ENABLED=1 GOOS=windows GOARCH=amd64 CC=x86_64-w64-mingw32-gcc go build -buildmode=c-shared -trimpath -o ./build/lighter-signer-windows-amd64.dll ./sharedlib"
|
||||
|
||||
### WASM builds
|
||||
|
||||
build-wasm:
|
||||
go mod vendor
|
||||
GOOS=js GOARCH=wasm go build -trimpath -o ./build/lighter-signer.wasm ./wasm/
|
||||
@@ -0,0 +1,690 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/elliottech/lighter-go/client"
|
||||
"github.com/elliottech/lighter-go/client/http"
|
||||
"github.com/elliottech/lighter-go/types"
|
||||
"github.com/elliottech/lighter-go/types/txtypes"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
typedef struct {
|
||||
char* str;
|
||||
char* err;
|
||||
} StrOrErr;
|
||||
|
||||
typedef struct {
|
||||
uint8_t txType;
|
||||
char* txInfo;
|
||||
char* txHash;
|
||||
char* messageToSign;
|
||||
char* err;
|
||||
} SignedTxResponse;
|
||||
|
||||
typedef struct {
|
||||
char* privateKey;
|
||||
char* publicKey;
|
||||
char* err;
|
||||
} ApiKeyResponse;
|
||||
|
||||
typedef struct {
|
||||
uint8_t MarketIndex;
|
||||
int64_t ClientOrderIndex;
|
||||
int64_t BaseAmount;
|
||||
uint32_t Price;
|
||||
uint8_t IsAsk;
|
||||
uint8_t Type;
|
||||
uint8_t TimeInForce;
|
||||
uint8_t ReduceOnly;
|
||||
uint32_t TriggerPrice;
|
||||
int64_t OrderExpiry;
|
||||
} CreateOrderTxReq;
|
||||
*/
|
||||
import "C"
|
||||
|
||||
var chainId uint32
|
||||
|
||||
func wrapErr(err any) *C.char {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return C.CString(fmt.Sprintf("%v", err))
|
||||
}
|
||||
|
||||
func messageToSign(txInfo txtypes.TxInfo) string {
|
||||
switch typed := txInfo.(type) {
|
||||
case *txtypes.L2ChangePubKeyTxInfo:
|
||||
return typed.GetL1SignatureBody()
|
||||
case *txtypes.L2TransferTxInfo:
|
||||
return typed.GetL1SignatureBody(chainId)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func signedTxResponseErr(err any) C.SignedTxResponse {
|
||||
return C.SignedTxResponse{err: wrapErr(err)}
|
||||
}
|
||||
|
||||
func signedTxResponsePanic(err any) C.SignedTxResponse {
|
||||
return signedTxResponseErr(fmt.Errorf("panic: %v", err))
|
||||
}
|
||||
|
||||
func convertTxInfoToResponse(txInfo txtypes.TxInfo, err error) C.SignedTxResponse {
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
if txInfo == nil {
|
||||
return signedTxResponseErr("nil transaction info")
|
||||
}
|
||||
|
||||
txInfoStr, err := txInfo.GetTxInfo()
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
resp := C.SignedTxResponse{
|
||||
txType: C.uint8_t(txInfo.GetTxType()),
|
||||
txInfo: C.CString(txInfoStr),
|
||||
txHash: C.CString(txInfo.GetTxHash()),
|
||||
}
|
||||
|
||||
if msg := messageToSign(txInfo); msg != "" {
|
||||
resp.messageToSign = C.CString(msg)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// getClient returns the go TxClient from the specified cApiKeyIndex and cAccountIndex
|
||||
func getClient(cApiKeyIndex C.int, cAccountIndex C.longlong) (*client.TxClient, error) {
|
||||
apiKeyIndex := uint8(cApiKeyIndex)
|
||||
accountIndex := int64(cAccountIndex)
|
||||
return client.GetClient(apiKeyIndex, accountIndex)
|
||||
}
|
||||
|
||||
func getTransactOpts(cNonce C.longlong) *types.TransactOpts {
|
||||
nonce := int64(cNonce)
|
||||
return &types.TransactOpts{
|
||||
Nonce: &nonce,
|
||||
}
|
||||
}
|
||||
|
||||
//export GenerateAPIKey
|
||||
func GenerateAPIKey(cSeed *C.char) (ret C.ApiKeyResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = C.ApiKeyResponse{err: wrapErr(fmt.Errorf("panic: %v", r))}
|
||||
}
|
||||
}()
|
||||
|
||||
seed := C.GoString(cSeed)
|
||||
privateKeyStr, publicKeyStr, err := client.GenerateAPIKey(seed)
|
||||
if err != nil {
|
||||
return C.ApiKeyResponse{err: wrapErr(err)}
|
||||
}
|
||||
|
||||
return C.ApiKeyResponse{
|
||||
privateKey: C.CString(privateKeyStr),
|
||||
publicKey: C.CString(publicKeyStr),
|
||||
}
|
||||
}
|
||||
|
||||
//export CreateClient
|
||||
func CreateClient(cUrl *C.char, cPrivateKey *C.char, cChainId C.int, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret *C.char) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = wrapErr(fmt.Errorf("panic: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
url := C.GoString(cUrl)
|
||||
privateKey := C.GoString(cPrivateKey)
|
||||
chainId = uint32(cChainId)
|
||||
apiKeyIndex := uint8(cApiKeyIndex)
|
||||
accountIndex := int64(cAccountIndex)
|
||||
|
||||
httpClient := http.NewClient(url)
|
||||
|
||||
_, err := client.CreateClient(httpClient, privateKey, chainId, apiKeyIndex, accountIndex)
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
//export CheckClient
|
||||
func CheckClient(cApiKeyIndex C.int, cAccountIndex C.longlong) (ret *C.char) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = wrapErr(fmt.Errorf("panic: %v", r))
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
return wrapErr(c.Check())
|
||||
}
|
||||
|
||||
//export SignChangePubKey
|
||||
func SignChangePubKey(cPubKey *C.char, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
pubKeyStr := C.GoString(cPubKey)
|
||||
pubKeyBytes, err := hexutil.Decode(pubKeyStr)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
if len(pubKeyBytes) != 40 {
|
||||
return signedTxResponseErr(fmt.Errorf("invalid pub key length. expected 40 but got %v", len(pubKeyBytes)))
|
||||
}
|
||||
var pubKey [40]byte
|
||||
copy(pubKey[:], pubKeyBytes)
|
||||
|
||||
tx := &types.ChangePubKeyReq{
|
||||
PubKey: pubKey,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetChangePubKeyTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCreateOrder
|
||||
func SignCreateOrder(cMarketIndex C.int, cClientOrderIndex C.longlong, cBaseAmount C.longlong, cPrice C.int, cIsAsk C.int, cOrderType C.int, cTimeInForce C.int, cReduceOnly C.int, cTriggerPrice C.int, cOrderExpiry C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
marketIndex := int16(cMarketIndex)
|
||||
clientOrderIndex := int64(cClientOrderIndex)
|
||||
baseAmount := int64(cBaseAmount)
|
||||
price := uint32(cPrice)
|
||||
isAsk := uint8(cIsAsk)
|
||||
orderType := uint8(cOrderType)
|
||||
timeInForce := uint8(cTimeInForce)
|
||||
reduceOnly := uint8(cReduceOnly)
|
||||
triggerPrice := uint32(cTriggerPrice)
|
||||
orderExpiry := int64(cOrderExpiry)
|
||||
|
||||
if orderExpiry == -1 {
|
||||
orderExpiry = time.Now().Add(time.Hour * 24 * 28).UnixMilli() // 28 days
|
||||
}
|
||||
|
||||
tx := &types.CreateOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
ClientOrderIndex: clientOrderIndex,
|
||||
BaseAmount: baseAmount,
|
||||
Price: price,
|
||||
IsAsk: isAsk,
|
||||
Type: orderType,
|
||||
TimeInForce: timeInForce,
|
||||
ReduceOnly: reduceOnly,
|
||||
TriggerPrice: triggerPrice,
|
||||
OrderExpiry: orderExpiry,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCreateOrderTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCreateGroupedOrders
|
||||
func SignCreateGroupedOrders(cGroupingType C.uint8_t, cOrders *C.CreateOrderTxReq, cLen C.int, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
length := int(cLen)
|
||||
orders := make([]*types.CreateOrderTxReq, length)
|
||||
size := unsafe.Sizeof(*cOrders)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
order := (*C.CreateOrderTxReq)(unsafe.Pointer(uintptr(unsafe.Pointer(cOrders)) + uintptr(i)*uintptr(size)))
|
||||
|
||||
orderExpiry := int64(order.OrderExpiry)
|
||||
if orderExpiry == -1 {
|
||||
orderExpiry = time.Now().Add(time.Hour * 24 * 28).UnixMilli()
|
||||
}
|
||||
|
||||
orders[i] = &types.CreateOrderTxReq{
|
||||
MarketIndex: int16(order.MarketIndex),
|
||||
ClientOrderIndex: int64(order.ClientOrderIndex),
|
||||
BaseAmount: int64(order.BaseAmount),
|
||||
Price: uint32(order.Price),
|
||||
IsAsk: uint8(order.IsAsk),
|
||||
Type: uint8(order.Type),
|
||||
TimeInForce: uint8(order.TimeInForce),
|
||||
ReduceOnly: uint8(order.ReduceOnly),
|
||||
TriggerPrice: uint32(order.TriggerPrice),
|
||||
OrderExpiry: orderExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
tx := &types.CreateGroupedOrdersTxReq{
|
||||
GroupingType: uint8(cGroupingType),
|
||||
Orders: orders,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCreateGroupedOrdersTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCancelOrder
|
||||
func SignCancelOrder(cMarketIndex C.int, cOrderIndex C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
marketIndex := int16(cMarketIndex)
|
||||
orderIndex := int64(cOrderIndex)
|
||||
|
||||
tx := &types.CancelOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
Index: orderIndex,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCancelOrderTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignWithdraw
|
||||
func SignWithdraw(cAssetIndex C.int, cRouteType C.int, cAmount C.ulonglong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
assetIndex := int16(cAssetIndex)
|
||||
routeType := uint8(cRouteType)
|
||||
amount := uint64(cAmount)
|
||||
|
||||
tx := &types.WithdrawTxReq{
|
||||
AssetIndex: assetIndex,
|
||||
RouteType: routeType,
|
||||
Amount: amount,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetWithdrawTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCreateSubAccount
|
||||
func SignCreateSubAccount(cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCreateSubAccountTransaction(ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCancelAllOrders
|
||||
func SignCancelAllOrders(cTimeInForce C.int, cTime C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
timeInForce := uint8(cTimeInForce)
|
||||
t := int64(cTime)
|
||||
|
||||
tx := &types.CancelAllOrdersTxReq{
|
||||
TimeInForce: timeInForce,
|
||||
Time: t,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCancelAllOrdersTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignModifyOrder
|
||||
func SignModifyOrder(cMarketIndex C.int, cIndex C.longlong, cBaseAmount C.longlong, cPrice C.longlong, cTriggerPrice C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
marketIndex := int16(cMarketIndex)
|
||||
index := int64(cIndex)
|
||||
baseAmount := int64(cBaseAmount)
|
||||
price := uint32(cPrice)
|
||||
triggerPrice := uint32(cTriggerPrice)
|
||||
|
||||
tx := &types.ModifyOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
Index: index,
|
||||
BaseAmount: baseAmount,
|
||||
Price: price,
|
||||
TriggerPrice: triggerPrice,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetModifyOrderTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignTransfer
|
||||
func SignTransfer(cToAccountIndex C.longlong, cAssetIndex C.int16_t, cFromRouteType, cToRouteType C.uint8_t, cAmount, cUsdcFee C.longlong, cMemo *C.char, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
toAccountIndex := int64(cToAccountIndex)
|
||||
assetIndex := int16(cAssetIndex)
|
||||
fromRouteType := uint8(cFromRouteType)
|
||||
toRouteType := uint8(cToRouteType)
|
||||
amount := int64(cAmount)
|
||||
usdcFee := int64(cUsdcFee)
|
||||
memo := [32]byte{}
|
||||
memoStr := C.GoString(cMemo)
|
||||
if len(memoStr) == 66 {
|
||||
if memoStr[0:2] == "0x" {
|
||||
memoStr = memoStr[2:66]
|
||||
} else {
|
||||
return signedTxResponseErr(fmt.Sprintf("memo expected to be 32 bytes or 64 hex encoded or 66 if 0x hex encoded -- long but received %v", len(memoStr)))
|
||||
}
|
||||
}
|
||||
|
||||
// assume hex encoded here
|
||||
if len(memoStr) == 64 {
|
||||
b, err := hex.DecodeString(memoStr)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(fmt.Sprintf("failed to decode hex string. err: %v", err))
|
||||
}
|
||||
|
||||
for i := 0; i < 32; i += 1 {
|
||||
memo[i] = b[i]
|
||||
}
|
||||
} else if len(memoStr) == 32 {
|
||||
for i := 0; i < 32; i++ {
|
||||
memo[i] = byte(memoStr[i])
|
||||
}
|
||||
} else {
|
||||
return signedTxResponseErr(fmt.Sprintf("memo expected to be 32 bytes or 64 hex encoded or 66 if 0x hex encoded -- long but received %v", len(memoStr)))
|
||||
}
|
||||
|
||||
tx := &types.TransferTxReq{
|
||||
ToAccountIndex: toAccountIndex,
|
||||
AssetIndex: assetIndex,
|
||||
FromRouteType: fromRouteType,
|
||||
ToRouteType: toRouteType,
|
||||
Amount: amount,
|
||||
USDCFee: usdcFee,
|
||||
Memo: memo,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetTransferTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignCreatePublicPool
|
||||
func SignCreatePublicPool(cOperatorFee C.longlong, cInitialTotalShares C.int, cMinOperatorShareRate C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
operatorFee := int64(cOperatorFee)
|
||||
initialTotalShares := int64(cInitialTotalShares)
|
||||
minOperatorShareRate := uint16(cMinOperatorShareRate)
|
||||
|
||||
tx := &types.CreatePublicPoolTxReq{
|
||||
OperatorFee: operatorFee,
|
||||
InitialTotalShares: initialTotalShares,
|
||||
MinOperatorShareRate: minOperatorShareRate,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetCreatePublicPoolTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignUpdatePublicPool
|
||||
func SignUpdatePublicPool(cPublicPoolIndex C.longlong, cStatus C.int, cOperatorFee C.longlong, cMinOperatorShareRate C.int, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := int64(cPublicPoolIndex)
|
||||
status := uint8(cStatus)
|
||||
operatorFee := int64(cOperatorFee)
|
||||
minOperatorShareRate := uint16(cMinOperatorShareRate)
|
||||
|
||||
tx := &types.UpdatePublicPoolTxReq{
|
||||
PublicPoolIndex: publicPoolIndex,
|
||||
Status: status,
|
||||
OperatorFee: operatorFee,
|
||||
MinOperatorShareRate: minOperatorShareRate,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetUpdatePublicPoolTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignMintShares
|
||||
func SignMintShares(cPublicPoolIndex C.longlong, cShareAmount C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := int64(cPublicPoolIndex)
|
||||
shareAmount := int64(cShareAmount)
|
||||
|
||||
tx := &types.MintSharesTxReq{
|
||||
PublicPoolIndex: publicPoolIndex,
|
||||
ShareAmount: shareAmount,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetMintSharesTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignBurnShares
|
||||
func SignBurnShares(cPublicPoolIndex C.longlong, cShareAmount C.longlong, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := int64(cPublicPoolIndex)
|
||||
shareAmount := int64(cShareAmount)
|
||||
|
||||
tx := &types.BurnSharesTxReq{
|
||||
PublicPoolIndex: publicPoolIndex,
|
||||
ShareAmount: shareAmount,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetBurnSharesTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export SignUpdateLeverage
|
||||
func SignUpdateLeverage(cMarketIndex C.int, cInitialMarginFraction C.int, cMarginMode C.int, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
marketIndex := int16(cMarketIndex)
|
||||
initialMarginFraction := uint16(cInitialMarginFraction)
|
||||
marginMode := uint8(cMarginMode)
|
||||
|
||||
tx := &types.UpdateLeverageTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
InitialMarginFraction: initialMarginFraction,
|
||||
MarginMode: marginMode,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetUpdateLeverageTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
//export CreateAuthToken
|
||||
func CreateAuthToken(cDeadline C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.StrOrErr) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = C.StrOrErr{err: wrapErr(fmt.Errorf("panic: %v", r))}
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return C.StrOrErr{err: wrapErr(err)}
|
||||
}
|
||||
|
||||
deadline := int64(cDeadline)
|
||||
if deadline == 0 {
|
||||
deadline = time.Now().Add(time.Hour * 7).Unix()
|
||||
}
|
||||
|
||||
authToken, err := c.GetAuthToken(time.Unix(deadline, 0))
|
||||
if err != nil {
|
||||
return C.StrOrErr{err: wrapErr(err)}
|
||||
}
|
||||
|
||||
return C.StrOrErr{str: C.CString(authToken)}
|
||||
}
|
||||
|
||||
//export SignUpdateMargin
|
||||
func SignUpdateMargin(cMarketIndex C.int, cUSDCAmount C.longlong, cDirection C.int, cNonce C.longlong, cApiKeyIndex C.int, cAccountIndex C.longlong) (ret C.SignedTxResponse) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
ret = signedTxResponsePanic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
c, err := getClient(cApiKeyIndex, cAccountIndex)
|
||||
if err != nil {
|
||||
return signedTxResponseErr(err)
|
||||
}
|
||||
|
||||
marketIndex := int16(cMarketIndex)
|
||||
usdcAmount := int64(cUSDCAmount)
|
||||
direction := uint8(cDirection)
|
||||
|
||||
tx := &types.UpdateMarginTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
USDCAmount: usdcAmount,
|
||||
Direction: direction,
|
||||
}
|
||||
ops := getTransactOpts(cNonce)
|
||||
|
||||
txInfo, err := c.GetUpdateMarginTransaction(tx, ops)
|
||||
return convertTxInfoToResponse(txInfo, err)
|
||||
}
|
||||
|
||||
func main() {}
|
||||
@@ -0,0 +1,54 @@
|
||||
package signer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
curve "github.com/elliottech/poseidon_crypto/curve/ecgfp5"
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
schnorr "github.com/elliottech/poseidon_crypto/signature/schnorr"
|
||||
)
|
||||
|
||||
type Signer interface {
|
||||
Sign(message []byte, hFunc hash.Hash) ([]byte, error)
|
||||
}
|
||||
|
||||
type KeyManager interface {
|
||||
Signer
|
||||
PubKey() gFp5.Element
|
||||
PubKeyBytes() [40]byte
|
||||
PrvKeyBytes() []byte
|
||||
}
|
||||
|
||||
type keyManager struct {
|
||||
key curve.ECgFp5Scalar
|
||||
}
|
||||
|
||||
func NewKeyManager(b []byte) (KeyManager, error) {
|
||||
if len(b) != 40 {
|
||||
return nil, fmt.Errorf("invalid private key length. expected: 40 got: %v", len(b))
|
||||
}
|
||||
return &keyManager{key: curve.ScalarElementFromLittleEndianBytes(b)}, nil
|
||||
}
|
||||
|
||||
func (key *keyManager) Sign(hashedMessage []byte, hFunc hash.Hash) ([]byte, error) {
|
||||
hashedMessageAsQuinticExtension, err := gFp5.FromCanonicalLittleEndianBytes(hashedMessage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse message while signing. message: %v err: %w", hashedMessage, err)
|
||||
}
|
||||
return schnorr.SchnorrSignHashedMessage(hashedMessageAsQuinticExtension, key.key).ToBytes(), nil
|
||||
}
|
||||
|
||||
func (key *keyManager) PubKey() gFp5.Element {
|
||||
return schnorr.SchnorrPkFromSk(key.key)
|
||||
}
|
||||
|
||||
func (key *keyManager) PubKeyBytes() (res [40]byte) {
|
||||
bytes := key.PubKey().ToLittleEndianBytes()
|
||||
copy(res[:], bytes[:])
|
||||
return
|
||||
}
|
||||
|
||||
func (key *keyManager) PrvKeyBytes() []byte {
|
||||
return key.key.ToLittleEndianBytes()
|
||||
}
|
||||
@@ -28,17 +28,22 @@ type ChangePubKeyReq struct {
|
||||
|
||||
type TransferTxReq struct {
|
||||
ToAccountIndex int64
|
||||
USDCAmount int64
|
||||
Fee int64
|
||||
AssetIndex int16
|
||||
FromRouteType uint8
|
||||
ToRouteType uint8
|
||||
Amount int64
|
||||
USDCFee int64
|
||||
Memo [32]byte
|
||||
}
|
||||
|
||||
type WithdrawTxReq struct {
|
||||
USDCAmount uint64
|
||||
AssetIndex int16
|
||||
RouteType uint8
|
||||
Amount uint64
|
||||
}
|
||||
|
||||
type CreateOrderTxReq struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
ClientOrderIndex int64
|
||||
BaseAmount int64
|
||||
Price uint32
|
||||
@@ -56,7 +61,7 @@ type CreateGroupedOrdersTxReq struct {
|
||||
}
|
||||
|
||||
type ModifyOrderTxReq struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
Index int64
|
||||
BaseAmount int64
|
||||
Price uint32
|
||||
@@ -64,7 +69,7 @@ type ModifyOrderTxReq struct {
|
||||
}
|
||||
|
||||
type CancelOrderTxReq struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
Index int64
|
||||
}
|
||||
|
||||
@@ -76,14 +81,14 @@ type CancelAllOrdersTxReq struct {
|
||||
type CreatePublicPoolTxReq struct {
|
||||
OperatorFee int64
|
||||
InitialTotalShares int64
|
||||
MinOperatorShareRate int64
|
||||
MinOperatorShareRate uint16
|
||||
}
|
||||
|
||||
type UpdatePublicPoolTxReq struct {
|
||||
PublicPoolIndex int64
|
||||
Status uint8
|
||||
OperatorFee int64
|
||||
MinOperatorShareRate int64
|
||||
MinOperatorShareRate uint16
|
||||
}
|
||||
|
||||
type MintSharesTxReq struct {
|
||||
@@ -97,13 +102,13 @@ type BurnSharesTxReq struct {
|
||||
}
|
||||
|
||||
type UpdateLeverageTxReq struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
InitialMarginFraction uint16
|
||||
MarginMode uint8
|
||||
}
|
||||
|
||||
type UpdateMarginTxReq struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
USDCAmount int64
|
||||
Direction uint8
|
||||
}
|
||||
@@ -468,8 +473,11 @@ func ConvertTransferTx(tx *TransferTxReq, ops *TransactOpts) *txtypes.L2Transfer
|
||||
FromAccountIndex: *ops.FromAccountIndex,
|
||||
ApiKeyIndex: *ops.ApiKeyIndex,
|
||||
ToAccountIndex: tx.ToAccountIndex,
|
||||
USDCAmount: tx.USDCAmount,
|
||||
Fee: tx.Fee,
|
||||
AssetIndex: tx.AssetIndex,
|
||||
FromRouteType: tx.FromRouteType,
|
||||
ToRouteType: tx.ToRouteType,
|
||||
Amount: tx.Amount,
|
||||
USDCFee: tx.USDCFee,
|
||||
Memo: tx.Memo,
|
||||
ExpiredAt: ops.ExpiredAt,
|
||||
Nonce: *ops.Nonce,
|
||||
@@ -563,7 +571,9 @@ func ConvertWithdrawTx(tx *WithdrawTxReq, ops *TransactOpts) *txtypes.L2Withdraw
|
||||
return &txtypes.L2WithdrawTxInfo{
|
||||
FromAccountIndex: *ops.FromAccountIndex,
|
||||
ApiKeyIndex: *ops.ApiKeyIndex,
|
||||
USDCAmount: tx.USDCAmount,
|
||||
AssetIndex: tx.AssetIndex,
|
||||
RouteType: tx.RouteType,
|
||||
Amount: tx.Amount,
|
||||
ExpiredAt: ops.ExpiredAt,
|
||||
Nonce: *ops.Nonce,
|
||||
}
|
||||
@@ -641,6 +651,7 @@ func ConvertUpdateLeverageTx(tx *UpdateLeverageTxReq, ops *TransactOpts) *txtype
|
||||
ApiKeyIndex: *ops.ApiKeyIndex,
|
||||
MarketIndex: tx.MarketIndex,
|
||||
InitialMarginFraction: tx.InitialMarginFraction,
|
||||
MarginMode: tx.MarginMode,
|
||||
ExpiredAt: ops.ExpiredAt,
|
||||
Nonce: *ops.Nonce,
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ func (txInfo *L2BurnSharesTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// PublicPoolIndex
|
||||
if txInfo.PublicPoolIndex < MinAccountIndex {
|
||||
if txInfo.PublicPoolIndex < MinSubAccountIndex {
|
||||
return ErrPublicPoolIndexTooLow
|
||||
}
|
||||
if txInfo.PublicPoolIndex > MaxAccountIndex {
|
||||
@@ -73,7 +73,6 @@ func (txInfo *L2BurnSharesTxInfo) Validate() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (txInfo *L2BurnSharesTxInfo) Hash(lighterChainId uint32, extra ...g.Element) (msgHash []byte, err error) {
|
||||
elems := make([]g.Element, 0, 8)
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ func (txInfo *L2CancelAllOrdersTxInfo) Validate() error {
|
||||
}
|
||||
case AbortScheduledCancelAll:
|
||||
if txInfo.Time != 0 {
|
||||
return ErrCancelAllTimeIsNotInRange
|
||||
return ErrCancelAllTimeisNotNill
|
||||
}
|
||||
default:
|
||||
return ErrInvalidCancelAllTimeInForce
|
||||
|
||||
@@ -11,7 +11,7 @@ type L2CancelOrderTxInfo struct {
|
||||
AccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
Index int64 // Client Order Index or Order Index of the order to cancel
|
||||
|
||||
ExpiredAt int64
|
||||
@@ -50,11 +50,10 @@ func (txInfo *L2CancelOrderTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MarketIndex
|
||||
if txInfo.MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
}
|
||||
if txInfo.MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
isSpotMarket := txInfo.MarketIndex >= MinSpotMarketIndex && txInfo.MarketIndex <= MaxSpotMarketIndex
|
||||
isPerpsMarket := txInfo.MarketIndex >= MinPerpsMarketIndex && txInfo.MarketIndex <= MaxPerpsMarketIndex
|
||||
if !isSpotMarket && !isPerpsMarket {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
|
||||
// Index
|
||||
|
||||
@@ -2,35 +2,12 @@ package txtypes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
p2 "github.com/elliottech/poseidon_crypto/hash/poseidon2_goldilocks"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
const (
|
||||
templateChangePubKey = "Register Lighter Account\n\npubkey: 0x%s\nnonce: %s\naccount index: %s\napi key index: %s\nOnly sign this message for a trusted client!"
|
||||
)
|
||||
|
||||
func getHex10FromUint64(value uint64) string {
|
||||
v := hexutil.EncodeUint64(value)
|
||||
v = strings.Replace(v, "0x", "", 1)
|
||||
|
||||
// Make sure result has fixed bytes
|
||||
vBytes := []byte(v)
|
||||
if len(vBytes) < 16 {
|
||||
toAppend := make([]byte, 16-len(vBytes))
|
||||
for i := range toAppend {
|
||||
toAppend[i] = 48
|
||||
}
|
||||
vBytes = append(toAppend, vBytes...)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%s", string(vBytes))
|
||||
}
|
||||
|
||||
var _ TxInfo = (*L2ChangePubKeyTxInfo)(nil)
|
||||
|
||||
type L2ChangePubKeyTxInfo struct {
|
||||
@@ -84,7 +61,7 @@ func (txInfo *L2ChangePubKeyTxInfo) Validate() error {
|
||||
return ErrExpiredAtInvalid
|
||||
}
|
||||
|
||||
if !IsValidPubKey(txInfo.PubKey) {
|
||||
if !IsValidPubKeyLength(txInfo.PubKey) {
|
||||
return ErrPubKeyInvalid
|
||||
}
|
||||
|
||||
@@ -92,7 +69,8 @@ func (txInfo *L2ChangePubKeyTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
func (txInfo *L2ChangePubKeyTxInfo) GetL1SignatureBody() string {
|
||||
signatureBody := fmt.Sprintf(templateChangePubKey,
|
||||
signatureBody := fmt.Sprintf(
|
||||
TemplateChangePubKey,
|
||||
common.Bytes2Hex(txInfo.PubKey),
|
||||
getHex10FromUint64(uint64(txInfo.Nonce)),
|
||||
getHex10FromUint64(uint64(txInfo.AccountIndex)),
|
||||
@@ -101,6 +79,10 @@ func (txInfo *L2ChangePubKeyTxInfo) GetL1SignatureBody() string {
|
||||
return signatureBody
|
||||
}
|
||||
|
||||
func (txInfo *L2ChangePubKeyTxInfo) GetL1AddressBySignature() common.Address {
|
||||
return calculateL1AddressBySignature(txInfo.GetL1SignatureBody(), txInfo.L1Sig)
|
||||
}
|
||||
|
||||
func (txInfo *L2ChangePubKeyTxInfo) Hash(lighterChainId uint32, extra ...g.Element) (msgHash []byte, err error) {
|
||||
elems := make([]g.Element, 0, 11)
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
TxTypeEmpty = 0
|
||||
TxTypeL1Deposit = 1
|
||||
TxTypeL1ChangePubKey = 2
|
||||
TxTypeL1CreateMarket = 3
|
||||
TxTypeL1UpdateMarket = 4
|
||||
TxTypeL1CancelAllOrders = 5
|
||||
TxTypeL1Withdraw = 6
|
||||
TxTypeL1CreateOrder = 7
|
||||
|
||||
TxTypeL2ChangePubKey = 8
|
||||
TxTypeL2CreateSubAccount = 9
|
||||
TxTypeL2CreatePublicPool = 10
|
||||
@@ -41,6 +50,7 @@ const (
|
||||
|
||||
TxTypeL2CreateGroupedOrders = 28
|
||||
TxTypeL2UpdateMargin = 29
|
||||
TxTypeL1BurnShares = 30
|
||||
)
|
||||
|
||||
// Order Type
|
||||
@@ -83,23 +93,57 @@ const (
|
||||
AbortScheduledCancelAll = 2
|
||||
)
|
||||
|
||||
// Asset Margin Mode
|
||||
const (
|
||||
HashLength int = 32
|
||||
AssetMarginMode_Disabled = 0
|
||||
AssetMarginMode_Enabled = 1
|
||||
AssetMarginMode_Max = AssetMarginMode_Enabled
|
||||
)
|
||||
|
||||
// Asset Route Type
|
||||
const (
|
||||
AssetRouteType_Perps = 0
|
||||
AssetRouteType_Spot = 1
|
||||
)
|
||||
|
||||
// Position Margin Mode
|
||||
const (
|
||||
CrossMargin = iota
|
||||
IsolatedMargin = 1
|
||||
)
|
||||
|
||||
// Margin Update Direction
|
||||
const (
|
||||
RemoveFromIsolatedMargin = iota
|
||||
AddToIsolatedMargin = 1
|
||||
)
|
||||
|
||||
const (
|
||||
OneUSDC = 1000000
|
||||
|
||||
FeeTick int64 = 1_000_000
|
||||
MarginFractionTick int64 = 10_000
|
||||
ShareTick int64 = 10_000
|
||||
FeeTick int64 = 1_000_000
|
||||
MarginFractionTick int64 = 10_000
|
||||
ShareTick uint16 = 10_000
|
||||
|
||||
MinAccountIndex int64 = 0
|
||||
MaxAccountIndex int64 = 281474976710654 // (1 << 48) - 2
|
||||
MinApiKeyIndex uint8 = 0
|
||||
MaxApiKeyIndex uint8 = 254 // (1 << 8) - 2
|
||||
MaxMasterAccountIndex int64 = 140737488355327 // (1 << 47) - 1
|
||||
MinSubAccountIndex int64 = 140737488355328 // (1 << 47)
|
||||
MinApiKeyIndex uint8 = 0
|
||||
MaxApiKeyIndex uint8 = 254 // (1 << 8) - 2
|
||||
|
||||
MinMarketIndex uint8 = 0
|
||||
MaxMarketIndex uint8 = 254 // (1 << 8) - 2
|
||||
MinMarketIndex int16 = 0
|
||||
MinPerpsMarketIndex int16 = 0
|
||||
MaxPerpsMarketIndex int16 = 254 // (1 << 8) - 2
|
||||
NilMarketIndex int16 = 255
|
||||
MinSpotMarketIndex int16 = 2048 // (1 << 11)
|
||||
MaxSpotMarketIndex int16 = 4094 // (1 << 12) - 2
|
||||
|
||||
NativeAssetIndex = uint16(1)
|
||||
USDCAssetIndex = uint16(3)
|
||||
MinAssetIndex = 1
|
||||
MaxAssetIndex = (1 << 6) - 2
|
||||
NilAssetIndex = 0
|
||||
|
||||
MaxInvestedPublicPoolCount int64 = 16
|
||||
InitialPoolShareValue int64 = 1_000 // 0.001 USDC
|
||||
@@ -124,7 +168,7 @@ const (
|
||||
MaxClientOrderIndex int64 = (1 << 48) - 1
|
||||
|
||||
MinOrderIndex int64 = MaxClientOrderIndex + 1
|
||||
MaxOrderIndex int64 = (1 << 56) - 1
|
||||
MaxOrderIndex int64 = (1 << 60) - 1
|
||||
|
||||
MinOrderBaseAmount int64 = 1
|
||||
MaxOrderBaseAmount int64 = (1 << 48) - 1
|
||||
@@ -162,14 +206,3 @@ const (
|
||||
MinWithdrawalAmount uint64 = 1
|
||||
MaxWithdrawalAmount uint64 = MaxExchangeUSDC
|
||||
)
|
||||
|
||||
// Margin Modes
|
||||
const (
|
||||
CrossMargin = iota
|
||||
IsolatedMargin = 1
|
||||
)
|
||||
|
||||
const (
|
||||
RemoveFromIsolatedMargin = 0
|
||||
AddToIsolatedMargin = 1
|
||||
)
|
||||
|
||||
@@ -55,11 +55,8 @@ func (txInfo *L2CreateGroupedOrdersTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MarketIndex for first order
|
||||
if txInfo.Orders[0].MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
}
|
||||
if txInfo.Orders[0].MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
if txInfo.Orders[0].MarketIndex < MinPerpsMarketIndex || txInfo.Orders[0].MarketIndex > MaxPerpsMarketIndex {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
|
||||
// Perform range checks for all orders
|
||||
|
||||
@@ -48,11 +48,10 @@ func (txInfo *L2CreateOrderTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MarketIndex
|
||||
if txInfo.MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
}
|
||||
if txInfo.MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
isSpotMarket := txInfo.MarketIndex >= MinSpotMarketIndex && txInfo.MarketIndex <= MaxSpotMarketIndex
|
||||
isPerpsMarket := txInfo.MarketIndex >= MinPerpsMarketIndex && txInfo.MarketIndex <= MaxPerpsMarketIndex
|
||||
if !isSpotMarket && !isPerpsMarket {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
|
||||
// ClientOrderIndex
|
||||
@@ -89,14 +88,17 @@ func (txInfo *L2CreateOrderTxInfo) Validate() error {
|
||||
return ErrIsAskInvalid
|
||||
}
|
||||
|
||||
// Type
|
||||
if txInfo.TimeInForce != ImmediateOrCancel && txInfo.TimeInForce != GoodTillTime && txInfo.TimeInForce != PostOnly {
|
||||
return ErrOrderTimeInForceInvalid
|
||||
}
|
||||
|
||||
if txInfo.ReduceOnly != 0 && txInfo.ReduceOnly != 1 {
|
||||
// ReduceOnly
|
||||
if (txInfo.ReduceOnly != 0 && txInfo.ReduceOnly != 1) || (isSpotMarket && txInfo.ReduceOnly == 1) {
|
||||
return ErrOrderReduceOnlyInvalid
|
||||
}
|
||||
|
||||
// OrderExpiry
|
||||
if (txInfo.OrderExpiry < MinOrderExpiry || txInfo.OrderExpiry > MaxOrderExpiry) && txInfo.OrderExpiry != NilOrderExpiry {
|
||||
return ErrOrderExpiryInvalid
|
||||
}
|
||||
@@ -119,7 +121,9 @@ func (txInfo *L2CreateOrderTxInfo) Validate() error {
|
||||
return ErrOrderExpiryInvalid
|
||||
}
|
||||
case StopLossOrder, TakeProfitOrder:
|
||||
if txInfo.TimeInForce != ImmediateOrCancel {
|
||||
if !isPerpsMarket {
|
||||
return ErrOrderTypeInvalid
|
||||
} else if txInfo.TimeInForce != ImmediateOrCancel {
|
||||
return ErrOrderTimeInForceInvalid
|
||||
} else if txInfo.TriggerPrice == NilOrderTriggerPrice {
|
||||
return ErrOrderTriggerPriceInvalid
|
||||
@@ -127,7 +131,9 @@ func (txInfo *L2CreateOrderTxInfo) Validate() error {
|
||||
return ErrOrderExpiryInvalid
|
||||
}
|
||||
case StopLossLimitOrder, TakeProfitLimitOrder:
|
||||
if txInfo.TriggerPrice == NilOrderTriggerPrice {
|
||||
if !isPerpsMarket {
|
||||
return ErrOrderTypeInvalid
|
||||
} else if txInfo.TriggerPrice == NilOrderTriggerPrice {
|
||||
return ErrOrderTriggerPriceInvalid
|
||||
} else if txInfo.OrderExpiry == NilOrderExpiry {
|
||||
return ErrOrderExpiryInvalid
|
||||
|
||||
@@ -13,7 +13,7 @@ type L2CreatePublicPoolTxInfo struct {
|
||||
|
||||
OperatorFee int64
|
||||
InitialTotalShares int64
|
||||
MinOperatorShareRate int64
|
||||
MinOperatorShareRate uint16
|
||||
|
||||
ExpiredAt int64
|
||||
Nonce int64
|
||||
@@ -64,9 +64,6 @@ func (txInfo *L2CreatePublicPoolTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MinOperatorShareRate
|
||||
if txInfo.MinOperatorShareRate < 0 {
|
||||
return ErrPoolMinOperatorShareRateTooLow
|
||||
}
|
||||
if txInfo.MinOperatorShareRate > ShareTick {
|
||||
return ErrPoolMinOperatorShareRateTooHigh
|
||||
}
|
||||
@@ -95,7 +92,7 @@ func (txInfo *L2CreatePublicPoolTxInfo) Hash(lighterChainId uint32, extra ...g.E
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.ApiKeyIndex)))
|
||||
elems = append(elems, g.FromInt64(txInfo.OperatorFee))
|
||||
elems = append(elems, g.FromInt64(txInfo.InitialTotalShares))
|
||||
elems = append(elems, g.FromInt64(txInfo.MinOperatorShareRate))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.MinOperatorShareRate)))
|
||||
|
||||
return p2.HashToQuinticExtension(elems).ToLittleEndianBytes(), nil
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (txInfo *L2CreateSubAccountTxInfo) Validate() error {
|
||||
if txInfo.AccountIndex < MinAccountIndex {
|
||||
return ErrFromAccountIndexTooLow
|
||||
}
|
||||
if txInfo.AccountIndex > MaxAccountIndex {
|
||||
if txInfo.AccountIndex > MaxMasterAccountIndex {
|
||||
return ErrFromAccountIndexTooHigh
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ package txtypes
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
ErrAssetIndexTooLow = fmt.Errorf("AssetIndex should not be less than %d", MinAssetIndex)
|
||||
ErrAssetIndexTooHigh = fmt.Errorf("AssetIndex should not be larger than %d", MaxAssetIndex)
|
||||
ErrRouteTypeInvalid = fmt.Errorf("RouteType is invalid")
|
||||
ErrAccountIndexTooLow = fmt.Errorf("AccountIndex should not be less than %d", MinAccountIndex)
|
||||
ErrAccountIndexTooHigh = fmt.Errorf("AccountIndex should not be larger than %d", MaxAccountIndex)
|
||||
ErrNonceTooLow = fmt.Errorf("AccountNonce should not be less than %d", MinNonce)
|
||||
@@ -36,9 +39,12 @@ var (
|
||||
ErrWithdrawalAmountTooHigh = fmt.Errorf("WithdrawalAmount should not be larger than %d", MaxWithdrawalAmount)
|
||||
ErrTransferAmountTooLow = fmt.Errorf("TransferAmount should be larger than %d", MinTransferAmount)
|
||||
ErrTransferAmountTooHigh = fmt.Errorf("TransferAmount should not be larger than %d", MaxTransferAmount)
|
||||
ErrTransferFeeNegative = fmt.Errorf("TransferFee should not be negative")
|
||||
ErrTransferFeeTooHigh = fmt.Errorf("TransferFee should not be larger than %d", MaxTransferAmount)
|
||||
ErrMarketIndexTooLow = fmt.Errorf("MarketIndex should not be less than %d", MinMarketIndex)
|
||||
ErrMarketIndexTooHigh = fmt.Errorf("MarketIndex should not be larger than %d", MaxMarketIndex)
|
||||
ErrMarketIndexTooHigh = fmt.Errorf("MarketIndex should not be larger than %d", MaxSpotMarketIndex)
|
||||
ErrMarketIndexMismatch = fmt.Errorf("MarketIndex should match the market index of the order")
|
||||
ErrInvalidMarketIndex = fmt.Errorf("MarketIndex is not valid")
|
||||
ErrInitialMarginFractionTooLow = fmt.Errorf("InitialMarginFraction should not be less than %d", 0)
|
||||
ErrInitialMarginFractionTooHigh = fmt.Errorf("InitialMarginFraction should not be larger than %d", MarginFractionTick)
|
||||
ErrClientOrderIndexTooLow = fmt.Errorf("ClientOrderIndex should not be less than %d", MinClientOrderIndex)
|
||||
@@ -61,6 +67,4 @@ var (
|
||||
ErrInvalidMarginMode = fmt.Errorf("MarginMode is not valid")
|
||||
ErrCancelModeInvalid = fmt.Errorf("CancelMode is not valid")
|
||||
ErrInvalidUpdateMarginDirection = fmt.Errorf("Margin movement direction is not valid")
|
||||
ErrTransferFeeNegative = fmt.Errorf("Transfer fee is negative")
|
||||
ErrTransferFeeTooHigh = fmt.Errorf("Transfer fee is higher than %d", MaxTransferAmount)
|
||||
)
|
||||
|
||||
@@ -21,7 +21,7 @@ type TxInfo interface {
|
||||
}
|
||||
|
||||
type OrderInfo struct {
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
|
||||
ClientOrderIndex int64
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ func (txInfo *L2MintSharesTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// PublicPoolIndex
|
||||
if txInfo.PublicPoolIndex < MinAccountIndex {
|
||||
if txInfo.PublicPoolIndex < MinSubAccountIndex {
|
||||
return ErrPublicPoolIndexTooLow
|
||||
}
|
||||
if txInfo.PublicPoolIndex > MaxAccountIndex {
|
||||
|
||||
@@ -11,7 +11,7 @@ type L2ModifyOrderTxInfo struct {
|
||||
AccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
Index int64 // Client Order Index or Order Index of the order to modify
|
||||
BaseAmount int64
|
||||
Price uint32
|
||||
@@ -52,11 +52,10 @@ func (txInfo *L2ModifyOrderTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MarketIndex
|
||||
if txInfo.MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
}
|
||||
if txInfo.MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
isSpotMarket := txInfo.MarketIndex >= MinSpotMarketIndex && txInfo.MarketIndex <= MaxSpotMarketIndex
|
||||
isPerpsMarket := txInfo.MarketIndex >= MinPerpsMarketIndex && txInfo.MarketIndex <= MaxPerpsMarketIndex
|
||||
if !isSpotMarket && !isPerpsMarket {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
|
||||
// Index
|
||||
|
||||
@@ -7,29 +7,31 @@ import (
|
||||
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
p2 "github.com/elliottech/poseidon_crypto/hash/poseidon2_goldilocks"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
const templateTransfer = "Transfer\n\nnonce: %s\nfrom: %s\napi key: %s\nto: %s\namount: %s\nfee: %s\nmemo: %s\nOnly sign this message for a trusted client!"
|
||||
|
||||
var _ TxInfo = (*L2TransferTxInfo)(nil)
|
||||
|
||||
type L2TransferTxInfo struct {
|
||||
FromAccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
ToAccountIndex int64
|
||||
AssetIndex int16
|
||||
FromRouteType uint8
|
||||
ToRouteType uint8
|
||||
Amount int64
|
||||
USDCFee int64
|
||||
|
||||
ToAccountIndex int64
|
||||
USDCAmount int64 // USDCAmount is given with 6 decimals
|
||||
Fee int64
|
||||
Memo [32]byte
|
||||
|
||||
Memo [32]byte
|
||||
ExpiredAt int64
|
||||
Nonce int64
|
||||
Sig []byte
|
||||
L1Sig string
|
||||
SignedHash string `json:"-"`
|
||||
}
|
||||
|
||||
func (txInfo *L2TransferTxInfo) Validate() error {
|
||||
// plus one for treasury account
|
||||
// FromAccountIndex
|
||||
if txInfo.FromAccountIndex < MinAccountIndex+1 {
|
||||
return ErrFromAccountIndexTooLow
|
||||
}
|
||||
@@ -41,29 +43,47 @@ func (txInfo *L2TransferTxInfo) Validate() error {
|
||||
if txInfo.ApiKeyIndex < MinApiKeyIndex {
|
||||
return ErrApiKeyIndexTooLow
|
||||
}
|
||||
|
||||
if txInfo.ApiKeyIndex > MaxApiKeyIndex {
|
||||
return ErrApiKeyIndexTooHigh
|
||||
}
|
||||
|
||||
if txInfo.ToAccountIndex < MinAccountIndex+1 {
|
||||
// ToAccountIndex
|
||||
if txInfo.ToAccountIndex < MinAccountIndex {
|
||||
return ErrToAccountIndexTooLow
|
||||
}
|
||||
if txInfo.ToAccountIndex > MaxAccountIndex {
|
||||
return ErrToAccountIndexTooHigh
|
||||
}
|
||||
|
||||
if txInfo.USDCAmount <= 0 {
|
||||
// AssetIndex
|
||||
if txInfo.AssetIndex < MinAssetIndex {
|
||||
return ErrAssetIndexTooLow
|
||||
}
|
||||
if txInfo.AssetIndex > MaxAssetIndex {
|
||||
return ErrAssetIndexTooHigh
|
||||
}
|
||||
|
||||
// FromRouteType
|
||||
if txInfo.FromRouteType != AssetRouteType_Perps && txInfo.FromRouteType != AssetRouteType_Spot {
|
||||
return ErrRouteTypeInvalid
|
||||
}
|
||||
|
||||
// ToRouteType
|
||||
if txInfo.ToRouteType != AssetRouteType_Perps && txInfo.ToRouteType != AssetRouteType_Spot {
|
||||
return ErrRouteTypeInvalid
|
||||
}
|
||||
|
||||
if txInfo.Amount <= 0 {
|
||||
return ErrTransferAmountTooLow
|
||||
}
|
||||
if txInfo.USDCAmount > MaxTransferAmount {
|
||||
if txInfo.Amount > MaxTransferAmount {
|
||||
return ErrTransferAmountTooHigh
|
||||
}
|
||||
|
||||
if txInfo.Fee < 0 {
|
||||
if txInfo.USDCFee < 0 {
|
||||
return ErrTransferFeeNegative
|
||||
}
|
||||
if txInfo.Fee > MaxTransferAmount {
|
||||
if txInfo.USDCFee > MaxTransferAmount {
|
||||
return ErrTransferFeeTooHigh
|
||||
}
|
||||
|
||||
@@ -90,8 +110,33 @@ func (txInfo *L2TransferTxInfo) GetTxInfo() (string, error) {
|
||||
return getTxInfo(txInfo)
|
||||
}
|
||||
|
||||
func (txInfo *L2TransferTxInfo) GetL1SignatureBody(chainId uint32) string {
|
||||
hexMemo := hex.EncodeToString(txInfo.Memo[:])
|
||||
hexMemo = strings.Replace(hexMemo, "0x", "", 1)
|
||||
|
||||
signatureBody := fmt.Sprintf(
|
||||
TemplateTransfer,
|
||||
getHex10FromUint64(uint64(txInfo.Nonce)),
|
||||
getHex10FromUint64(uint64(txInfo.FromAccountIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.FromRouteType)),
|
||||
getHex10FromUint64(uint64(txInfo.ApiKeyIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.ToAccountIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.ToRouteType)),
|
||||
getHex10FromUint64(uint64(txInfo.AssetIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.Amount)), //nolint:gosec
|
||||
getHex10FromUint64(uint64(txInfo.USDCFee)), //nolint:gosec
|
||||
getHex10FromUint64(uint64(chainId)), //nolint:gosec
|
||||
hexMemo,
|
||||
)
|
||||
return signatureBody
|
||||
}
|
||||
|
||||
func (txInfo *L2TransferTxInfo) GetL1AddressBySignature(chainId uint32) common.Address {
|
||||
return calculateL1AddressBySignature(txInfo.GetL1SignatureBody(chainId), txInfo.L1Sig)
|
||||
}
|
||||
|
||||
func (txInfo *L2TransferTxInfo) Hash(lighterChainId uint32, extra ...g.Element) (msgHash []byte, err error) {
|
||||
elems := make([]g.Element, 0, 11)
|
||||
elems := make([]g.Element, 0, 14)
|
||||
|
||||
elems = append(elems, g.FromUint32(lighterChainId))
|
||||
elems = append(elems, g.FromUint32(TxTypeL2Transfer))
|
||||
@@ -101,28 +146,13 @@ func (txInfo *L2TransferTxInfo) Hash(lighterChainId uint32, extra ...g.Element)
|
||||
elems = append(elems, g.FromInt64(txInfo.FromAccountIndex))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.ApiKeyIndex)))
|
||||
elems = append(elems, g.FromInt64(txInfo.ToAccountIndex))
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.USDCAmount)&0xFFFFFFFF)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.USDCAmount)>>32)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.Fee)&0xFFFFFFFF)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.Fee)>>32)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.AssetIndex)))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.FromRouteType)))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.ToRouteType)))
|
||||
elems = append(elems, g.FromUint64((uint64(txInfo.Amount))&0xFFFFFFFF)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.Amount)>>32)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64((uint64(txInfo.USDCFee))&0xFFFFFFFF)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64((uint64(txInfo.USDCFee))>>32)) //nolint:gosec
|
||||
|
||||
return p2.HashToQuinticExtension(elems).ToLittleEndianBytes(), nil
|
||||
}
|
||||
|
||||
func (txInfo *L2TransferTxInfo) GetL1SignatureBody() string {
|
||||
hexMemo := hex.EncodeToString(txInfo.Memo[:])
|
||||
hexMemo = strings.Replace(hexMemo, "0x", "", 1)
|
||||
|
||||
signatureBody := fmt.Sprintf(
|
||||
templateTransfer,
|
||||
|
||||
getHex10FromUint64(uint64(txInfo.Nonce)),
|
||||
getHex10FromUint64(uint64(txInfo.FromAccountIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.ApiKeyIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.ToAccountIndex)),
|
||||
getHex10FromUint64(uint64(txInfo.USDCAmount)),
|
||||
getHex10FromUint64(uint64(txInfo.Fee)),
|
||||
hexMemo,
|
||||
)
|
||||
return signatureBody
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ type L2UpdateLeverageTxInfo struct {
|
||||
AccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
InitialMarginFraction uint16
|
||||
MarginMode uint8
|
||||
|
||||
@@ -48,13 +48,13 @@ func (txInfo *L2UpdateLeverageTxInfo) Validate() error {
|
||||
if txInfo.ApiKeyIndex > MaxApiKeyIndex {
|
||||
return ErrApiKeyIndexTooHigh
|
||||
}
|
||||
|
||||
// MarketIndex
|
||||
if txInfo.MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
if txInfo.MarketIndex == NilMarketIndex {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
if txInfo.MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
|
||||
if txInfo.MarginMode != CrossMargin && txInfo.MarginMode != IsolatedMargin {
|
||||
return ErrInvalidMarginMode
|
||||
}
|
||||
|
||||
// InitialMarginFraction
|
||||
@@ -73,10 +73,6 @@ func (txInfo *L2UpdateLeverageTxInfo) Validate() error {
|
||||
return ErrExpiredAtInvalid
|
||||
}
|
||||
|
||||
if txInfo.MarginMode != CrossMargin && txInfo.MarginMode != IsolatedMargin {
|
||||
return ErrInvalidMarginMode
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ type L2UpdateMarginTxInfo struct {
|
||||
AccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
|
||||
MarketIndex uint8
|
||||
MarketIndex int16
|
||||
USDCAmount int64
|
||||
Direction uint8
|
||||
|
||||
@@ -50,20 +50,16 @@ func (txInfo *L2UpdateMarginTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MarketIndex
|
||||
if txInfo.MarketIndex < MinMarketIndex {
|
||||
return ErrMarketIndexTooLow
|
||||
}
|
||||
if txInfo.MarketIndex > MaxMarketIndex {
|
||||
return ErrMarketIndexTooHigh
|
||||
if txInfo.MarketIndex < MinPerpsMarketIndex || txInfo.MarketIndex > MaxPerpsMarketIndex {
|
||||
return ErrInvalidMarketIndex
|
||||
}
|
||||
|
||||
if txInfo.USDCAmount <= 0 {
|
||||
if txInfo.USDCAmount == 0 {
|
||||
return ErrTransferAmountTooLow
|
||||
}
|
||||
if txInfo.USDCAmount > MaxTransferAmount {
|
||||
return ErrTransferAmountTooHigh
|
||||
}
|
||||
|
||||
if txInfo.Direction != RemoveFromIsolatedMargin && txInfo.Direction != AddToIsolatedMargin {
|
||||
return ErrInvalidUpdateMarginDirection
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type L2UpdatePublicPoolTxInfo struct {
|
||||
|
||||
Status uint8
|
||||
OperatorFee int64
|
||||
MinOperatorShareRate int64
|
||||
MinOperatorShareRate uint16
|
||||
|
||||
ExpiredAt int64
|
||||
Nonce int64
|
||||
@@ -71,9 +71,6 @@ func (txInfo *L2UpdatePublicPoolTxInfo) Validate() error {
|
||||
}
|
||||
|
||||
// MinOperatorShareRate
|
||||
if txInfo.MinOperatorShareRate < 0 {
|
||||
return ErrPoolMinOperatorShareRateTooLow
|
||||
}
|
||||
if txInfo.MinOperatorShareRate > ShareTick {
|
||||
return ErrPoolMinOperatorShareRateTooHigh
|
||||
}
|
||||
@@ -103,7 +100,7 @@ func (txInfo *L2UpdatePublicPoolTxInfo) Hash(lighterChainId uint32, extra ...g.E
|
||||
elems = append(elems, g.FromInt64(txInfo.PublicPoolIndex))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.Status)))
|
||||
elems = append(elems, g.FromInt64(txInfo.OperatorFee))
|
||||
elems = append(elems, g.FromInt64(txInfo.MinOperatorShareRate))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.MinOperatorShareRate)))
|
||||
|
||||
return p2.HashToQuinticExtension(elems).ToLittleEndianBytes(), nil
|
||||
}
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
package txtypes
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
func IsValidPubKey(bytes []byte) bool {
|
||||
if len(bytes) != 40 {
|
||||
return false
|
||||
}
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
gQuint "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
return !isZeroByteSlice(bytes)
|
||||
const (
|
||||
TemplateChangePubKey = "Register Lighter Account\n\npubkey: 0x%s\nnonce: %s\naccount index: %s\napi key index: %s\nOnly sign this message for a trusted client!"
|
||||
TemplateTransfer = "Transfer\n\nnonce: %s\nfrom: %s (route %s)\napi key: %s\nto: %s (route %s)\nasset: %s\namount: %s\nfee: %s" +
|
||||
"\nchainId: %s\nmemo: %s\nOnly sign this message for a trusted client!"
|
||||
TemplateSubAccount = "Create Lighter Sub Account\n\nmaster account index: %s\nOnly sign this message for a trusted client!"
|
||||
)
|
||||
|
||||
const SignatureLength = 80
|
||||
const L1SignatureLength = 65
|
||||
const PubKeyLength = gFp5.Bytes
|
||||
const HashLength = gQuint.Bytes
|
||||
|
||||
func IsValidPubKeyLength(bytes []byte) bool {
|
||||
return len(bytes) == gFp5.Bytes
|
||||
}
|
||||
|
||||
func isZeroByteSlice(bytes []byte) bool {
|
||||
func IsZeroByteSlice(bytes []byte) bool {
|
||||
for _, s := range bytes {
|
||||
if s != 0 {
|
||||
return false
|
||||
@@ -26,3 +45,44 @@ func getTxInfo(tx interface{}) (string, error) {
|
||||
}
|
||||
return string(txInfoBytes), nil
|
||||
}
|
||||
|
||||
func getHex10FromUint64(value uint64) string {
|
||||
v := hexutil.EncodeUint64(value)
|
||||
v = strings.Replace(v, "0x", "", 1)
|
||||
|
||||
// Make sure result has fixed bytes
|
||||
vBytes := []byte(v)
|
||||
if len(vBytes) < 16 {
|
||||
toAppend := make([]byte, 16-len(vBytes))
|
||||
for i := range toAppend {
|
||||
toAppend[i] = 48
|
||||
}
|
||||
vBytes = append(toAppend, vBytes...)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("0x%s", string(vBytes))
|
||||
}
|
||||
|
||||
func calculateL1AddressBySignature(signatureBody, l1Signature string) common.Address {
|
||||
message := accounts.TextHash([]byte(signatureBody))
|
||||
// Decode from signature string to get the signature byte array
|
||||
signatureContent, err := hexutil.Decode(l1Signature)
|
||||
if err != nil {
|
||||
return [20]byte{}
|
||||
}
|
||||
|
||||
// Transform yellow paper V from 27/28 to 0/1
|
||||
if signatureContent[64] >= 27 {
|
||||
signatureContent[64] -= 27
|
||||
}
|
||||
|
||||
// Calculate the public key from the signature and source string
|
||||
signaturePublicKey, err := crypto.SigToPub(message, signatureContent)
|
||||
if err != nil {
|
||||
return [20]byte{}
|
||||
}
|
||||
|
||||
// Calculate the address from the public key
|
||||
publicAddress := crypto.PubkeyToAddress(*signaturePublicKey)
|
||||
return publicAddress
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ var _ TxInfo = (*L2WithdrawTxInfo)(nil)
|
||||
type L2WithdrawTxInfo struct {
|
||||
FromAccountIndex int64
|
||||
ApiKeyIndex uint8
|
||||
|
||||
USDCAmount uint64 // USDCAmount is given with 6 decimals
|
||||
|
||||
ExpiredAt int64
|
||||
Nonce int64
|
||||
Sig []byte
|
||||
SignedHash string `json:"-"`
|
||||
AssetIndex int16
|
||||
RouteType uint8
|
||||
Amount uint64
|
||||
ExpiredAt int64
|
||||
Nonce int64
|
||||
Sig []byte
|
||||
SignedHash string `json:"-"`
|
||||
}
|
||||
|
||||
func (txInfo *L2WithdrawTxInfo) Validate() error {
|
||||
@@ -35,10 +35,24 @@ func (txInfo *L2WithdrawTxInfo) Validate() error {
|
||||
return ErrApiKeyIndexTooHigh
|
||||
}
|
||||
|
||||
if txInfo.USDCAmount == 0 {
|
||||
// AssetIndex
|
||||
if txInfo.AssetIndex < MinAssetIndex {
|
||||
return ErrAssetIndexTooLow
|
||||
}
|
||||
if txInfo.AssetIndex > MaxAssetIndex {
|
||||
return ErrAssetIndexTooHigh
|
||||
}
|
||||
|
||||
// RouteType
|
||||
if txInfo.RouteType != AssetRouteType_Perps && txInfo.RouteType != AssetRouteType_Spot {
|
||||
return ErrRouteTypeInvalid
|
||||
}
|
||||
|
||||
// Amount
|
||||
if txInfo.Amount == 0 {
|
||||
return ErrWithdrawalAmountTooLow
|
||||
}
|
||||
if txInfo.USDCAmount > MaxWithdrawalAmount {
|
||||
if txInfo.Amount > MaxWithdrawalAmount {
|
||||
return ErrWithdrawalAmountTooHigh
|
||||
}
|
||||
|
||||
@@ -66,7 +80,7 @@ func (txInfo *L2WithdrawTxInfo) GetTxHash() string {
|
||||
}
|
||||
|
||||
func (txInfo *L2WithdrawTxInfo) Hash(lighterChainId uint32, extra ...g.Element) (msgHash []byte, err error) {
|
||||
elems := make([]g.Element, 0, 8)
|
||||
elems := make([]g.Element, 0, 14)
|
||||
|
||||
elems = append(elems, g.FromUint32(lighterChainId))
|
||||
elems = append(elems, g.FromUint32(TxTypeL2Withdraw))
|
||||
@@ -75,8 +89,10 @@ func (txInfo *L2WithdrawTxInfo) Hash(lighterChainId uint32, extra ...g.Element)
|
||||
|
||||
elems = append(elems, g.FromInt64(txInfo.FromAccountIndex))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.ApiKeyIndex)))
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.USDCAmount)&0xFFFFFFFF)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.USDCAmount)>>32)) //nolint:gosec
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.AssetIndex)))
|
||||
elems = append(elems, g.FromUint32(uint32(txInfo.RouteType)))
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.Amount&0xFFFFFFFF)))
|
||||
elems = append(elems, g.FromUint64(uint64(txInfo.Amount>>32)))
|
||||
|
||||
return p2.HashToQuinticExtension(elems).ToLittleEndianBytes(), nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,731 @@
|
||||
//go:build js
|
||||
// +build js
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"syscall/js"
|
||||
"time"
|
||||
|
||||
"github.com/elliottech/lighter-go/client"
|
||||
"github.com/elliottech/lighter-go/client/http"
|
||||
"github.com/elliottech/lighter-go/types"
|
||||
"github.com/elliottech/lighter-go/types/txtypes"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func wrapErr(err error) js.Value {
|
||||
if err != nil {
|
||||
return js.ValueOf(map[string]interface{}{"error": fmt.Sprintf("%v", err)})
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{})
|
||||
}
|
||||
|
||||
func messageToSign(info txtypes.TxInfo) string {
|
||||
switch tx := info.(type) {
|
||||
case *txtypes.L2ChangePubKeyTxInfo:
|
||||
return tx.GetL1SignatureBody()
|
||||
case *txtypes.L2TransferTxInfo:
|
||||
return tx.GetL1SignatureBody()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func convertTxInfoToJS(info txtypes.TxInfo, err error) js.Value {
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
if info == nil {
|
||||
return js.ValueOf(map[string]interface{}{"error": "nil response"})
|
||||
}
|
||||
|
||||
txInfoStr, strErr := info.GetTxInfo()
|
||||
if strErr != nil {
|
||||
return wrapErr(strErr)
|
||||
}
|
||||
|
||||
out := map[string]interface{}{
|
||||
"txType": info.GetTxType(),
|
||||
"txInfo": txInfoStr,
|
||||
"txHash": info.GetTxHash(),
|
||||
}
|
||||
if msg := messageToSign(info); msg != "" {
|
||||
out["messageToSign"] = msg
|
||||
}
|
||||
return js.ValueOf(out)
|
||||
}
|
||||
|
||||
// safeInt safely extracts an int from a js.Value, handling undefined values
|
||||
func safeInt(v js.Value, index int) (int64, error) {
|
||||
if v.Type() == js.TypeUndefined {
|
||||
return 0, fmt.Errorf("argument %d is undefined", index)
|
||||
}
|
||||
return int64(v.Int()), nil
|
||||
}
|
||||
|
||||
// safeUint8 safely extracts a uint8 from a js.Value, handling undefined values
|
||||
func safeUint8(v js.Value, index int) (uint8, error) {
|
||||
if v.Type() == js.TypeUndefined {
|
||||
return 0, fmt.Errorf("argument %d is undefined", index)
|
||||
}
|
||||
return uint8(v.Int()), nil
|
||||
}
|
||||
|
||||
// safeUint32 safely extracts a uint32 from a js.Value, handling undefined values
|
||||
func safeUint32(v js.Value, index int) (uint32, error) {
|
||||
if v.Type() == js.TypeUndefined {
|
||||
return 0, fmt.Errorf("argument %d is undefined", index)
|
||||
}
|
||||
return uint32(v.Int()), nil
|
||||
}
|
||||
|
||||
func getClient(args []js.Value) (*client.TxClient, error) {
|
||||
l := len(args)
|
||||
if l < 2 {
|
||||
return nil, fmt.Errorf("insufficient arguments: need at least 2 for apiKeyIndex and accountIndex")
|
||||
}
|
||||
// Check if the last two arguments are valid and extract safely
|
||||
if args[l-2].Type() == js.TypeUndefined || args[l-1].Type() == js.TypeUndefined {
|
||||
return nil, fmt.Errorf("apiKeyIndex or accountIndex is undefined")
|
||||
}
|
||||
apiKeyIndexVal, err := safeUint8(args[l-2], l-2)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
accountIndexVal, err := safeInt(args[l-1], l-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client.GetClient(apiKeyIndexVal, accountIndexVal)
|
||||
}
|
||||
|
||||
// recoverPanic wraps a function execution with panic recovery
|
||||
func recoverPanic(fn func() js.Value) (result js.Value) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = wrapErr(fmt.Errorf("panic: %v", r))
|
||||
}
|
||||
}()
|
||||
return fn()
|
||||
}
|
||||
|
||||
func main() {
|
||||
js.Global().Set("GenerateAPIKey", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 1 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "GenerateAPIKey expects 1 arg: seed"})
|
||||
}
|
||||
seed := args[0].String()
|
||||
privateKey, publicKey, err := client.GenerateAPIKey(seed)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{"privateKey": privateKey, "publicKey": publicKey})
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("CreateClient", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "CreateClient expects 5 args: url, privateKey, chainId, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
url := args[0].String()
|
||||
privateKey := args[1].String()
|
||||
chainId := uint32(args[2].Int())
|
||||
apiKeyIndex := uint8(args[3].Int())
|
||||
accountIndex := int64(args[4].Int())
|
||||
httpClient := http.NewClient(url)
|
||||
_, err := client.CreateClient(httpClient, privateKey, chainId, apiKeyIndex, accountIndex)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
return wrapErr(nil)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("CheckClient", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 2 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "CheckClient expects 2 args: apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
err = c.Check()
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
return wrapErr(nil)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("CreateAuthToken", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 3 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "CreateAuthToken expects 3 args: deadline, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
deadline := int64(args[0].Int())
|
||||
if deadline == 0 {
|
||||
deadline = time.Now().Add(time.Hour * 7).Unix()
|
||||
}
|
||||
|
||||
token, err := c.GetAuthToken(time.Unix(deadline, 0))
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
return js.ValueOf(map[string]interface{}{"authToken": token})
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignChangePubKey", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 4 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignChangePubKey expects 4 args: pubKeyHex, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
pubKeyHex := args[0].String()
|
||||
nonce := int64(args[1].Int())
|
||||
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
pubKeyBytes, err := hexutil.Decode(pubKeyHex)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
if len(pubKeyBytes) != 40 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "invalid pub key length. expected 40 but got " + strconv.Itoa(len(pubKeyBytes))})
|
||||
}
|
||||
var pubKey [40]byte
|
||||
copy(pubKey[:], pubKeyBytes)
|
||||
|
||||
txInfo := &types.ChangePubKeyReq{
|
||||
PubKey: pubKey,
|
||||
}
|
||||
ops := &types.TransactOpts{
|
||||
Nonce: &nonce,
|
||||
}
|
||||
|
||||
tx, err := c.GetChangePubKeyTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCreateOrder", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 13 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCreateOrder expects 13 args: marketIndex, clientOrderIndex, baseAmount, price, isAsk, orderType, timeInForce, reduceOnly, triggerPrice, orderExpiry, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
// Validate all arguments are defined before accessing
|
||||
for i := 0; i < 13; i++ {
|
||||
if args[i].Type() == js.TypeUndefined {
|
||||
return js.ValueOf(map[string]interface{}{"error": fmt.Sprintf("argument %d is undefined", i)})
|
||||
}
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
marketIndex, err := safeUint8(args[0], 0)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
clientOrderIndex, err := safeInt(args[1], 1)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
baseAmount, err := safeInt(args[2], 2)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
price, err := safeUint32(args[3], 3)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
isAsk, err := safeUint8(args[4], 4)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
orderType, err := safeUint8(args[5], 5)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
timeInForce, err := safeUint8(args[6], 6)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
reduceOnly, err := safeUint8(args[7], 7)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
triggerPrice, err := safeUint32(args[8], 8)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
orderExpiry, err := safeInt(args[9], 9)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
nonce, err := safeInt(args[10], 10)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
if orderExpiry == -1 {
|
||||
orderExpiry = time.Now().Add(time.Hour * 24 * 28).UnixMilli() // 28 days
|
||||
}
|
||||
|
||||
txInfo := &types.CreateOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
ClientOrderIndex: clientOrderIndex,
|
||||
BaseAmount: baseAmount,
|
||||
Price: price,
|
||||
IsAsk: isAsk,
|
||||
Type: orderType,
|
||||
TimeInForce: timeInForce,
|
||||
ReduceOnly: reduceOnly,
|
||||
TriggerPrice: triggerPrice,
|
||||
OrderExpiry: orderExpiry,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetCreateOrderTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCancelOrder", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCancelOrder expects 5 args: marketIndex, orderIndex, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
marketIndex := uint8(args[0].Int())
|
||||
orderIndex := int64(args[1].Int())
|
||||
nonce := int64(args[2].Int())
|
||||
|
||||
txInfo := &types.CancelOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
Index: orderIndex,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetCancelOrderTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCancelAllOrders", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCancelAllOrders expects 5 args: timeInForce, time, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
timeInForce := uint8(args[0].Int())
|
||||
timeVal := int64(args[1].Int())
|
||||
nonce := int64(args[2].Int())
|
||||
|
||||
txInfo := &types.CancelAllOrdersTxReq{
|
||||
TimeInForce: timeInForce,
|
||||
Time: timeVal,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetCancelAllOrdersTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignTransfer", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 7 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignTransfer expects 7 args: toAccount, usdcAmount, fee, memo, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
toAccount := int64(args[0].Int())
|
||||
usdcAmount := int64(args[1].Int())
|
||||
fee := int64(args[2].Int())
|
||||
memoStr := args[3].String()
|
||||
nonce := int64(args[4].Int())
|
||||
|
||||
var memoArr [32]byte
|
||||
bs := []byte(memoStr)
|
||||
if len(bs) != 32 {
|
||||
return wrapErr(fmt.Errorf("memo expected to be 32 bytes long"))
|
||||
}
|
||||
for i := 0; i < 32; i++ {
|
||||
memoArr[i] = bs[i]
|
||||
}
|
||||
|
||||
txInfo := &types.TransferTxReq{
|
||||
ToAccountIndex: toAccount,
|
||||
USDCAmount: usdcAmount,
|
||||
Fee: fee,
|
||||
Memo: memoArr,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetTransferTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignWithdraw", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 4 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignWithdraw expects 4 args: usdcAmount, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
usdcAmount := uint64(args[0].Int())
|
||||
nonce := int64(args[1].Int())
|
||||
|
||||
txInfo := &types.WithdrawTxReq{
|
||||
USDCAmount: usdcAmount,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetWithdrawTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignUpdateLeverage", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 6 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignUpdateLeverage expects 6 args: marketIndex, fraction, marginMode, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
marketIndex := uint8(args[0].Int())
|
||||
fraction := uint16(args[1].Int())
|
||||
marginMode := uint8(args[2].Int())
|
||||
nonce := int64(args[3].Int())
|
||||
|
||||
txInfo := &types.UpdateLeverageTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
InitialMarginFraction: fraction,
|
||||
MarginMode: marginMode,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetUpdateLeverageTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignModifyOrder", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 8 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignModifyOrder expects 8 args: marketIndex, index, baseAmount, price, triggerPrice, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
marketIndex := uint8(args[0].Int())
|
||||
index := int64(args[1].Int())
|
||||
baseAmount := int64(args[2].Int())
|
||||
price := uint32(args[3].Int())
|
||||
triggerPrice := uint32(args[4].Int())
|
||||
nonce := int64(args[5].Int())
|
||||
|
||||
txInfo := &types.ModifyOrderTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
Index: index,
|
||||
BaseAmount: baseAmount,
|
||||
Price: price,
|
||||
TriggerPrice: triggerPrice,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetModifyOrderTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCreateSubAccount", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 3 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCreateSubAccount expects 3 args: nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
nonce := int64(args[0].Int())
|
||||
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetCreateSubAccountTransaction(ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCreatePublicPool", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 6 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCreatePublicPool expects 6 args: operatorFee, initialTotalShares, minOperatorShareRate, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
operatorFee := int64(args[0].Int())
|
||||
initialTotalShares := int64(args[1].Int())
|
||||
minOperatorShareRate := int64(args[2].Int())
|
||||
nonce := int64(args[3].Int())
|
||||
|
||||
txInfo := &types.CreatePublicPoolTxReq{
|
||||
OperatorFee: operatorFee,
|
||||
InitialTotalShares: initialTotalShares,
|
||||
MinOperatorShareRate: minOperatorShareRate,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetCreatePublicPoolTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignUpdatePublicPool", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 7 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignUpdatePublicPool expects 7 args: publicPoolIndex, status, operatorFee, minOperatorShareRate, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := uint8(args[0].Int())
|
||||
status := uint8(args[1].Int())
|
||||
operatorFee := int64(args[2].Int())
|
||||
minOperatorShareRate := int64(args[3].Int())
|
||||
nonce := int64(args[4].Int())
|
||||
|
||||
txInfo := &types.UpdatePublicPoolTxReq{
|
||||
PublicPoolIndex: int64(publicPoolIndex),
|
||||
Status: status,
|
||||
OperatorFee: operatorFee,
|
||||
MinOperatorShareRate: minOperatorShareRate,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetUpdatePublicPoolTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignMintShares", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignMintShares expects 5 args: publicPoolIndex, shareAmount, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := int64(args[0].Int())
|
||||
shareAmount := int64(args[1].Int())
|
||||
nonce := int64(args[2].Int())
|
||||
|
||||
txInfo := &types.MintSharesTxReq{
|
||||
PublicPoolIndex: publicPoolIndex,
|
||||
ShareAmount: shareAmount,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetMintSharesTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignBurnShares", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignBurnShares expects 5 args: publicPoolIndex, shareAmount, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
publicPoolIndex := int64(args[0].Int())
|
||||
shareAmount := int64(args[1].Int())
|
||||
nonce := int64(args[2].Int())
|
||||
|
||||
txInfo := &types.BurnSharesTxReq{
|
||||
PublicPoolIndex: publicPoolIndex,
|
||||
ShareAmount: shareAmount,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetBurnSharesTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignUpdateMargin", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 6 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignUpdateMargin expects 6 args: marketIndex, usdcAmount, direction, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
marketIndex := uint8(args[0].Int())
|
||||
usdcAmount := int64(args[1].Int())
|
||||
direction := uint8(args[2].Int())
|
||||
nonce := int64(args[3].Int())
|
||||
|
||||
txInfo := &types.UpdateMarginTxReq{
|
||||
MarketIndex: marketIndex,
|
||||
USDCAmount: usdcAmount,
|
||||
Direction: direction,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
tx, err := c.GetUpdateMarginTransaction(txInfo, ops)
|
||||
return convertTxInfoToJS(tx, err)
|
||||
})
|
||||
}))
|
||||
|
||||
js.Global().Set("SignCreateGroupedOrders", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
return recoverPanic(func() js.Value {
|
||||
if len(args) < 5 {
|
||||
return js.ValueOf(map[string]interface{}{"error": "SignCreateGroupedOrders expects 5 args: groupingType, orders array, nonce, apiKeyIndex, accountIndex"})
|
||||
}
|
||||
c, err := getClient(args)
|
||||
if err != nil {
|
||||
return wrapErr(err)
|
||||
}
|
||||
|
||||
groupingType := uint8(args[0].Int())
|
||||
|
||||
// Parse orders array from JS
|
||||
ordersArg := args[1]
|
||||
if ordersArg.Type() != js.TypeObject {
|
||||
return js.ValueOf(map[string]interface{}{"error": "orders must be an array"})
|
||||
}
|
||||
length := ordersArg.Length()
|
||||
orders := make([]*types.CreateOrderTxReq, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
orderObj := ordersArg.Index(i)
|
||||
if orderObj.Type() != js.TypeObject {
|
||||
return js.ValueOf(map[string]interface{}{"error": fmt.Sprintf("order %d must be an object", i)})
|
||||
}
|
||||
|
||||
orderExpiry := int64(orderObj.Get("OrderExpiry").Int())
|
||||
if orderExpiry == -1 {
|
||||
orderExpiry = time.Now().Add(time.Hour * 24 * 28).UnixMilli()
|
||||
}
|
||||
|
||||
orders[i] = &types.CreateOrderTxReq{
|
||||
MarketIndex: uint8(orderObj.Get("MarketIndex").Int()),
|
||||
ClientOrderIndex: int64(orderObj.Get("ClientOrderIndex").Int()),
|
||||
BaseAmount: int64(orderObj.Get("BaseAmount").Int()),
|
||||
Price: uint32(orderObj.Get("Price").Int()),
|
||||
IsAsk: uint8(orderObj.Get("IsAsk").Int()),
|
||||
Type: uint8(orderObj.Get("Type").Int()),
|
||||
TimeInForce: uint8(orderObj.Get("TimeInForce").Int()),
|
||||
ReduceOnly: uint8(orderObj.Get("ReduceOnly").Int()),
|
||||
TriggerPrice: uint32(orderObj.Get("TriggerPrice").Int()),
|
||||
OrderExpiry: orderExpiry,
|
||||
}
|
||||
}
|
||||
|
||||
nonce := int64(args[2].Int())
|
||||
|
||||
req := &types.CreateGroupedOrdersTxReq{
|
||||
GroupingType: groupingType,
|
||||
Orders: orders,
|
||||
}
|
||||
ops := new(types.TransactOpts)
|
||||
if nonce != -1 {
|
||||
ops.Nonce = &nonce
|
||||
}
|
||||
|
||||
txInfo, err := c.GetCreateGroupedOrdersTransaction(req, ops)
|
||||
return convertTxInfoToJS(txInfo, err)
|
||||
})
|
||||
}))
|
||||
|
||||
select {}
|
||||
}
|
||||
@@ -68,4 +68,9 @@ openapi-generator-cli.jar
|
||||
|
||||
.idea
|
||||
|
||||
examples/secrets.py
|
||||
examples/secrets.py
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*
|
||||
api_key_config.json
|
||||
@@ -1,6 +1,7 @@
|
||||
docs/Account.md
|
||||
docs/AccountApi.md
|
||||
docs/AccountApiKeys.md
|
||||
docs/AccountAsset.md
|
||||
docs/AccountLimits.md
|
||||
docs/AccountMarginStats.md
|
||||
docs/AccountMarketStats.md
|
||||
@@ -14,9 +15,12 @@ docs/Announcement.md
|
||||
docs/AnnouncementApi.md
|
||||
docs/Announcements.md
|
||||
docs/ApiKey.md
|
||||
docs/Asset.md
|
||||
docs/AssetDetails.md
|
||||
docs/Block.md
|
||||
docs/BlockApi.md
|
||||
docs/Blocks.md
|
||||
docs/Bridge.md
|
||||
docs/BridgeApi.md
|
||||
docs/BridgeSupportedNetwork.md
|
||||
docs/Candlestick.md
|
||||
@@ -46,28 +50,27 @@ docs/LiqTrade.md
|
||||
docs/Liquidation.md
|
||||
docs/LiquidationInfo.md
|
||||
docs/LiquidationInfos.md
|
||||
docs/MarketInfo.md
|
||||
docs/MarketConfig.md
|
||||
docs/NextNonce.md
|
||||
docs/NotificationApi.md
|
||||
docs/Order.md
|
||||
docs/OrderApi.md
|
||||
docs/OrderBook.md
|
||||
docs/OrderBookDepth.md
|
||||
docs/OrderBookDetail.md
|
||||
docs/OrderBookDetails.md
|
||||
docs/OrderBookOrders.md
|
||||
docs/OrderBookStats.md
|
||||
docs/OrderBooks.md
|
||||
docs/Orders.md
|
||||
docs/PerpsMarketStats.md
|
||||
docs/PerpsOrderBookDetail.md
|
||||
docs/PnLEntry.md
|
||||
docs/PositionFunding.md
|
||||
docs/PositionFundings.md
|
||||
docs/PriceLevel.md
|
||||
docs/PublicPool.md
|
||||
docs/PublicPoolInfo.md
|
||||
docs/PublicPoolMetadata.md
|
||||
docs/PublicPoolShare.md
|
||||
docs/PublicPools.md
|
||||
docs/ReferralApi.md
|
||||
docs/ReferralPointEntry.md
|
||||
docs/ReferralPoints.md
|
||||
@@ -81,8 +84,10 @@ docs/ReqGetAccountLimits.md
|
||||
docs/ReqGetAccountMetadata.md
|
||||
docs/ReqGetAccountPnL.md
|
||||
docs/ReqGetAccountTxs.md
|
||||
docs/ReqGetAssetDetails.md
|
||||
docs/ReqGetBlock.md
|
||||
docs/ReqGetBlockTxs.md
|
||||
docs/ReqGetBridgesByL1Addr.md
|
||||
docs/ReqGetByAccount.md
|
||||
docs/ReqGetCandlesticks.md
|
||||
docs/ReqGetDepositHistory.md
|
||||
@@ -97,7 +102,6 @@ docs/ReqGetOrderBookDetails.md
|
||||
docs/ReqGetOrderBookOrders.md
|
||||
docs/ReqGetOrderBooks.md
|
||||
docs/ReqGetPositionFunding.md
|
||||
docs/ReqGetPublicPools.md
|
||||
docs/ReqGetPublicPoolsMetadata.md
|
||||
docs/ReqGetRangeWithCursor.md
|
||||
docs/ReqGetRangeWithIndex.md
|
||||
@@ -110,10 +114,14 @@ docs/ReqGetTransferHistory.md
|
||||
docs/ReqGetTx.md
|
||||
docs/ReqGetWithdrawHistory.md
|
||||
docs/RespChangeAccountTier.md
|
||||
docs/RespGetBridgesByL1Addr.md
|
||||
docs/RespGetFastBridgeInfo.md
|
||||
docs/RespGetIsNextBridgeFast.md
|
||||
docs/RespPublicPoolsMetadata.md
|
||||
docs/RespSendTx.md
|
||||
docs/RespSendTxBatch.md
|
||||
docs/RespUpdateKickback.md
|
||||
docs/RespUpdateReferralCode.md
|
||||
docs/RespWithdrawalDelay.md
|
||||
docs/ResultCode.md
|
||||
docs/RiskInfo.md
|
||||
@@ -121,6 +129,8 @@ docs/RiskParameters.md
|
||||
docs/RootApi.md
|
||||
docs/SharePrice.md
|
||||
docs/SimpleOrder.md
|
||||
docs/SpotMarketStats.md
|
||||
docs/SpotOrderBookDetail.md
|
||||
docs/Status.md
|
||||
docs/SubAccounts.md
|
||||
docs/Ticker.md
|
||||
@@ -160,6 +170,7 @@ lighter/exceptions.py
|
||||
lighter/models/__init__.py
|
||||
lighter/models/account.py
|
||||
lighter/models/account_api_keys.py
|
||||
lighter/models/account_asset.py
|
||||
lighter/models/account_limits.py
|
||||
lighter/models/account_margin_stats.py
|
||||
lighter/models/account_market_stats.py
|
||||
@@ -172,8 +183,11 @@ lighter/models/account_trade_stats.py
|
||||
lighter/models/announcement.py
|
||||
lighter/models/announcements.py
|
||||
lighter/models/api_key.py
|
||||
lighter/models/asset.py
|
||||
lighter/models/asset_details.py
|
||||
lighter/models/block.py
|
||||
lighter/models/blocks.py
|
||||
lighter/models/bridge.py
|
||||
lighter/models/bridge_supported_network.py
|
||||
lighter/models/candlestick.py
|
||||
lighter/models/candlesticks.py
|
||||
@@ -199,26 +213,25 @@ lighter/models/liq_trade.py
|
||||
lighter/models/liquidation.py
|
||||
lighter/models/liquidation_info.py
|
||||
lighter/models/liquidation_infos.py
|
||||
lighter/models/market_info.py
|
||||
lighter/models/market_config.py
|
||||
lighter/models/next_nonce.py
|
||||
lighter/models/order.py
|
||||
lighter/models/order_book.py
|
||||
lighter/models/order_book_depth.py
|
||||
lighter/models/order_book_detail.py
|
||||
lighter/models/order_book_details.py
|
||||
lighter/models/order_book_orders.py
|
||||
lighter/models/order_book_stats.py
|
||||
lighter/models/order_books.py
|
||||
lighter/models/orders.py
|
||||
lighter/models/perps_market_stats.py
|
||||
lighter/models/perps_order_book_detail.py
|
||||
lighter/models/pn_l_entry.py
|
||||
lighter/models/position_funding.py
|
||||
lighter/models/position_fundings.py
|
||||
lighter/models/price_level.py
|
||||
lighter/models/public_pool.py
|
||||
lighter/models/public_pool_info.py
|
||||
lighter/models/public_pool_metadata.py
|
||||
lighter/models/public_pool_share.py
|
||||
lighter/models/public_pools.py
|
||||
lighter/models/referral_point_entry.py
|
||||
lighter/models/referral_points.py
|
||||
lighter/models/req_export_data.py
|
||||
@@ -231,8 +244,10 @@ lighter/models/req_get_account_limits.py
|
||||
lighter/models/req_get_account_metadata.py
|
||||
lighter/models/req_get_account_pn_l.py
|
||||
lighter/models/req_get_account_txs.py
|
||||
lighter/models/req_get_asset_details.py
|
||||
lighter/models/req_get_block.py
|
||||
lighter/models/req_get_block_txs.py
|
||||
lighter/models/req_get_bridges_by_l1_addr.py
|
||||
lighter/models/req_get_by_account.py
|
||||
lighter/models/req_get_candlesticks.py
|
||||
lighter/models/req_get_deposit_history.py
|
||||
@@ -247,7 +262,6 @@ lighter/models/req_get_order_book_details.py
|
||||
lighter/models/req_get_order_book_orders.py
|
||||
lighter/models/req_get_order_books.py
|
||||
lighter/models/req_get_position_funding.py
|
||||
lighter/models/req_get_public_pools.py
|
||||
lighter/models/req_get_public_pools_metadata.py
|
||||
lighter/models/req_get_range_with_cursor.py
|
||||
lighter/models/req_get_range_with_index.py
|
||||
@@ -260,16 +274,22 @@ lighter/models/req_get_transfer_history.py
|
||||
lighter/models/req_get_tx.py
|
||||
lighter/models/req_get_withdraw_history.py
|
||||
lighter/models/resp_change_account_tier.py
|
||||
lighter/models/resp_get_bridges_by_l1_addr.py
|
||||
lighter/models/resp_get_fast_bridge_info.py
|
||||
lighter/models/resp_get_is_next_bridge_fast.py
|
||||
lighter/models/resp_public_pools_metadata.py
|
||||
lighter/models/resp_send_tx.py
|
||||
lighter/models/resp_send_tx_batch.py
|
||||
lighter/models/resp_update_kickback.py
|
||||
lighter/models/resp_update_referral_code.py
|
||||
lighter/models/resp_withdrawal_delay.py
|
||||
lighter/models/result_code.py
|
||||
lighter/models/risk_info.py
|
||||
lighter/models/risk_parameters.py
|
||||
lighter/models/share_price.py
|
||||
lighter/models/simple_order.py
|
||||
lighter/models/spot_market_stats.py
|
||||
lighter/models/spot_order_book_detail.py
|
||||
lighter/models/status.py
|
||||
lighter/models/sub_accounts.py
|
||||
lighter/models/ticker.py
|
||||
@@ -291,4 +311,19 @@ lighter/rest.py
|
||||
setup.cfg
|
||||
test-requirements.txt
|
||||
test/__init__.py
|
||||
test/test_account_asset.py
|
||||
test/test_asset.py
|
||||
test/test_asset_details.py
|
||||
test/test_bridge.py
|
||||
test/test_market_config.py
|
||||
test/test_perps_market_stats.py
|
||||
test/test_perps_order_book_detail.py
|
||||
test/test_req_get_asset_details.py
|
||||
test/test_req_get_bridges_by_l1_addr.py
|
||||
test/test_resp_get_bridges_by_l1_addr.py
|
||||
test/test_resp_get_is_next_bridge_fast.py
|
||||
test/test_resp_update_kickback.py
|
||||
test/test_resp_update_referral_code.py
|
||||
test/test_spot_market_stats.py
|
||||
test/test_spot_order_book_detail.py
|
||||
tox.ini
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -36,9 +36,12 @@ import asyncio
|
||||
|
||||
async def main():
|
||||
client = lighter.ApiClient()
|
||||
account_api = lighter.AccountApi(client)
|
||||
account = await account_api.get_account(by="index", value="1")
|
||||
print(account)
|
||||
try:
|
||||
account_api = lighter.AccountApi(client)
|
||||
account = await account_api.account(by="index", value="1")
|
||||
print(account)
|
||||
finally:
|
||||
await client.close() # Make sure connection is cleanly closed
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
disallowAdditionalPropertiesIfNotPresent: false
|
||||
library: asyncio
|
||||
packageName: lighter-sdk
|
||||
projectName: lighter-sdk
|
||||
@@ -12,7 +12,6 @@ Name | Type | Description | Notes
|
||||
**l1_address** | **str** | |
|
||||
**cancel_all_time** | **int** | |
|
||||
**total_order_count** | **int** | |
|
||||
**total_isolated_order_count** | **int** | |
|
||||
**pending_order_count** | **int** | |
|
||||
**available_balance** | **str** | |
|
||||
**status** | **int** | |
|
||||
|
||||
@@ -14,7 +14,6 @@ Method | HTTP request | Description
|
||||
[**liquidations**](AccountApi.md#liquidations) | **GET** /api/v1/liquidations | liquidations
|
||||
[**pnl**](AccountApi.md#pnl) | **GET** /api/v1/pnl | pnl
|
||||
[**position_funding**](AccountApi.md#position_funding) | **GET** /api/v1/positionFunding | positionFunding
|
||||
[**public_pools**](AccountApi.md#public_pools) | **GET** /api/v1/publicPools | publicPools
|
||||
[**public_pools_metadata**](AccountApi.md#public_pools_metadata) | **GET** /api/v1/publicPoolsMetadata | publicPoolsMetadata
|
||||
|
||||
|
||||
@@ -770,85 +769,6 @@ No authorization required
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **public_pools**
|
||||
> PublicPools public_pools(index, limit, authorization=authorization, auth=auth, filter=filter, account_index=account_index)
|
||||
|
||||
publicPools
|
||||
|
||||
Get public pools
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.public_pools import PublicPools
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.AccountApi(api_client)
|
||||
index = 56 # int |
|
||||
limit = 56 # int |
|
||||
authorization = 'authorization_example' # str | (optional)
|
||||
auth = 'auth_example' # str | (optional)
|
||||
filter = 'filter_example' # str | (optional)
|
||||
account_index = 56 # int | (optional)
|
||||
|
||||
try:
|
||||
# publicPools
|
||||
api_response = await api_instance.public_pools(index, limit, authorization=authorization, auth=auth, filter=filter, account_index=account_index)
|
||||
print("The response of AccountApi->public_pools:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling AccountApi->public_pools: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**index** | **int**| |
|
||||
**limit** | **int**| |
|
||||
**authorization** | **str**| | [optional]
|
||||
**auth** | **str**| | [optional]
|
||||
**filter** | **str**| | [optional]
|
||||
**account_index** | **int**| | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**PublicPools**](PublicPools.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **public_pools_metadata**
|
||||
> RespPublicPoolsMetadata public_pools_metadata(index, limit, authorization=authorization, auth=auth, filter=filter, account_index=account_index)
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# AccountAsset
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**asset_id** | **int** | |
|
||||
**balance** | **str** | |
|
||||
**locked_balance** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.account_asset import AccountAsset
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of AccountAsset from a JSON string
|
||||
account_asset_instance = AccountAsset.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(AccountAsset.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
account_asset_dict = account_asset_instance.to_dict()
|
||||
# create an instance of AccountAsset from a dict
|
||||
account_asset_from_dict = AccountAsset.from_dict(account_asset_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@ Name | Type | Description | Notes
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**max_llp_percentage** | **int** | |
|
||||
**max_llp_amount** | **str** | |
|
||||
**user_tier** | **str** | |
|
||||
**can_create_public_pool** | **bool** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Name | Type | Description | Notes
|
||||
**title** | **str** | |
|
||||
**content** | **str** | |
|
||||
**created_at** | **int** | |
|
||||
**expired_at** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
# Asset
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**asset_id** | **int** | |
|
||||
**symbol** | **str** | |
|
||||
**l1_decimals** | **int** | |
|
||||
**decimals** | **int** | |
|
||||
**min_transfer_amount** | **str** | |
|
||||
**min_withdrawal_amount** | **str** | |
|
||||
**margin_mode** | **str** | |
|
||||
**index_price** | **str** | |
|
||||
**l1_address** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.asset import Asset
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of Asset from a JSON string
|
||||
asset_instance = Asset.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(Asset.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
asset_dict = asset_instance.to_dict()
|
||||
# create an instance of Asset from a dict
|
||||
asset_from_dict = Asset.from_dict(asset_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
+9
-10
@@ -1,4 +1,4 @@
|
||||
# PublicPools
|
||||
# AssetDetails
|
||||
|
||||
|
||||
## Properties
|
||||
@@ -7,25 +7,24 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**total** | **int** | |
|
||||
**public_pools** | [**List[PublicPool]**](PublicPool.md) | |
|
||||
**asset_details** | [**List[Asset]**](Asset.md) | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.public_pools import PublicPools
|
||||
from lighter.models.asset_details import AssetDetails
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of PublicPools from a JSON string
|
||||
public_pools_instance = PublicPools.from_json(json)
|
||||
# create an instance of AssetDetails from a JSON string
|
||||
asset_details_instance = AssetDetails.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(PublicPools.to_json())
|
||||
print(AssetDetails.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
public_pools_dict = public_pools_instance.to_dict()
|
||||
# create an instance of PublicPools from a dict
|
||||
public_pools_from_dict = PublicPools.from_dict(public_pools_dict)
|
||||
asset_details_dict = asset_details_instance.to_dict()
|
||||
# create an instance of AssetDetails from a dict
|
||||
asset_details_from_dict = AssetDetails.from_dict(asset_details_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# Bridge
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **int** | |
|
||||
**version** | **int** | |
|
||||
**source** | **str** | |
|
||||
**source_chain_id** | **str** | |
|
||||
**fast_bridge_tx_hash** | **str** | |
|
||||
**batch_claim_tx_hash** | **str** | |
|
||||
**cctp_burn_tx_hash** | **str** | |
|
||||
**amount** | **str** | |
|
||||
**intent_address** | **str** | |
|
||||
**status** | **str** | |
|
||||
**step** | **str** | |
|
||||
**description** | **str** | |
|
||||
**created_at** | **int** | |
|
||||
**updated_at** | **int** | |
|
||||
**is_external_deposit** | **bool** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.bridge import Bridge
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of Bridge from a JSON string
|
||||
bridge_instance = Bridge.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(Bridge.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
bridge_dict = bridge_instance.to_dict()
|
||||
# create an instance of Bridge from a dict
|
||||
bridge_from_dict = Bridge.from_dict(bridge_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -4,9 +4,149 @@ All URIs are relative to *https://mainnet.zklighter.elliot.ai*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**bridges**](BridgeApi.md#bridges) | **GET** /api/v1/bridges | bridges
|
||||
[**bridges_is_next_bridge_fast**](BridgeApi.md#bridges_is_next_bridge_fast) | **GET** /api/v1/bridges/isNextBridgeFast | bridges_isNextBridgeFast
|
||||
[**fastbridge_info**](BridgeApi.md#fastbridge_info) | **GET** /api/v1/fastbridge/info | fastbridge_info
|
||||
|
||||
|
||||
# **bridges**
|
||||
> RespGetBridgesByL1Addr bridges(l1_address)
|
||||
|
||||
bridges
|
||||
|
||||
Get bridges for given l1 address
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.resp_get_bridges_by_l1_addr import RespGetBridgesByL1Addr
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.BridgeApi(api_client)
|
||||
l1_address = 'l1_address_example' # str |
|
||||
|
||||
try:
|
||||
# bridges
|
||||
api_response = await api_instance.bridges(l1_address)
|
||||
print("The response of BridgeApi->bridges:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling BridgeApi->bridges: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**l1_address** | **str**| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**RespGetBridgesByL1Addr**](RespGetBridgesByL1Addr.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **bridges_is_next_bridge_fast**
|
||||
> RespGetIsNextBridgeFast bridges_is_next_bridge_fast(l1_address)
|
||||
|
||||
bridges_isNextBridgeFast
|
||||
|
||||
Get if next bridge is fast
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.resp_get_is_next_bridge_fast import RespGetIsNextBridgeFast
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.BridgeApi(api_client)
|
||||
l1_address = 'l1_address_example' # str |
|
||||
|
||||
try:
|
||||
# bridges_isNextBridgeFast
|
||||
api_response = await api_instance.bridges_is_next_bridge_fast(l1_address)
|
||||
print("The response of BridgeApi->bridges_is_next_bridge_fast:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling BridgeApi->bridges_is_next_bridge_fast: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**l1_address** | **str**| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**RespGetIsNextBridgeFast**](RespGetIsNextBridgeFast.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **fastbridge_info**
|
||||
> RespGetFastBridgeInfo fastbridge_info()
|
||||
|
||||
|
||||
@@ -10,6 +10,10 @@ Name | Type | Description | Notes
|
||||
**high** | **float** | |
|
||||
**low** | **float** | |
|
||||
**close** | **float** | |
|
||||
**open_raw** | **float** | |
|
||||
**high_raw** | **float** | |
|
||||
**low_raw** | **float** | |
|
||||
**close_raw** | **float** | |
|
||||
**volume0** | **float** | |
|
||||
**volume1** | **float** | |
|
||||
**last_trade_id** | **int** | |
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **str** | |
|
||||
**asset_id** | **int** | |
|
||||
**amount** | **str** | |
|
||||
**timestamp** | **int** | |
|
||||
**status** | **str** | |
|
||||
|
||||
@@ -12,7 +12,6 @@ Name | Type | Description | Notes
|
||||
**l1_address** | **str** | |
|
||||
**cancel_all_time** | **int** | |
|
||||
**total_order_count** | **int** | |
|
||||
**total_isolated_order_count** | **int** | |
|
||||
**pending_order_count** | **int** | |
|
||||
**available_balance** | **str** | |
|
||||
**status** | **int** | |
|
||||
@@ -23,6 +22,7 @@ Name | Type | Description | Notes
|
||||
**can_invite** | **bool** | Remove After FE uses L1 meta endpoint |
|
||||
**referral_points_percentage** | **str** | Remove After FE uses L1 meta endpoint |
|
||||
**positions** | [**List[AccountPosition]**](AccountPosition.md) | |
|
||||
**assets** | [**List[AccountAsset]**](AccountAsset.md) | |
|
||||
**total_asset_value** | **str** | |
|
||||
**cross_asset_value** | **str** | |
|
||||
**pool_info** | [**PublicPoolInfo**](PublicPoolInfo.md) | |
|
||||
|
||||
@@ -10,6 +10,10 @@ Name | Type | Description | Notes
|
||||
**high** | **float** | |
|
||||
**low** | **float** | |
|
||||
**close** | **float** | |
|
||||
**open_raw** | **float** | |
|
||||
**high_raw** | **float** | |
|
||||
**low_raw** | **float** | |
|
||||
**close_raw** | **float** | |
|
||||
**volume0** | **float** | |
|
||||
**volume1** | **float** | |
|
||||
**last_trade_id** | **int** | |
|
||||
|
||||
@@ -22,6 +22,7 @@ Name | Type | Description | Notes
|
||||
**executed_at** | **int** | |
|
||||
**sequence_index** | **int** | |
|
||||
**parent_hash** | **str** | |
|
||||
**api_key_index** | **int** | |
|
||||
**committed_at** | **int** | |
|
||||
**verified_at** | **int** | |
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# MarketConfig
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**market_margin_mode** | **int** | |
|
||||
**insurance_fund_account_index** | **int** | |
|
||||
**liquidation_mode** | **int** | |
|
||||
**force_reduce_only** | **bool** | |
|
||||
**trading_hours** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.market_config import MarketConfig
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of MarketConfig from a JSON string
|
||||
market_config_instance = MarketConfig.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(MarketConfig.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
market_config_dict = market_config_instance.to_dict()
|
||||
# create an instance of MarketConfig from a dict
|
||||
market_config_from_dict = MarketConfig.from_dict(market_config_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ Name | Type | Description | Notes
|
||||
**to_cancel_order_id_0** | **str** | |
|
||||
**block_height** | **int** | |
|
||||
**timestamp** | **int** | |
|
||||
**created_at** | **int** | |
|
||||
**updated_at** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**account_active_orders**](OrderApi.md#account_active_orders) | **GET** /api/v1/accountActiveOrders | accountActiveOrders
|
||||
[**account_inactive_orders**](OrderApi.md#account_inactive_orders) | **GET** /api/v1/accountInactiveOrders | accountInactiveOrders
|
||||
[**asset_details**](OrderApi.md#asset_details) | **GET** /api/v1/assetDetails | assetDetails
|
||||
[**exchange_stats**](OrderApi.md#exchange_stats) | **GET** /api/v1/exchangeStats | exchangeStats
|
||||
[**export**](OrderApi.md#export) | **GET** /api/v1/export | export
|
||||
[**order_book_details**](OrderApi.md#order_book_details) | **GET** /api/v1/orderBookDetails | orderBookDetails
|
||||
@@ -173,6 +174,75 @@ No authorization required
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **asset_details**
|
||||
> AssetDetails asset_details(asset_id=asset_id)
|
||||
|
||||
assetDetails
|
||||
|
||||
Get asset details
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.asset_details import AssetDetails
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.OrderApi(api_client)
|
||||
asset_id = 0 # int | (optional) (default to 0)
|
||||
|
||||
try:
|
||||
# assetDetails
|
||||
api_response = await api_instance.asset_details(asset_id=asset_id)
|
||||
print("The response of OrderApi->asset_details:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling OrderApi->asset_details: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**asset_id** | **int**| | [optional] [default to 0]
|
||||
|
||||
### Return type
|
||||
|
||||
[**AssetDetails**](AssetDetails.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **exchange_stats**
|
||||
> ExchangeStats exchange_stats()
|
||||
|
||||
@@ -316,7 +386,7 @@ No authorization required
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **order_book_details**
|
||||
> OrderBookDetails order_book_details(market_id=market_id)
|
||||
> OrderBookDetails order_book_details(market_id=market_id, filter=filter)
|
||||
|
||||
orderBookDetails
|
||||
|
||||
@@ -343,10 +413,11 @@ async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.OrderApi(api_client)
|
||||
market_id = 255 # int | (optional) (default to 255)
|
||||
filter = all # str | (optional) (default to all)
|
||||
|
||||
try:
|
||||
# orderBookDetails
|
||||
api_response = await api_instance.order_book_details(market_id=market_id)
|
||||
api_response = await api_instance.order_book_details(market_id=market_id, filter=filter)
|
||||
print("The response of OrderApi->order_book_details:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
@@ -361,6 +432,7 @@ async with lighter.ApiClient(configuration) as api_client:
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**market_id** | **int**| | [optional] [default to 255]
|
||||
**filter** | **str**| | [optional] [default to all]
|
||||
|
||||
### Return type
|
||||
|
||||
@@ -456,7 +528,7 @@ No authorization required
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **order_books**
|
||||
> OrderBooks order_books(market_id=market_id)
|
||||
> OrderBooks order_books(market_id=market_id, filter=filter)
|
||||
|
||||
orderBooks
|
||||
|
||||
@@ -483,10 +555,11 @@ async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.OrderApi(api_client)
|
||||
market_id = 255 # int | (optional) (default to 255)
|
||||
filter = all # str | (optional) (default to all)
|
||||
|
||||
try:
|
||||
# orderBooks
|
||||
api_response = await api_instance.order_books(market_id=market_id)
|
||||
api_response = await api_instance.order_books(market_id=market_id, filter=filter)
|
||||
print("The response of OrderApi->order_books:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
@@ -501,6 +574,7 @@ async with lighter.ApiClient(configuration) as api_client:
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**market_id** | **int**| | [optional] [default to 255]
|
||||
**filter** | **str**| | [optional] [default to all]
|
||||
|
||||
### Return type
|
||||
|
||||
@@ -596,7 +670,7 @@ No authorization required
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **trades**
|
||||
> Trades trades(sort_by, limit, authorization=authorization, auth=auth, market_id=market_id, account_index=account_index, order_index=order_index, sort_dir=sort_dir, cursor=cursor, var_from=var_from, ask_filter=ask_filter)
|
||||
> Trades trades(sort_by, limit, authorization=authorization, auth=auth, market_id=market_id, account_index=account_index, order_index=order_index, sort_dir=sort_dir, cursor=cursor, var_from=var_from, ask_filter=ask_filter, role=role, type=type, aggregate=aggregate)
|
||||
|
||||
trades
|
||||
|
||||
@@ -633,10 +707,13 @@ async with lighter.ApiClient(configuration) as api_client:
|
||||
cursor = 'cursor_example' # str | (optional)
|
||||
var_from = -1 # int | (optional) (default to -1)
|
||||
ask_filter = -1 # int | (optional) (default to -1)
|
||||
role = all # str | (optional) (default to all)
|
||||
type = all # str | (optional) (default to all)
|
||||
aggregate = False # bool | (optional) (default to False)
|
||||
|
||||
try:
|
||||
# trades
|
||||
api_response = await api_instance.trades(sort_by, limit, authorization=authorization, auth=auth, market_id=market_id, account_index=account_index, order_index=order_index, sort_dir=sort_dir, cursor=cursor, var_from=var_from, ask_filter=ask_filter)
|
||||
api_response = await api_instance.trades(sort_by, limit, authorization=authorization, auth=auth, market_id=market_id, account_index=account_index, order_index=order_index, sort_dir=sort_dir, cursor=cursor, var_from=var_from, ask_filter=ask_filter, role=role, type=type, aggregate=aggregate)
|
||||
print("The response of OrderApi->trades:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
@@ -661,6 +738,9 @@ Name | Type | Description | Notes
|
||||
**cursor** | **str**| | [optional]
|
||||
**var_from** | **int**| | [optional] [default to -1]
|
||||
**ask_filter** | **int**| | [optional] [default to -1]
|
||||
**role** | **str**| | [optional] [default to all]
|
||||
**type** | **str**| | [optional] [default to all]
|
||||
**aggregate** | **bool**| | [optional] [default to False]
|
||||
|
||||
### Return type
|
||||
|
||||
|
||||
@@ -7,12 +7,16 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**market_id** | **int** | |
|
||||
**market_type** | **str** | |
|
||||
**base_asset_id** | **int** | |
|
||||
**quote_asset_id** | **int** | |
|
||||
**status** | **str** | |
|
||||
**taker_fee** | **str** | |
|
||||
**maker_fee** | **str** | |
|
||||
**liquidation_fee** | **str** | |
|
||||
**min_base_amount** | **str** | |
|
||||
**min_quote_amount** | **str** | |
|
||||
**order_quote_limit** | **str** | |
|
||||
**supported_size_decimals** | **int** | |
|
||||
**supported_price_decimals** | **int** | |
|
||||
**supported_quote_decimals** | **int** | |
|
||||
|
||||
@@ -10,6 +10,7 @@ Name | Type | Description | Notes
|
||||
**asks** | [**List[PriceLevel]**](PriceLevel.md) | |
|
||||
**bids** | [**List[PriceLevel]**](PriceLevel.md) | |
|
||||
**offset** | **int** | |
|
||||
**nonce** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**order_book_details** | [**List[OrderBookDetail]**](OrderBookDetail.md) | |
|
||||
**order_book_details** | [**List[PerpsOrderBookDetail]**](PerpsOrderBookDetail.md) | |
|
||||
**spot_order_book_details** | [**List[SpotOrderBookDetail]**](SpotOrderBookDetail.md) | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
+12
-8
@@ -1,14 +1,18 @@
|
||||
# MarketInfo
|
||||
# PerpsMarketStats
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**market_id** | **int** | |
|
||||
**index_price** | **str** | |
|
||||
**mark_price** | **str** | |
|
||||
**open_interest** | **str** | |
|
||||
**open_interest_limit** | **str** | |
|
||||
**funding_clamp_small** | **str** | |
|
||||
**funding_clamp_big** | **str** | |
|
||||
**last_trade_price** | **str** | |
|
||||
**current_funding_rate** | **str** | |
|
||||
**funding_rate** | **str** | |
|
||||
@@ -22,19 +26,19 @@ Name | Type | Description | Notes
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.market_info import MarketInfo
|
||||
from lighter.models.perps_market_stats import PerpsMarketStats
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of MarketInfo from a JSON string
|
||||
market_info_instance = MarketInfo.from_json(json)
|
||||
# create an instance of PerpsMarketStats from a JSON string
|
||||
perps_market_stats_instance = PerpsMarketStats.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(MarketInfo.to_json())
|
||||
print(PerpsMarketStats.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
market_info_dict = market_info_instance.to_dict()
|
||||
# create an instance of MarketInfo from a dict
|
||||
market_info_from_dict = MarketInfo.from_dict(market_info_dict)
|
||||
perps_market_stats_dict = perps_market_stats_instance.to_dict()
|
||||
# create an instance of PerpsMarketStats from a dict
|
||||
perps_market_stats_from_dict = PerpsMarketStats.from_dict(perps_market_stats_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
+13
-8
@@ -1,4 +1,4 @@
|
||||
# OrderBookDetail
|
||||
# PerpsOrderBookDetail
|
||||
|
||||
|
||||
## Properties
|
||||
@@ -7,12 +7,16 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**market_id** | **int** | |
|
||||
**market_type** | **str** | |
|
||||
**base_asset_id** | **int** | |
|
||||
**quote_asset_id** | **int** | |
|
||||
**status** | **str** | |
|
||||
**taker_fee** | **str** | |
|
||||
**maker_fee** | **str** | |
|
||||
**liquidation_fee** | **str** | |
|
||||
**min_base_amount** | **str** | |
|
||||
**min_quote_amount** | **str** | |
|
||||
**order_quote_limit** | **str** | |
|
||||
**supported_size_decimals** | **int** | |
|
||||
**supported_price_decimals** | **int** | |
|
||||
**supported_quote_decimals** | **int** | |
|
||||
@@ -32,23 +36,24 @@ Name | Type | Description | Notes
|
||||
**daily_price_change** | **float** | |
|
||||
**open_interest** | **float** | |
|
||||
**daily_chart** | **Dict[str, float]** | |
|
||||
**market_config** | [**MarketConfig**](MarketConfig.md) | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.order_book_detail import OrderBookDetail
|
||||
from lighter.models.perps_order_book_detail import PerpsOrderBookDetail
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of OrderBookDetail from a JSON string
|
||||
order_book_detail_instance = OrderBookDetail.from_json(json)
|
||||
# create an instance of PerpsOrderBookDetail from a JSON string
|
||||
perps_order_book_detail_instance = PerpsOrderBookDetail.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(OrderBookDetail.to_json())
|
||||
print(PerpsOrderBookDetail.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
order_book_detail_dict = order_book_detail_instance.to_dict()
|
||||
# create an instance of OrderBookDetail from a dict
|
||||
order_book_detail_from_dict = OrderBookDetail.from_dict(order_book_detail_dict)
|
||||
perps_order_book_detail_dict = perps_order_book_detail_instance.to_dict()
|
||||
# create an instance of PerpsOrderBookDetail from a dict
|
||||
perps_order_book_detail_from_dict = PerpsOrderBookDetail.from_dict(perps_order_book_detail_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
@@ -7,8 +7,11 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**timestamp** | **int** | |
|
||||
**trade_pnl** | **float** | |
|
||||
**trade_spot_pnl** | **float** | |
|
||||
**inflow** | **float** | |
|
||||
**outflow** | **float** | |
|
||||
**spot_outflow** | **float** | |
|
||||
**spot_inflow** | **float** | |
|
||||
**pool_pnl** | **float** | |
|
||||
**pool_inflow** | **float** | |
|
||||
**pool_outflow** | **float** | |
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# PublicPool
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**account_type** | **int** | |
|
||||
**index** | **int** | |
|
||||
**l1_address** | **str** | |
|
||||
**cancel_all_time** | **int** | |
|
||||
**total_order_count** | **int** | |
|
||||
**total_isolated_order_count** | **int** | |
|
||||
**pending_order_count** | **int** | |
|
||||
**available_balance** | **str** | |
|
||||
**status** | **int** | |
|
||||
**collateral** | **str** | |
|
||||
**account_index** | **int** | |
|
||||
**name** | **str** | |
|
||||
**description** | **str** | |
|
||||
**can_invite** | **bool** | Remove After FE uses L1 meta endpoint |
|
||||
**referral_points_percentage** | **str** | Remove After FE uses L1 meta endpoint |
|
||||
**total_asset_value** | **str** | |
|
||||
**cross_asset_value** | **str** | |
|
||||
**pool_info** | [**PublicPoolInfo**](PublicPoolInfo.md) | |
|
||||
**account_share** | [**PublicPoolShare**](PublicPoolShare.md) | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.public_pool import PublicPool
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of PublicPool from a JSON string
|
||||
public_pool_instance = PublicPool.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(PublicPool.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
public_pool_dict = public_pool_instance.to_dict()
|
||||
# create an instance of PublicPool from a dict
|
||||
public_pool_from_dict = PublicPool.from_dict(public_pool_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Name | Type | Description | Notes
|
||||
**total_shares** | **int** | |
|
||||
**operator_shares** | **int** | |
|
||||
**annual_percentage_yield** | **float** | |
|
||||
**sharpe_ratio** | **float** | |
|
||||
**daily_returns** | [**List[DailyReturn]**](DailyReturn.md) | |
|
||||
**share_prices** | [**List[SharePrice]**](SharePrice.md) | |
|
||||
|
||||
|
||||
@@ -8,10 +8,13 @@ Name | Type | Description | Notes
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**account_index** | **int** | |
|
||||
**created_at** | **int** | |
|
||||
**master_account_index** | **int** | |
|
||||
**account_type** | **int** | |
|
||||
**name** | **str** | |
|
||||
**l1_address** | **str** | |
|
||||
**annual_percentage_yield** | **float** | |
|
||||
**sharpe_ratio** | **float** | |
|
||||
**status** | **int** | |
|
||||
**operator_fee** | **str** | |
|
||||
**total_asset_value** | **str** | |
|
||||
|
||||
@@ -4,9 +4,86 @@ All URIs are relative to *https://mainnet.zklighter.elliot.ai*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**referral_kickback_update**](ReferralApi.md#referral_kickback_update) | **POST** /api/v1/referral/kickback/update | referral_kickback_update
|
||||
[**referral_points**](ReferralApi.md#referral_points) | **GET** /api/v1/referral/points | referral_points
|
||||
[**referral_update**](ReferralApi.md#referral_update) | **POST** /api/v1/referral/update | referral_update
|
||||
|
||||
|
||||
# **referral_kickback_update**
|
||||
> RespUpdateKickback referral_kickback_update(account_index, kickback_percentage, authorization=authorization, auth=auth)
|
||||
|
||||
referral_kickback_update
|
||||
|
||||
Update kickback percentage for referral rewards
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.resp_update_kickback import RespUpdateKickback
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.ReferralApi(api_client)
|
||||
account_index = 56 # int |
|
||||
kickback_percentage = 3.4 # float |
|
||||
authorization = 'authorization_example' # str | make required after integ is done (optional)
|
||||
auth = 'auth_example' # str | made optional to support header auth clients (optional)
|
||||
|
||||
try:
|
||||
# referral_kickback_update
|
||||
api_response = await api_instance.referral_kickback_update(account_index, kickback_percentage, authorization=authorization, auth=auth)
|
||||
print("The response of ReferralApi->referral_kickback_update:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling ReferralApi->referral_kickback_update: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**account_index** | **int**| |
|
||||
**kickback_percentage** | **float**| |
|
||||
**authorization** | **str**| make required after integ is done | [optional]
|
||||
**auth** | **str**| made optional to support header auth clients | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**RespUpdateKickback**](RespUpdateKickback.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: multipart/form-data
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **referral_points**
|
||||
> ReferralPoints referral_points(account_index, authorization=authorization, auth=auth)
|
||||
|
||||
@@ -80,3 +157,78 @@ No authorization required
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **referral_update**
|
||||
> RespUpdateReferralCode referral_update(account_index, new_referral_code, authorization=authorization, auth=auth)
|
||||
|
||||
referral_update
|
||||
|
||||
Update referral code (allowed once per account)
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import lighter
|
||||
from lighter.models.resp_update_referral_code import RespUpdateReferralCode
|
||||
from lighter.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to https://mainnet.zklighter.elliot.ai
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = lighter.Configuration(
|
||||
host = "https://mainnet.zklighter.elliot.ai"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with lighter.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = lighter.ReferralApi(api_client)
|
||||
account_index = 56 # int |
|
||||
new_referral_code = 'new_referral_code_example' # str |
|
||||
authorization = 'authorization_example' # str | make required after integ is done (optional)
|
||||
auth = 'auth_example' # str | made optional to support header auth clients (optional)
|
||||
|
||||
try:
|
||||
# referral_update
|
||||
api_response = await api_instance.referral_update(account_index, new_referral_code, authorization=authorization, auth=auth)
|
||||
print("The response of ReferralApi->referral_update:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling ReferralApi->referral_update: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**account_index** | **int**| |
|
||||
**new_referral_code** | **str**| |
|
||||
**authorization** | **str**| make required after integ is done | [optional]
|
||||
**auth** | **str**| made optional to support header auth clients | [optional]
|
||||
|
||||
### Return type
|
||||
|
||||
[**RespUpdateReferralCode**](RespUpdateReferralCode.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: multipart/form-data
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | A successful response. | - |
|
||||
**400** | Bad request | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**l1_address** | **str** | |
|
||||
**total_points** | **int** | |
|
||||
**week_points** | **int** | |
|
||||
**total_reward_points** | **int** | |
|
||||
**week_reward_points** | **int** | |
|
||||
**total_points** | **float** | |
|
||||
**week_points** | **float** | |
|
||||
**total_reward_points** | **float** | |
|
||||
**week_reward_points** | **float** | |
|
||||
**reward_point_multiplier** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**referrals** | [**List[ReferralPointEntry]**](ReferralPointEntry.md) | |
|
||||
**user_total_points** | **int** | |
|
||||
**user_last_week_points** | **int** | |
|
||||
**user_total_referral_reward_points** | **int** | |
|
||||
**user_last_week_referral_reward_points** | **int** | |
|
||||
**user_total_points** | **float** | |
|
||||
**user_last_week_points** | **float** | |
|
||||
**user_total_referral_reward_points** | **float** | |
|
||||
**user_last_week_referral_reward_points** | **float** | |
|
||||
**reward_point_multiplier** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# ReqGetAssetDetails
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**asset_id** | **int** | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.req_get_asset_details import ReqGetAssetDetails
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of ReqGetAssetDetails from a JSON string
|
||||
req_get_asset_details_instance = ReqGetAssetDetails.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(ReqGetAssetDetails.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
req_get_asset_details_dict = req_get_asset_details_instance.to_dict()
|
||||
# create an instance of ReqGetAssetDetails from a dict
|
||||
req_get_asset_details_from_dict = ReqGetAssetDetails.from_dict(req_get_asset_details_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# ReqGetBridgesByL1Addr
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**l1_address** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.req_get_bridges_by_l1_addr import ReqGetBridgesByL1Addr
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of ReqGetBridgesByL1Addr from a JSON string
|
||||
req_get_bridges_by_l1_addr_instance = ReqGetBridgesByL1Addr.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(ReqGetBridgesByL1Addr.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
req_get_bridges_by_l1_addr_dict = req_get_bridges_by_l1_addr_instance.to_dict()
|
||||
# create an instance of ReqGetBridgesByL1Addr from a dict
|
||||
req_get_bridges_by_l1_addr_from_dict = ReqGetBridgesByL1Addr.from_dict(req_get_bridges_by_l1_addr_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**market_id** | **int** | | [optional]
|
||||
**filter** | **str** | | [optional] [default to 'all']
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**market_id** | **int** | | [optional]
|
||||
**filter** | **str** | | [optional] [default to 'all']
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# ReqGetPublicPools
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**auth** | **str** | | [optional]
|
||||
**filter** | **str** | | [optional]
|
||||
**index** | **int** | |
|
||||
**limit** | **int** | |
|
||||
**account_index** | **int** | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.req_get_public_pools import ReqGetPublicPools
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of ReqGetPublicPools from a JSON string
|
||||
req_get_public_pools_instance = ReqGetPublicPools.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(ReqGetPublicPools.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
req_get_public_pools_dict = req_get_public_pools_instance.to_dict()
|
||||
# create an instance of ReqGetPublicPools from a dict
|
||||
req_get_public_pools_from_dict = ReqGetPublicPools.from_dict(req_get_public_pools_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -14,7 +14,10 @@ Name | Type | Description | Notes
|
||||
**cursor** | **str** | | [optional]
|
||||
**var_from** | **int** | | [optional] [default to -1]
|
||||
**ask_filter** | **int** | | [optional]
|
||||
**role** | **str** | | [optional] [default to 'all']
|
||||
**type** | **str** | | [optional] [default to 'all']
|
||||
**limit** | **int** | |
|
||||
**aggregate** | **bool** | | [optional] [default to False]
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# RespGetBridgesByL1Addr
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**bridges** | [**List[Bridge]**](Bridge.md) | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.resp_get_bridges_by_l1_addr import RespGetBridgesByL1Addr
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of RespGetBridgesByL1Addr from a JSON string
|
||||
resp_get_bridges_by_l1_addr_instance = RespGetBridgesByL1Addr.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(RespGetBridgesByL1Addr.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
resp_get_bridges_by_l1_addr_dict = resp_get_bridges_by_l1_addr_instance.to_dict()
|
||||
# create an instance of RespGetBridgesByL1Addr from a dict
|
||||
resp_get_bridges_by_l1_addr_from_dict = RespGetBridgesByL1Addr.from_dict(resp_get_bridges_by_l1_addr_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# RespGetIsNextBridgeFast
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**is_next_bridge_fast** | **bool** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.resp_get_is_next_bridge_fast import RespGetIsNextBridgeFast
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of RespGetIsNextBridgeFast from a JSON string
|
||||
resp_get_is_next_bridge_fast_instance = RespGetIsNextBridgeFast.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(RespGetIsNextBridgeFast.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
resp_get_is_next_bridge_fast_dict = resp_get_is_next_bridge_fast_instance.to_dict()
|
||||
# create an instance of RespGetIsNextBridgeFast from a dict
|
||||
resp_get_is_next_bridge_fast_from_dict = RespGetIsNextBridgeFast.from_dict(resp_get_is_next_bridge_fast_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ Name | Type | Description | Notes
|
||||
**message** | **str** | | [optional]
|
||||
**tx_hash** | **str** | |
|
||||
**predicted_execution_time_ms** | **int** | |
|
||||
**volume_quota_remaining** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ Name | Type | Description | Notes
|
||||
**message** | **str** | | [optional]
|
||||
**tx_hash** | **List[str]** | |
|
||||
**predicted_execution_time_ms** | **int** | |
|
||||
**volume_quota_remaining** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# RespUpdateKickback
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**success** | **bool** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.resp_update_kickback import RespUpdateKickback
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of RespUpdateKickback from a JSON string
|
||||
resp_update_kickback_instance = RespUpdateKickback.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(RespUpdateKickback.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
resp_update_kickback_dict = resp_update_kickback_instance.to_dict()
|
||||
# create an instance of RespUpdateKickback from a dict
|
||||
resp_update_kickback_from_dict = RespUpdateKickback.from_dict(resp_update_kickback_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# RespUpdateReferralCode
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**code** | **int** | |
|
||||
**message** | **str** | | [optional]
|
||||
**success** | **bool** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.resp_update_referral_code import RespUpdateReferralCode
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of RespUpdateReferralCode from a JSON string
|
||||
resp_update_referral_code_instance = RespUpdateReferralCode.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(RespUpdateReferralCode.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
resp_update_referral_code_dict = resp_update_referral_code_instance.to_dict()
|
||||
# create an instance of RespUpdateReferralCode from a dict
|
||||
resp_update_referral_code_from_dict = RespUpdateReferralCode.from_dict(resp_update_referral_code_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# SpotMarketStats
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**market_id** | **int** | |
|
||||
**index_price** | **str** | |
|
||||
**mid_price** | **str** | |
|
||||
**last_trade_price** | **str** | |
|
||||
**daily_base_token_volume** | **float** | |
|
||||
**daily_quote_token_volume** | **float** | |
|
||||
**daily_price_low** | **float** | |
|
||||
**daily_price_high** | **float** | |
|
||||
**daily_price_change** | **float** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.spot_market_stats import SpotMarketStats
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of SpotMarketStats from a JSON string
|
||||
spot_market_stats_instance = SpotMarketStats.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(SpotMarketStats.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
spot_market_stats_dict = spot_market_stats_instance.to_dict()
|
||||
# create an instance of SpotMarketStats from a dict
|
||||
spot_market_stats_from_dict = SpotMarketStats.from_dict(spot_market_stats_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# SpotOrderBookDetail
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**symbol** | **str** | |
|
||||
**market_id** | **int** | |
|
||||
**market_type** | **str** | |
|
||||
**base_asset_id** | **int** | |
|
||||
**quote_asset_id** | **int** | |
|
||||
**status** | **str** | |
|
||||
**taker_fee** | **str** | |
|
||||
**maker_fee** | **str** | |
|
||||
**liquidation_fee** | **str** | |
|
||||
**min_base_amount** | **str** | |
|
||||
**min_quote_amount** | **str** | |
|
||||
**order_quote_limit** | **str** | |
|
||||
**supported_size_decimals** | **int** | |
|
||||
**supported_price_decimals** | **int** | |
|
||||
**supported_quote_decimals** | **int** | |
|
||||
**size_decimals** | **int** | |
|
||||
**price_decimals** | **int** | |
|
||||
**last_trade_price** | **float** | |
|
||||
**daily_trades_count** | **int** | |
|
||||
**daily_base_token_volume** | **float** | |
|
||||
**daily_quote_token_volume** | **float** | |
|
||||
**daily_price_low** | **float** | |
|
||||
**daily_price_high** | **float** | |
|
||||
**daily_price_change** | **float** | |
|
||||
**daily_chart** | **Dict[str, float]** | |
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from lighter.models.spot_order_book_detail import SpotOrderBookDetail
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of SpotOrderBookDetail from a JSON string
|
||||
spot_order_book_detail_instance = SpotOrderBookDetail.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(SpotOrderBookDetail.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
spot_order_book_detail_dict = spot_order_book_detail_instance.to_dict()
|
||||
# create an instance of SpotOrderBookDetail from a dict
|
||||
spot_order_book_detail_from_dict = SpotOrderBookDetail.from_dict(spot_order_book_detail_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ Name | Type | Description | Notes
|
||||
**usd_amount** | **str** | |
|
||||
**ask_id** | **int** | |
|
||||
**bid_id** | **int** | |
|
||||
**ask_client_id** | **int** | |
|
||||
**bid_client_id** | **int** | |
|
||||
**ask_account_id** | **int** | |
|
||||
**bid_account_id** | **int** | |
|
||||
**is_maker_ask** | **bool** | |
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **str** | |
|
||||
**asset_id** | **int** | |
|
||||
**amount** | **str** | |
|
||||
**timestamp** | **int** | |
|
||||
**type** | **str** | |
|
||||
@@ -13,6 +14,8 @@ Name | Type | Description | Notes
|
||||
**to_l1_address** | **str** | |
|
||||
**from_account_index** | **int** | |
|
||||
**to_account_index** | **int** | |
|
||||
**from_route** | **str** | |
|
||||
**to_route** | **str** | |
|
||||
**tx_hash** | **str** | |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -20,6 +20,7 @@ Name | Type | Description | Notes
|
||||
**executed_at** | **int** | |
|
||||
**sequence_index** | **int** | |
|
||||
**parent_hash** | **str** | |
|
||||
**api_key_index** | **int** | |
|
||||
|
||||
## Example
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **str** | |
|
||||
**asset_id** | **int** | |
|
||||
**amount** | **str** | |
|
||||
**timestamp** | **int** | |
|
||||
**status** | **str** | |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
api_key_config.json
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user