odin-connect 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/readme.md CHANGED
@@ -1,205 +1,468 @@
1
- # OdinConnect
2
-
3
- ## Installation
4
-
5
- `npm i odin-connect`
6
-
7
- ## Demo
8
-
9
- This repository includes a demo. See `/demo` folder
10
-
11
- To run it, simply do `npm run demo`
12
-
13
- ## Examples
14
-
15
- ### Initializing new instance
16
-
17
- Instantiate the OdinConnect class with some information about your application
18
-
19
- - `name` - Name of your app
20
- - `env` - Odin Environment. Accepted values: `prod`, `dev`, `local`. Default: `prod`.
21
-
22
- ```typescript
23
- const odinConnect = new OdinConnect({ name: "Demo App", env: "prod" });
24
- ```
25
-
26
- ### Request for user connection
27
-
28
- ```typescript
29
- const user = await odinConnect.connect({
30
- // these will be used when window.open is called
31
- open: {
32
- target: "_blank",
33
- settings: "height=800,width=400",
34
- },
35
- // flag to determine if api key is being requested
36
- requires_api: true,
37
- // flag ot determine if DelegationChain is being requested
38
- requires_delegation: false,
39
- });
40
- ```
41
-
42
- ### Request for Identity
43
-
44
- ```typescript
45
- const user = await odinConnect.connect({
46
- // set to true
47
- requires_delegation: true,
48
- // canister ids
49
- targets: ["aaaa-aa"],
50
- });
51
- const identity = user.getIdentity();
52
- ```
53
-
54
- ### Request for various user data from API
55
-
56
- ```typescript
57
- // get balances
58
- const balances = await user.getBalances({ page: 1, limit: 10 });
59
- // get tokens
60
- const tokens = await user.getTokens({ page: 1, limit: 10 });
61
- // get created tokens
62
- const createdTokens = await user.getCreatedTokens({ page: 1, limit: 10 });
63
- // get liquidity
64
- const liquidity = await user.getLiquidity({ page: 1, limit: 10 });
65
- // get activity
66
- const activity = await user.getActivity({ page: 1, limit: 10 });
67
- // get achievements
68
- const achievements = await user.getAchievements({ page: 1, limit: 10 });
69
- // get transactions
70
- const transactions = await user.getTransactions({ page: 1, limit: 10 });
71
- ```
72
-
73
- ### Request for token transfer
74
-
75
- ```typescript
76
- await odinConnect.transfer({
77
- principal: "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
78
- destination:
79
- "vv5jb-7sm7u-vn3nq-6nflf-dghis-fd7ji-cx764-xunni-zosog-eqvpw-oae",
80
- token: "2jjj",
81
- amount: 20_000_000n, // 20K sats in millisats
82
- });
83
- ```
84
-
85
- ### Request for buy authorization
86
-
87
- ```typescript
88
- const user = await odinConnect.connect();
89
- await user.buy({
90
- btcAmount: 10_000_000n,
91
- token: "2jjj",
92
- });
93
- ```
94
-
95
- ### Request for sell authorization
96
-
97
- ```typescript
98
- const user = await odinConnect.connect();
99
- await user.sell({
100
- tokenAmount: 20_000_000n,
101
- token: "2jjj",
102
- });
103
- ```
104
-
105
- ### Request for add liquidity authorization
106
-
107
- ```typescript
108
- const user = await odinConnect.connect();
109
- await user.addLiquidity({
110
- btcAmount: 20_000_000n,
111
- token: "2jj",
112
- });
113
- ```
114
-
115
- ### Request for remove liquidity authorization
116
-
117
- ```typescript
118
- const user = await odinConnect.connect();
119
- await user.removeLiquidity({
120
- btcAmount: 20_000_000n,
121
- token: "2jj",
122
- });
123
- ```
124
-
125
- ### Request for creating new token
126
-
127
- Note: `require_api` must be set set to true
128
-
129
- ```typescript
130
- const user = await odinConnect.connect({ requires_api: true });
131
- await user.createToken({
132
- image: file, // instance of a file
133
- principal: "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
134
- name: "Test Token",
135
- ticker: "TEST",
136
- description: "Test Description", // optional
137
- website: "http://test-website.com", // optional
138
- telegram: "", // optional
139
- twitter: "", // optional
140
- buy: 20_000_000n, // 20K sats
141
- discount: "",
142
- });
143
- ```
144
-
145
- ### API Methods
146
-
147
- ```typescript
148
- // Get tokens
149
- const tokens = await odin.api.getTokens(
150
- {
151
- page: 1,
152
- limit: 10,
153
- },
154
- {
155
- field: "marketcap",
156
- direction: "desc",
157
- },
158
- {
159
- marketcap_min: 100000000n, // in millisats
160
- }
161
- );
162
-
163
- // Get activities
164
- const activity = await odinConnect.api.getUserActivity({
165
- pagination: { page: 1, limit: 10 },
166
- });
167
-
168
- // Get token by id
169
- const token = await odinConnect.api.getToken("2jjj");
170
-
171
- // Get user data by id
172
- const user = await odinConnect.api.getUser(
173
- "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe"
174
- );
175
-
176
- // Get user balance by user id
177
- const balances = await odinConnect.api.getBalances(
178
- "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
179
- { page: 1, limit: 20 }
180
- );
181
-
182
- // Get activities by user id
183
- const activity = await odinConnect.api.getUserActivity(
184
- "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
185
- { page: 1, limit: 10 }
186
- );
187
-
188
- // Get transactions by user id
189
- const transactions = await odinConnecct.api.getUserTransactions(
190
- "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
191
- {
192
- page: 1,
193
- limit: 10,
194
- }
195
- );
196
-
197
- // Get user stats
198
- const stats = await odin.api.getUserStats(
199
- "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe"
200
- );
201
- ```
202
-
203
- ## General Notes
204
-
205
- - BTC amounts are in millisatoshis
1
+ # OdinConnect
2
+
3
+ A TypeScript SDK for integrating with the [Odin](https://odin.fun) decentralized token platform on the Internet Computer (ICP). OdinConnect handles user authentication, token trading, liquidity management, and API interactions through a simple, promise-based interface.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Installation](#installation)
8
+ - [Architecture](#architecture)
9
+ - [How It Works](#how-it-works)
10
+ - [Authentication Flow](#authentication-flow)
11
+ - [Trading Action Flow](#trading-action-flow)
12
+ - [API Request Flow](#api-request-flow)
13
+ - [Getting Started](#getting-started)
14
+ - [Authentication](#authentication)
15
+ - [Connected User Operations](#connected-user-operations)
16
+ - [Fetching User Data](#fetching-user-data)
17
+ - [Trading](#trading)
18
+ - [Liquidity](#liquidity)
19
+ - [Token Creation](#token-creation)
20
+ - [Public API](#public-api)
21
+ - [Tokens](#tokens)
22
+ - [Users](#users)
23
+ - [Utilities](#utilities)
24
+ - [Configuration](#configuration)
25
+ - [Types](#types)
26
+ - [Demo](#demo)
27
+ - [General Notes](#general-notes)
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ npm i odin-connect
33
+ ```
34
+
35
+ ## Architecture
36
+
37
+ OdinConnect is built with a layered architecture. Your application interacts with the `OdinConnect` class, which delegates to specialized internal services:
38
+
39
+ ```mermaid
40
+ graph TB
41
+ App["Your Application"]
42
+
43
+ subgraph OdinConnect SDK
44
+ OC["OdinConnect<br/>(Entry Point)"]
45
+ CU["ConnectedUser<br/>(Authenticated Session)"]
46
+ API["OdinApiClient<br/>(REST API)"]
47
+ CAN["OdinCanisterClient<br/>(Blockchain Actions)"]
48
+ WIN["WindowClient<br/>(Popup Management)"]
49
+ HTTP["HttpClient<br/>(Axios + BigInt)"]
50
+ end
51
+
52
+ OdinAPI["Odin API Server"]
53
+ OdinFE["Odin Frontend<br/>(Popup Window)"]
54
+
55
+ App --> OC
56
+ OC --> CU
57
+ OC --> API
58
+ CU --> API
59
+ CU --> CAN
60
+ CAN --> WIN
61
+ WIN --> OdinFE
62
+ API --> HTTP
63
+ HTTP --> OdinAPI
64
+ ```
65
+
66
+ | Component | Role |
67
+ |-----------|------|
68
+ | **OdinConnect** | Main entry point. Initializes the SDK with your app info and environment. |
69
+ | **ConnectedUser** | Returned after authentication. Provides user-scoped data fetching and trading actions. |
70
+ | **OdinApiClient** | Handles all REST API calls to `api.odin.fun`. Available both on the instance (`odinConnect.api`) and on the connected user. |
71
+ | **OdinCanisterClient** | Manages popup-based authorization for blockchain actions (buy, sell, transfer, etc.). |
72
+ | **WindowClient** | Wraps `window.open()` for cross-origin popup communication via `postMessage`. |
73
+ | **HttpClient** | Axios wrapper with automatic BigInt deserialization for large number fields. |
74
+
75
+ ## How It Works
76
+
77
+ ### Authentication Flow
78
+
79
+ When your app calls `connect()`, a popup opens to the Odin frontend where the user signs in. On success, user credentials are passed back to your app via `postMessage`.
80
+
81
+ ```mermaid
82
+ sequenceDiagram
83
+ participant App as Your App
84
+ participant SDK as OdinConnect SDK
85
+ participant Popup as Odin Frontend (Popup)
86
+ participant User as User
87
+
88
+ App->>SDK: odinConnect.connect(options)
89
+ SDK->>Popup: window.open(odin.fun/authorize/connect?...)
90
+ Popup->>User: Show sign-in UI
91
+ User->>Popup: Authenticates
92
+ Popup->>SDK: postMessage({ principal, jwt, delegationChain? })
93
+ SDK->>SDK: Create ConnectedUser instance
94
+ SDK->>App: Returns ConnectedUser
95
+ ```
96
+
97
+ **What gets returned depends on your options:**
98
+
99
+ | Option | What You Get |
100
+ |--------|-------------|
101
+ | `requires_api: true` | A JWT token for authenticated API calls (image uploads, etc.) |
102
+ | `requires_delegation: true` | A `DelegationIdentity` for direct canister calls |
103
+ | Neither | Basic connection with user's principal |
104
+
105
+ ### Trading Action Flow
106
+
107
+ Trading operations (buy, sell, transfer, swap, liquidity) each open a popup for the user to authorize the transaction:
108
+
109
+ ```mermaid
110
+ sequenceDiagram
111
+ participant App as Your App
112
+ participant SDK as ConnectedUser
113
+ participant Popup as Odin Frontend (Popup)
114
+ participant User as User
115
+ participant Chain as ICP Blockchain
116
+
117
+ App->>SDK: user.buy({ token, btcAmount })
118
+ SDK->>Popup: window.open(odin.fun/authorize/buy?...)
119
+ Popup->>User: Show transaction details
120
+ User->>Popup: Approves transaction
121
+ Popup->>Chain: Execute on-chain transaction
122
+ Chain->>Popup: Transaction result
123
+ Popup->>SDK: postMessage("purchased")
124
+ SDK->>App: Returns true
125
+ ```
126
+
127
+ If the user rejects the transaction, the popup sends `"rejected"` and the promise resolves to `false`.
128
+
129
+ ### API Request Flow
130
+
131
+ API calls go through the HttpClient, which automatically handles BigInt deserialization for fields like `marketcap`, `volume`, and `balance`:
132
+
133
+ ```mermaid
134
+ flowchart LR
135
+ A["Your App"] --> B["OdinApiClient"]
136
+ B --> C["HttpClient<br/>(Axios + BigInt parser)"]
137
+ C --> D["api.odin.fun/v1"]
138
+ D --> C
139
+ C --> B
140
+ B --> A
141
+ ```
142
+
143
+ ## Getting Started
144
+
145
+ ```typescript
146
+ import { OdinConnect } from "odin-connect";
147
+
148
+ // 1. Initialize
149
+ const odinConnect = new OdinConnect({
150
+ name: "My App",
151
+ env: "prod",
152
+ });
153
+
154
+ // 2. Authenticate a user
155
+ const user = await odinConnect.connect({
156
+ requires_api: true,
157
+ });
158
+
159
+ // 3. Fetch data
160
+ const balances = await user.getBalances({ page: 1, limit: 10 });
161
+
162
+ // 4. Perform actions
163
+ await user.buy({ token: "2jjj", btcAmount: 10_000_000n });
164
+ ```
165
+
166
+ ## Authentication
167
+
168
+ ### Initializing a new instance
169
+
170
+ ```typescript
171
+ const odinConnect = new OdinConnect({
172
+ name: "Demo App", // Your app name (shown in auth popup)
173
+ env: "prod", // "prod" | "dev" | "local"
174
+ });
175
+ ```
176
+
177
+ ### Connecting a user
178
+
179
+ ```typescript
180
+ const user = await odinConnect.connect({
181
+ // window.open() settings for the auth popup
182
+ open: {
183
+ target: "_blank",
184
+ settings: "height=800,width=400",
185
+ },
186
+ // Request a JWT for authenticated API calls
187
+ requires_api: true,
188
+ // Request a DelegationChain for direct canister interaction
189
+ requires_delegation: false,
190
+ });
191
+ ```
192
+
193
+ ### Getting a Delegation Identity
194
+
195
+ If you need to make direct calls to ICP canisters, request a delegation:
196
+
197
+ ```typescript
198
+ const user = await odinConnect.connect({
199
+ requires_delegation: true,
200
+ targets: ["aaaa-aa"], // Canister IDs the delegation is scoped to
201
+ });
202
+
203
+ const identity = user.getIdentity();
204
+ // Use identity with @dfinity/agent
205
+ ```
206
+
207
+ ## Connected User Operations
208
+
209
+ After calling `connect()`, you receive a `ConnectedUser` with the following capabilities:
210
+
211
+ ### Fetching User Data
212
+
213
+ All data methods accept a `{ page, limit }` pagination object:
214
+
215
+ ```typescript
216
+ const profile = await user.getUser();
217
+ const balances = await user.getBalances({ page: 1, limit: 10 });
218
+ const tokens = await user.getTokens({ page: 1, limit: 10 });
219
+ const createdTokens = await user.getCreatedTokens({ page: 1, limit: 10 });
220
+ const liquidity = await user.getLiquidity({ page: 1, limit: 10 });
221
+ const activity = await user.getActivity({ page: 1, limit: 10 });
222
+ const achievements = await user.getAchievements({ page: 1, limit: 10 });
223
+ const transactions = await user.getTransactions({ page: 1, limit: 10 });
224
+ const stats = await user.getStats();
225
+ ```
226
+
227
+ ### Trading
228
+
229
+ #### Buy tokens
230
+
231
+ ```typescript
232
+ await user.buy({
233
+ btcAmount: 10_000_000n, // Amount in millisatoshis
234
+ token: "2jjj",
235
+ });
236
+ ```
237
+
238
+ #### Sell tokens
239
+
240
+ ```typescript
241
+ await user.sell({
242
+ tokenAmount: 20_000_000n,
243
+ token: "2jjj",
244
+ });
245
+ ```
246
+
247
+ #### Transfer tokens
248
+
249
+ ```typescript
250
+ await odinConnect.transfer({
251
+ principal: "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
252
+ destination: "vv5jb-7sm7u-vn3nq-6nflf-dghis-fd7ji-cx764-xunni-zosog-eqvpw-oae",
253
+ token: "2jjj",
254
+ amount: 20_000_000n,
255
+ });
256
+ ```
257
+
258
+ #### Swap tokens
259
+
260
+ ```typescript
261
+ await user.swap({
262
+ fromToken: "2jjj",
263
+ toToken: "abc1",
264
+ fromAmount: 10_000_000n,
265
+ });
266
+ ```
267
+
268
+ ### Liquidity
269
+
270
+ #### Add liquidity
271
+
272
+ ```typescript
273
+ await user.addLiquidity({
274
+ btcAmount: 20_000_000n,
275
+ token: "2jj",
276
+ });
277
+ ```
278
+
279
+ #### Remove liquidity
280
+
281
+ ```typescript
282
+ await user.removeLiquidity({
283
+ btcAmount: 20_000_000n,
284
+ token: "2jj",
285
+ });
286
+ ```
287
+
288
+ ### Token Creation
289
+
290
+ > **Note:** `requires_api` must be set to `true` when connecting.
291
+
292
+ ```typescript
293
+ const user = await odinConnect.connect({ requires_api: true });
294
+
295
+ await user.createToken({
296
+ image: file, // A File (PNG, JPEG, WebP, GIF, SVG, or AVIF; max 200KB)
297
+ principal: "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
298
+ name: "Test Token", // 3-30 characters
299
+ ticker: "TEST", // 3-10 uppercase alphanumeric, at least 2 letters
300
+ description: "A test token", // Optional, max 100 characters
301
+ website: "https://example.com", // Optional, valid URL
302
+ telegram: "", // Optional, valid Telegram URL
303
+ twitter: "", // Optional, valid Twitter/X URL
304
+ buy: 20_000_000n, // Optional, pre-buy amount in millisats
305
+ discount: "", // Optional, 10 alphanumeric characters
306
+ });
307
+ ```
308
+
309
+ ## Public API
310
+
311
+ The API client is available at `odinConnect.api` and does **not** require authentication for read operations.
312
+
313
+ ### Tokens
314
+
315
+ ```typescript
316
+ // List tokens with sorting and filtering
317
+ const tokens = await odinConnect.api.getTokens(
318
+ { page: 1, limit: 10 }, // Pagination
319
+ { field: "marketcap", direction: "desc" }, // Sort (optional)
320
+ { marketcap_min: 100_000_000n } // Filters (optional)
321
+ );
322
+
323
+ // Get a single token by ID
324
+ const token = await odinConnect.api.getToken("2jjj");
325
+ ```
326
+
327
+ **Available sort fields:** `marketcap`, `volume`, `price`, `holder_count`, `created_time`
328
+
329
+ **Available filters:**
330
+
331
+ | Filter | Type | Description |
332
+ |--------|------|-------------|
333
+ | `ascended` | `boolean` | Token has ascended |
334
+ | `etched` | `boolean` | Token has been etched |
335
+ | `external` | `boolean` | External (Bitcoin) token |
336
+ | `verified` | `boolean` | Verified token |
337
+ | `has_website` | `boolean` | Has a website |
338
+ | `has_twitter` | `boolean` | Has a Twitter account |
339
+ | `has_telegram` | `boolean` | Has a Telegram group |
340
+ | `marketcap_min` / `marketcap_max` | `bigint` | Market cap range (millisats) |
341
+ | `volume_min` / `volume_max` | `bigint` | Volume range (millisats) |
342
+ | `holders_min` / `holders_max` | `number` | Holder count range |
343
+ | `price_min` / `price_max` | `number` | Price range |
344
+ | `search` | `string` | Search by name or ticker |
345
+
346
+ ### Users
347
+
348
+ ```typescript
349
+ // Get activities (no principal required)
350
+ const activity = await odinConnect.api.getUserActivity({
351
+ pagination: { page: 1, limit: 10 },
352
+ });
353
+
354
+ // Get token by id
355
+ const token = await odinConnect.api.getToken("2jjj");
356
+
357
+ // Get user profile by id
358
+ const user = await odinConnect.api.getUser(
359
+ "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe"
360
+ );
361
+
362
+ // Get user balances by user id
363
+ const balances = await odinConnect.api.getBalances(
364
+ "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
365
+ { page: 1, limit: 20 }
366
+ );
367
+
368
+ // Get activities by user id
369
+ const activity = await odinConnect.api.getUserActivity(
370
+ "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
371
+ { page: 1, limit: 10 }
372
+ );
373
+
374
+ // Get transactions by user id
375
+ const transactions = await odinConnect.api.getUserTransactions(
376
+ "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe",
377
+ { page: 1, limit: 10 }
378
+ );
379
+
380
+ // Get user stats
381
+ const stats = await odinConnect.api.getUserStats(
382
+ "veyov-kjgrf-hke6v-6d63i-sdwae-oldgg-huau6-ke5g3-rllp2-5jhca-uqe"
383
+ );
384
+ ```
385
+
386
+ ## Utilities
387
+
388
+ OdinConnect exports utility functions under `OdinUtils`:
389
+
390
+ ```typescript
391
+ import { OdinUtils } from "odin-connect";
392
+
393
+ // Convert a decimal amount to the on-chain bigint representation
394
+ const amount = OdinUtils.convertToOdinAmount("1.5", token);
395
+ // For a token with decimals=3, divisibility=8 → 150_000_000_000n
396
+ ```
397
+
398
+ ### Token Field Validators
399
+
400
+ Validators are available for token creation fields:
401
+
402
+ ```typescript
403
+ import { OdinUtils } from "odin-connect";
404
+
405
+ OdinUtils.createTokenValidators.name("My Token"); // 3-30 chars
406
+ OdinUtils.createTokenValidators.ticker("TEST"); // 3-10 uppercase alphanumeric
407
+ OdinUtils.createTokenValidators.image(file); // PNG/JPEG/WebP/GIF/SVG/AVIF, max 200KB
408
+ OdinUtils.createTokenValidators.description("A token"); // Max 100 chars
409
+ OdinUtils.createTokenValidators.website("https://..."); // Valid URL
410
+ OdinUtils.createTokenValidators.twitter("https://x.com/...");
411
+ OdinUtils.createTokenValidators.telegram("https://t.me/...");
412
+ ```
413
+
414
+ ## Configuration
415
+
416
+ ### Environments
417
+
418
+ | Environment | Frontend URL | API Base URL |
419
+ |-------------|-------------|-------------|
420
+ | `prod` (default) | `https://odin.fun` | `https://api.odin.fun/v1` |
421
+ | `dev` | `https://dev.odin.fun` | `https://api.odin.fun/dev` |
422
+ | `local` | `http://localhost:5173` | `https://api.odin.fun/dev` |
423
+
424
+ ```typescript
425
+ const odinConnect = new OdinConnect({
426
+ name: "My App",
427
+ env: "dev", // Use development environment
428
+ });
429
+ ```
430
+
431
+ ## Types
432
+
433
+ All types are exported with the `Odin` prefix:
434
+
435
+ ```typescript
436
+ import type {
437
+ OdinUser,
438
+ OdinBalance,
439
+ OdinBaseToken,
440
+ OdinToken,
441
+ OdinTokenWithBalance,
442
+ OdinActivity,
443
+ OdinTransaction,
444
+ OdinAchievement,
445
+ OdinAchievementCategory,
446
+ OdinConnectedUser,
447
+ } from "odin-connect";
448
+ ```
449
+
450
+ ## Demo
451
+
452
+ This repository includes a working demo application in the `/demo` folder.
453
+
454
+ ```bash
455
+ # Build the library and start the demo dev server
456
+ npm run demo
457
+
458
+ # Or run just the demo (if already built)
459
+ npm run demo:start
460
+ ```
461
+
462
+ ## General Notes
463
+
464
+ - All BTC amounts are in **millisatoshis** (1 BTC = 100,000,000,000 millisats)
465
+ - All trading actions (buy, sell, transfer, swap, liquidity) open a popup for user authorization and return a `boolean`
466
+ - API data methods return paginated results; pass `{ page, limit }` to control pagination
467
+ - The SDK uses `postMessage` for secure cross-origin communication between your app and the Odin frontend popup
468
+ - BigInt fields (balances, amounts, market caps) are automatically deserialized from JSON