@vultisig/rujira 1.0.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/CHANGELOG.md +23 -0
- package/README.md +254 -0
- package/dist/assets/amount.d.ts +80 -0
- package/dist/assets/amount.d.ts.map +1 -0
- package/dist/assets/amount.js +186 -0
- package/dist/assets/asset.d.ts +43 -0
- package/dist/assets/asset.d.ts.map +1 -0
- package/dist/assets/asset.js +1 -0
- package/dist/assets/formats.d.ts +54 -0
- package/dist/assets/formats.d.ts.map +1 -0
- package/dist/assets/formats.js +164 -0
- package/dist/assets/index.d.ts +27 -0
- package/dist/assets/index.d.ts.map +1 -0
- package/dist/assets/index.js +45 -0
- package/dist/assets/registry.d.ts +37 -0
- package/dist/assets/registry.d.ts.map +1 -0
- package/dist/assets/registry.js +487 -0
- package/dist/assets/router.d.ts +44 -0
- package/dist/assets/router.d.ts.map +1 -0
- package/dist/assets/router.js +142 -0
- package/dist/client.d.ts +70 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +250 -0
- package/dist/config/constants.d.ts +25 -0
- package/dist/config/constants.d.ts.map +1 -0
- package/dist/config/constants.js +36 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +72 -0
- package/dist/discovery/discovery.d.ts +39 -0
- package/dist/discovery/discovery.d.ts.map +1 -0
- package/dist/discovery/discovery.js +250 -0
- package/dist/discovery/graphql-client.d.ts +46 -0
- package/dist/discovery/graphql-client.d.ts.map +1 -0
- package/dist/discovery/graphql-client.js +137 -0
- package/dist/discovery/index.d.ts +9 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +7 -0
- package/dist/discovery/types.d.ts +62 -0
- package/dist/discovery/types.d.ts.map +1 -0
- package/dist/discovery/types.js +5 -0
- package/dist/easy-routes.d.ts +216 -0
- package/dist/easy-routes.d.ts.map +1 -0
- package/dist/easy-routes.js +241 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +184 -0
- package/dist/index.d.ts +46 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/modules/assets.d.ts +68 -0
- package/dist/modules/assets.d.ts.map +1 -0
- package/dist/modules/assets.js +127 -0
- package/dist/modules/deposit.d.ts +152 -0
- package/dist/modules/deposit.d.ts.map +1 -0
- package/dist/modules/deposit.js +233 -0
- package/dist/modules/index.d.ts +12 -0
- package/dist/modules/index.d.ts.map +1 -0
- package/dist/modules/index.js +9 -0
- package/dist/modules/orderbook.d.ts +80 -0
- package/dist/modules/orderbook.d.ts.map +1 -0
- package/dist/modules/orderbook.js +320 -0
- package/dist/modules/swap.d.ts +48 -0
- package/dist/modules/swap.d.ts.map +1 -0
- package/dist/modules/swap.js +318 -0
- package/dist/modules/withdraw.d.ts +46 -0
- package/dist/modules/withdraw.d.ts.map +1 -0
- package/dist/modules/withdraw.js +218 -0
- package/dist/services/fee-estimator.d.ts +14 -0
- package/dist/services/fee-estimator.d.ts.map +1 -0
- package/dist/services/fee-estimator.js +89 -0
- package/dist/services/price-impact.d.ts +11 -0
- package/dist/services/price-impact.d.ts.map +1 -0
- package/dist/services/price-impact.js +58 -0
- package/dist/signer/index.d.ts +3 -0
- package/dist/signer/index.d.ts.map +1 -0
- package/dist/signer/index.js +1 -0
- package/dist/signer/keysign-builder.d.ts +21 -0
- package/dist/signer/keysign-builder.d.ts.map +1 -0
- package/dist/signer/keysign-builder.js +106 -0
- package/dist/signer/types.d.ts +81 -0
- package/dist/signer/types.d.ts.map +1 -0
- package/dist/signer/types.js +8 -0
- package/dist/signer/vultisig-provider.d.ts +33 -0
- package/dist/signer/vultisig-provider.d.ts.map +1 -0
- package/dist/signer/vultisig-provider.js +242 -0
- package/dist/types.d.ts +375 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +18 -0
- package/dist/utils/cache.d.ts +87 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +124 -0
- package/dist/utils/denom-conversion.d.ts +47 -0
- package/dist/utils/denom-conversion.d.ts.map +1 -0
- package/dist/utils/denom-conversion.js +105 -0
- package/dist/utils/encoding.d.ts +17 -0
- package/dist/utils/encoding.d.ts.map +1 -0
- package/dist/utils/encoding.js +55 -0
- package/dist/utils/format.d.ts +108 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +213 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/memo.d.ts +107 -0
- package/dist/utils/memo.d.ts.map +1 -0
- package/dist/utils/memo.js +190 -0
- package/dist/utils/rate-limiter.d.ts +38 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +67 -0
- package/dist/utils/type-guards.d.ts +22 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +27 -0
- package/dist/validation/address-validator.d.ts +15 -0
- package/dist/validation/address-validator.d.ts.map +1 -0
- package/dist/validation/address-validator.js +75 -0
- package/package.json +98 -0
- package/src/__tests__/live/README.md +47 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @vultisig/rujira
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#97](https://github.com/vultisig/vultisig-sdk/pull/97) [`75f441c`](https://github.com/vultisig/vultisig-sdk/commit/75f441cdf711e6ba04eed412dcf34002c5705144) Thanks [@bornslippynuxx](https://github.com/bornslippynuxx)! - Add Rujira DEX integration with FIN order book swaps, secured asset deposits/withdrawals, and CLI commands. New package: @vultisig/rujira for THORChain DEX operations (includes asset registry).
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#97](https://github.com/vultisig/vultisig-sdk/pull/97) [`e172aff`](https://github.com/vultisig/vultisig-sdk/commit/e172aff35aff86d182646a521dc1e3ac9e381f60) Thanks [@bornslippynuxx](https://github.com/bornslippynuxx)! - fix: address PR review bugs and safety issues
|
|
12
|
+
- Fix missing ChromeExtensionPolyfills import causing build failure
|
|
13
|
+
- Fix floating-point precision loss in CLI amount parsing for high-decimal tokens
|
|
14
|
+
- Fix BigInt crash on non-integer amount strings in swap validation
|
|
15
|
+
- Fix Number exponentiation precision loss in VaultSend formatAmount
|
|
16
|
+
- Use VaultError with error codes in chain validation instead of generic Error
|
|
17
|
+
- Add chainId mismatch validation in signAndBroadcast
|
|
18
|
+
- Add hex string input validation in hexDecode
|
|
19
|
+
- Guard against empty accounts array in client getAddress
|
|
20
|
+
- Use stricter bech32 THORChain address validator in deposit module
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`bd543af`](https://github.com/vultisig/vultisig-sdk/commit/bd543af73a50a4ce431f38e3ed77511c4ef65ea7), [`74516fa`](https://github.com/vultisig/vultisig-sdk/commit/74516fae8dabd844c9e0793b932f6284ce9aa009), [`7ceab79`](https://github.com/vultisig/vultisig-sdk/commit/7ceab79e53986bfefa3f5d4cb5d25855572fbd3f), [`cd57d64`](https://github.com/vultisig/vultisig-sdk/commit/cd57d6482e08bd6172550ec4eea0e0233abd7f76), [`e172aff`](https://github.com/vultisig/vultisig-sdk/commit/e172aff35aff86d182646a521dc1e3ac9e381f60), [`ea1e8d5`](https://github.com/vultisig/vultisig-sdk/commit/ea1e8d5dd14a7273021577471e44719609f983ca), [`3f5fdcb`](https://github.com/vultisig/vultisig-sdk/commit/3f5fdcbfbe23aa287dfbcb38e9be6c904af9caf0), [`6c5c77c`](https://github.com/vultisig/vultisig-sdk/commit/6c5c77ceb49620f711285effee98b052e6aab1f8)]:
|
|
23
|
+
- @vultisig/sdk@0.5.0
|
package/README.md
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# @vultisig/rujira
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for interacting with **Rujira (FIN)** on THORChain.
|
|
4
|
+
|
|
5
|
+
It provides:
|
|
6
|
+
- FIN **swap** quotes + execution (CosmWasm)
|
|
7
|
+
- **Secured asset** ("synth-like") deposit helpers (inbound address + memo)
|
|
8
|
+
- **Withdrawal** helpers (THORChain `MsgDeposit` withdrawal flow)
|
|
9
|
+
- Route discovery + convenience **easy routes**
|
|
10
|
+
|
|
11
|
+
> Status: Early/alpha. APIs may change.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# npm
|
|
19
|
+
npm i @vultisig/rujira
|
|
20
|
+
|
|
21
|
+
# yarn
|
|
22
|
+
yarn add @vultisig/rujira
|
|
23
|
+
|
|
24
|
+
# pnpm
|
|
25
|
+
pnpm add @vultisig/rujira
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
If you are working inside this monorepo, use the workspace dependency:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@vultisig/rujira": "workspace:^"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Secured assets (what they are)
|
|
41
|
+
|
|
42
|
+
THORChain can custody L1 assets in inbound vaults and represent them on THORChain as **secured assets** (bank balances / denoms).
|
|
43
|
+
|
|
44
|
+
Typical flow:
|
|
45
|
+
1. You send an L1 transfer (e.g. BTC) to a THORChain inbound address
|
|
46
|
+
2. You include a memo that tells THORChain which THOR address should receive the secured asset
|
|
47
|
+
3. THORChain credits your THOR address with the corresponding secured denom
|
|
48
|
+
4. Those secured denoms can be swapped on FIN (Rujira orderbook DEX)
|
|
49
|
+
5. You can withdraw back to L1 by broadcasting a THORChain `MsgDeposit` with a withdraw memo
|
|
50
|
+
|
|
51
|
+
Notes:
|
|
52
|
+
- THORChain secured balances are commonly stored with **8 decimals**.
|
|
53
|
+
- This package accepts **human-friendly asset notation** like `BTC.BTC` / `ETH.ETH` and maps to FIN denoms when available via the built-in asset registry.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Quick start (read-only)
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { RujiraClient } from '@vultisig/rujira'
|
|
61
|
+
|
|
62
|
+
const client = new RujiraClient({ network: 'mainnet' })
|
|
63
|
+
await client.connect()
|
|
64
|
+
|
|
65
|
+
// 1) Discover FIN routes / contracts
|
|
66
|
+
const quote = await client.swap.getQuote({
|
|
67
|
+
fromAsset: 'THOR.RUNE',
|
|
68
|
+
toAsset: 'BTC.BTC',
|
|
69
|
+
amount: '100000000', // base units (example)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
console.log('Expected out:', quote.expectedOutput)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Quick start (with Vultisig signing)
|
|
78
|
+
|
|
79
|
+
To execute swaps/withdrawals you need a Cosmos signer. When using a Vultisig vault, use `VultisigRujiraProvider`.
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
import { Vultisig } from '@vultisig/sdk'
|
|
83
|
+
import { RujiraClient, VultisigRujiraProvider } from '@vultisig/rujira'
|
|
84
|
+
|
|
85
|
+
// 1) Initialize Vultisig SDK + pick a vault
|
|
86
|
+
const sdk = new Vultisig({ /* ... */ })
|
|
87
|
+
await sdk.initialize()
|
|
88
|
+
const vault = await sdk.getActiveVault()
|
|
89
|
+
if (!vault) throw new Error('No active vault')
|
|
90
|
+
|
|
91
|
+
// 2) Create a Rujira client with a Vultisig-backed signer
|
|
92
|
+
const signer = new VultisigRujiraProvider(vault)
|
|
93
|
+
const client = new RujiraClient({ network: 'mainnet', signer })
|
|
94
|
+
await client.connect()
|
|
95
|
+
|
|
96
|
+
// 3) FIN swap (secured denoms on THORChain)
|
|
97
|
+
const thorDestination = await vault.address('THORChain')
|
|
98
|
+
|
|
99
|
+
const quote = await client.swap.getQuote({
|
|
100
|
+
fromAsset: 'BTC.BTC',
|
|
101
|
+
toAsset: 'ETH.ETH',
|
|
102
|
+
amount: '100000',
|
|
103
|
+
destination: thorDestination,
|
|
104
|
+
slippageBps: 100,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
const swapResult = await client.swap.execute(quote, { slippageBps: 100 })
|
|
108
|
+
console.log('Swap tx hash:', swapResult.txHash)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Deposits (secure L1 → THORChain)
|
|
114
|
+
|
|
115
|
+
### Get inbound address + memo
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
import { RujiraClient } from '@vultisig/rujira'
|
|
119
|
+
|
|
120
|
+
const client = new RujiraClient({ network: 'mainnet' })
|
|
121
|
+
await client.connect()
|
|
122
|
+
|
|
123
|
+
const deposit = await client.deposit.prepare({
|
|
124
|
+
fromAsset: 'BTC.BTC',
|
|
125
|
+
amount: '100000', // base units; used for validation / dust threshold checks
|
|
126
|
+
thorAddress: 'thor1...',
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
console.log('Send to:', deposit.inboundAddress)
|
|
130
|
+
console.log('Memo:', deposit.memo)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### List all inbound addresses
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
const inbound = await client.deposit.getInboundAddresses()
|
|
137
|
+
console.log(inbound.map(i => ({ chain: i.chain, address: i.address })))
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Check secured balances on THORChain
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const balances = await client.deposit.getBalances('thor1...')
|
|
144
|
+
console.log(balances)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Withdrawals (THORChain → L1)
|
|
150
|
+
|
|
151
|
+
Withdrawals are executed on THORChain via `MsgDeposit` with a withdraw memo. When using Vultisig, withdrawal execution requires a `VultisigRujiraProvider` (it exposes vault access for MsgDeposit signing).
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { RujiraClient, VultisigRujiraProvider } from '@vultisig/rujira'
|
|
155
|
+
|
|
156
|
+
const client = new RujiraClient({ network: 'mainnet', signer: new VultisigRujiraProvider(vault) })
|
|
157
|
+
await client.connect()
|
|
158
|
+
|
|
159
|
+
const prepared = await client.withdraw.prepare({
|
|
160
|
+
asset: 'BTC.BTC',
|
|
161
|
+
amount: '100000',
|
|
162
|
+
l1Address: 'bc1...',
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
const result = await client.withdraw.execute(prepared)
|
|
166
|
+
console.log('Withdraw tx hash:', result.txHash)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Easy routes (convenience)
|
|
172
|
+
|
|
173
|
+
Use pre-defined routes to avoid thinking about pairs.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { EASY_ROUTES, listEasyRoutes, getRoutesSummary } from '@vultisig/rujira'
|
|
177
|
+
|
|
178
|
+
console.log(getRoutesSummary())
|
|
179
|
+
console.log(listEasyRoutes())
|
|
180
|
+
|
|
181
|
+
const route = EASY_ROUTES.RUNE_TO_USDC
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## API reference (high level)
|
|
187
|
+
|
|
188
|
+
### `new RujiraClient(options)`
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
export interface RujiraClientOptions {
|
|
192
|
+
config?: Partial<RujiraConfig>
|
|
193
|
+
signer?: RujiraSigner
|
|
194
|
+
rpcEndpoint?: string
|
|
195
|
+
contractCache?: {
|
|
196
|
+
load?: () => Promise<Record<string, string>> | Record<string, string>
|
|
197
|
+
save?: (finContracts: Record<string, string>) => Promise<void> | void
|
|
198
|
+
}
|
|
199
|
+
debug?: boolean
|
|
200
|
+
swapOptions?: RujiraSwapOptions
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Key methods:
|
|
205
|
+
- `connect()` / `disconnect()`
|
|
206
|
+
- `isConnected()` / `canSign()`
|
|
207
|
+
- `getAddress()`
|
|
208
|
+
- `getBalance(address, denom)` / `getAllBalances(address)`
|
|
209
|
+
|
|
210
|
+
Modules:
|
|
211
|
+
- `client.swap.getQuote(params)` / `client.swap.execute(quote, options)`
|
|
212
|
+
- `client.deposit.prepare(params)` / `client.deposit.getInboundAddresses()` / `client.deposit.getBalances(thorAddress)`
|
|
213
|
+
- `client.withdraw.prepare(params)` / `client.withdraw.execute(prepared)`
|
|
214
|
+
|
|
215
|
+
### `VultisigRujiraProvider`
|
|
216
|
+
|
|
217
|
+
A Cosmos signer implementation that delegates signing to a Vultisig vault.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import { VultisigRujiraProvider } from '@vultisig/rujira'
|
|
221
|
+
|
|
222
|
+
const signer = new VultisigRujiraProvider(vault /*, chainId? */)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## GraphQL / discovery rate limits (optional)
|
|
228
|
+
|
|
229
|
+
Rujira contract discovery may use GraphQL. If you run into HTTP 429 rate limits, you can provide an API token to the **GraphQL client** used by discovery.
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
import { RujiraDiscovery } from '@vultisig/rujira'
|
|
233
|
+
|
|
234
|
+
const discovery = new RujiraDiscovery({
|
|
235
|
+
network: 'mainnet',
|
|
236
|
+
graphql: { apiKey: process.env.RUJIRA_API_KEY },
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
const contracts = await discovery.discoverContracts()
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Examples
|
|
245
|
+
|
|
246
|
+
See:
|
|
247
|
+
- `packages/rujira/examples/`
|
|
248
|
+
- `packages/rujira/docs/`
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
MIT
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Asset, Layer } from './asset.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decimal conversion utilities
|
|
4
|
+
*/
|
|
5
|
+
export declare function nativeToThorchain(amount: bigint, asset: Asset): bigint;
|
|
6
|
+
export declare function thorchainToNative(amount: bigint, asset: Asset): bigint;
|
|
7
|
+
export declare function thorchainToFin(amount: bigint, asset: Asset): bigint;
|
|
8
|
+
export declare function finToThorchain(amount: bigint, asset: Asset): bigint;
|
|
9
|
+
export declare function nativeToFin(amount: bigint, asset: Asset): bigint;
|
|
10
|
+
export declare function finToNative(amount: bigint, asset: Asset): bigint;
|
|
11
|
+
/**
|
|
12
|
+
* Amount class representing a value on a specific layer
|
|
13
|
+
*/
|
|
14
|
+
export declare class Amount {
|
|
15
|
+
readonly asset: Asset;
|
|
16
|
+
readonly layer: Layer;
|
|
17
|
+
readonly raw: bigint;
|
|
18
|
+
constructor(asset: Asset, layer: Layer, raw: bigint);
|
|
19
|
+
/**
|
|
20
|
+
* Create Amount from human-readable string
|
|
21
|
+
*/
|
|
22
|
+
static from(human: string, asset: Asset, layer: Layer): Amount;
|
|
23
|
+
/**
|
|
24
|
+
* Create Amount from raw bigint value
|
|
25
|
+
*/
|
|
26
|
+
static fromRaw(raw: bigint, asset: Asset, layer: Layer): Amount;
|
|
27
|
+
/**
|
|
28
|
+
* Convert to specific layer
|
|
29
|
+
*/
|
|
30
|
+
toLayer(target: Layer): Amount;
|
|
31
|
+
/**
|
|
32
|
+
* Convert to native layer
|
|
33
|
+
*/
|
|
34
|
+
toNative(): Amount;
|
|
35
|
+
/**
|
|
36
|
+
* Convert to THORChain layer
|
|
37
|
+
*/
|
|
38
|
+
toThorchain(): Amount;
|
|
39
|
+
/**
|
|
40
|
+
* Convert to FIN layer
|
|
41
|
+
*/
|
|
42
|
+
toFin(): Amount;
|
|
43
|
+
/**
|
|
44
|
+
* Get human-readable representation
|
|
45
|
+
*/
|
|
46
|
+
toHuman(precision?: number): string;
|
|
47
|
+
/**
|
|
48
|
+
* Get display string with asset name
|
|
49
|
+
*/
|
|
50
|
+
toDisplay(precision?: number): string;
|
|
51
|
+
/**
|
|
52
|
+
* Get raw bigint value
|
|
53
|
+
*/
|
|
54
|
+
toRaw(): bigint;
|
|
55
|
+
/**
|
|
56
|
+
* Add two amounts (must be same asset and layer)
|
|
57
|
+
*/
|
|
58
|
+
add(other: Amount): Amount;
|
|
59
|
+
/**
|
|
60
|
+
* Subtract two amounts (must be same asset and layer)
|
|
61
|
+
*/
|
|
62
|
+
subtract(other: Amount): Amount;
|
|
63
|
+
/**
|
|
64
|
+
* Multiply amount by a factor
|
|
65
|
+
*/
|
|
66
|
+
multiply(factor: number): Amount;
|
|
67
|
+
/**
|
|
68
|
+
* Check if amount is zero
|
|
69
|
+
*/
|
|
70
|
+
isZero(): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Check if amount is positive
|
|
73
|
+
*/
|
|
74
|
+
isPositive(): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Compare with another amount
|
|
77
|
+
*/
|
|
78
|
+
equals(other: Amount): boolean;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=amount.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"amount.d.ts","sourceRoot":"","sources":["../../src/assets/amount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAEzC;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAGtE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAGtE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAGnE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAGnE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAIhE;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAIhE;AAED;;GAEG;AACH,qBAAa,MAAM;IACjB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;gBAER,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM;IAMnD;;OAEG;IACH,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM;IAe9D;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM;IAI/D;;OAEG;IACH,OAAO,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM;IA0B9B;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAoBnC;;OAEG;IACH,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;IAIrC;;OAEG;IACH,KAAK,IAAI,MAAM;IAIf;;OAEG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAW1B;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAW/B;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAKhC;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAG/B"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decimal conversion utilities
|
|
3
|
+
*/
|
|
4
|
+
export function nativeToThorchain(amount, asset) {
|
|
5
|
+
const scale = asset.decimals.thorchain - asset.decimals.native;
|
|
6
|
+
return scale >= 0 ? amount * 10n ** BigInt(scale) : amount / 10n ** BigInt(-scale);
|
|
7
|
+
}
|
|
8
|
+
export function thorchainToNative(amount, asset) {
|
|
9
|
+
const scale = asset.decimals.native - asset.decimals.thorchain;
|
|
10
|
+
return scale >= 0 ? amount * 10n ** BigInt(scale) : amount / 10n ** BigInt(-scale);
|
|
11
|
+
}
|
|
12
|
+
export function thorchainToFin(amount, asset) {
|
|
13
|
+
const scale = asset.decimals.thorchain - asset.decimals.fin;
|
|
14
|
+
return amount / 10n ** BigInt(scale);
|
|
15
|
+
}
|
|
16
|
+
export function finToThorchain(amount, asset) {
|
|
17
|
+
const scale = asset.decimals.thorchain - asset.decimals.fin;
|
|
18
|
+
return amount * 10n ** BigInt(scale);
|
|
19
|
+
}
|
|
20
|
+
export function nativeToFin(amount, asset) {
|
|
21
|
+
// Convert through THORChain as intermediate
|
|
22
|
+
const thorchainAmount = nativeToThorchain(amount, asset);
|
|
23
|
+
return thorchainToFin(thorchainAmount, asset);
|
|
24
|
+
}
|
|
25
|
+
export function finToNative(amount, asset) {
|
|
26
|
+
// Convert through THORChain as intermediate
|
|
27
|
+
const thorchainAmount = finToThorchain(amount, asset);
|
|
28
|
+
return thorchainToNative(thorchainAmount, asset);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Amount class representing a value on a specific layer
|
|
32
|
+
*/
|
|
33
|
+
export class Amount {
|
|
34
|
+
constructor(asset, layer, raw) {
|
|
35
|
+
this.asset = asset;
|
|
36
|
+
this.layer = layer;
|
|
37
|
+
this.raw = raw;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create Amount from human-readable string
|
|
41
|
+
*/
|
|
42
|
+
static from(human, asset, layer) {
|
|
43
|
+
const decimals = asset.decimals[layer];
|
|
44
|
+
const parts = human.split('.');
|
|
45
|
+
let raw = BigInt(parts[0]) * 10n ** BigInt(decimals);
|
|
46
|
+
if (parts[1]) {
|
|
47
|
+
const fractionalDigits = Math.min(parts[1].length, decimals);
|
|
48
|
+
const fractionalPart = parts[1].substring(0, fractionalDigits).padEnd(decimals, '0');
|
|
49
|
+
raw += BigInt(fractionalPart);
|
|
50
|
+
}
|
|
51
|
+
return new Amount(asset, layer, raw);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create Amount from raw bigint value
|
|
55
|
+
*/
|
|
56
|
+
static fromRaw(raw, asset, layer) {
|
|
57
|
+
return new Amount(asset, layer, raw);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Convert to specific layer
|
|
61
|
+
*/
|
|
62
|
+
toLayer(target) {
|
|
63
|
+
if (this.layer === target) {
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
let targetRaw;
|
|
67
|
+
if (this.layer === 'native' && target === 'thorchain') {
|
|
68
|
+
targetRaw = nativeToThorchain(this.raw, this.asset);
|
|
69
|
+
}
|
|
70
|
+
else if (this.layer === 'thorchain' && target === 'native') {
|
|
71
|
+
targetRaw = thorchainToNative(this.raw, this.asset);
|
|
72
|
+
}
|
|
73
|
+
else if (this.layer === 'thorchain' && target === 'fin') {
|
|
74
|
+
targetRaw = thorchainToFin(this.raw, this.asset);
|
|
75
|
+
}
|
|
76
|
+
else if (this.layer === 'fin' && target === 'thorchain') {
|
|
77
|
+
targetRaw = finToThorchain(this.raw, this.asset);
|
|
78
|
+
}
|
|
79
|
+
else if (this.layer === 'native' && target === 'fin') {
|
|
80
|
+
targetRaw = nativeToFin(this.raw, this.asset);
|
|
81
|
+
}
|
|
82
|
+
else if (this.layer === 'fin' && target === 'native') {
|
|
83
|
+
targetRaw = finToNative(this.raw, this.asset);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
throw new Error(`Cannot convert from ${this.layer} to ${target}`);
|
|
87
|
+
}
|
|
88
|
+
return new Amount(this.asset, target, targetRaw);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Convert to native layer
|
|
92
|
+
*/
|
|
93
|
+
toNative() {
|
|
94
|
+
return this.toLayer('native');
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Convert to THORChain layer
|
|
98
|
+
*/
|
|
99
|
+
toThorchain() {
|
|
100
|
+
return this.toLayer('thorchain');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Convert to FIN layer
|
|
104
|
+
*/
|
|
105
|
+
toFin() {
|
|
106
|
+
return this.toLayer('fin');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get human-readable representation
|
|
110
|
+
*/
|
|
111
|
+
toHuman(precision) {
|
|
112
|
+
const decimals = this.asset.decimals[this.layer];
|
|
113
|
+
const divisor = 10n ** BigInt(decimals);
|
|
114
|
+
const wholePart = this.raw / divisor;
|
|
115
|
+
const fractionalPart = this.raw % divisor;
|
|
116
|
+
if (fractionalPart === 0n) {
|
|
117
|
+
return wholePart.toString();
|
|
118
|
+
}
|
|
119
|
+
const fractionalStr = fractionalPart.toString().padStart(decimals, '0');
|
|
120
|
+
const trimmed = precision !== undefined
|
|
121
|
+
? fractionalStr.substring(0, precision).replace(/0+$/, '')
|
|
122
|
+
: fractionalStr.replace(/0+$/, '');
|
|
123
|
+
return trimmed.length > 0 ? `${wholePart}.${trimmed}` : wholePart.toString();
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get display string with asset name
|
|
127
|
+
*/
|
|
128
|
+
toDisplay(precision) {
|
|
129
|
+
return `${this.toHuman(precision)} ${this.asset.id.toUpperCase()}`;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get raw bigint value
|
|
133
|
+
*/
|
|
134
|
+
toRaw() {
|
|
135
|
+
return this.raw;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Add two amounts (must be same asset and layer)
|
|
139
|
+
*/
|
|
140
|
+
add(other) {
|
|
141
|
+
if (this.asset.id !== other.asset.id) {
|
|
142
|
+
throw new Error('Cannot add amounts of different assets');
|
|
143
|
+
}
|
|
144
|
+
if (this.layer !== other.layer) {
|
|
145
|
+
throw new Error('Cannot add amounts from different layers');
|
|
146
|
+
}
|
|
147
|
+
return new Amount(this.asset, this.layer, this.raw + other.raw);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Subtract two amounts (must be same asset and layer)
|
|
151
|
+
*/
|
|
152
|
+
subtract(other) {
|
|
153
|
+
if (this.asset.id !== other.asset.id) {
|
|
154
|
+
throw new Error('Cannot subtract amounts of different assets');
|
|
155
|
+
}
|
|
156
|
+
if (this.layer !== other.layer) {
|
|
157
|
+
throw new Error('Cannot subtract amounts from different layers');
|
|
158
|
+
}
|
|
159
|
+
return new Amount(this.asset, this.layer, this.raw - other.raw);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Multiply amount by a factor
|
|
163
|
+
*/
|
|
164
|
+
multiply(factor) {
|
|
165
|
+
const factorBigInt = (BigInt(Math.round(factor * 1000000)) * this.raw) / 1000000n;
|
|
166
|
+
return new Amount(this.asset, this.layer, factorBigInt);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if amount is zero
|
|
170
|
+
*/
|
|
171
|
+
isZero() {
|
|
172
|
+
return this.raw === 0n;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if amount is positive
|
|
176
|
+
*/
|
|
177
|
+
isPositive() {
|
|
178
|
+
return this.raw > 0n;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Compare with another amount
|
|
182
|
+
*/
|
|
183
|
+
equals(other) {
|
|
184
|
+
return this.asset.id === other.asset.id && this.layer === other.layer && this.raw === other.raw;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset interface representing a digital asset with multi-layer support
|
|
3
|
+
*/
|
|
4
|
+
export type Asset = {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
chain: string;
|
|
8
|
+
contract?: string;
|
|
9
|
+
decimals: {
|
|
10
|
+
native: number;
|
|
11
|
+
thorchain: number;
|
|
12
|
+
fin: number;
|
|
13
|
+
};
|
|
14
|
+
formats: {
|
|
15
|
+
l1: string;
|
|
16
|
+
thorchain: string;
|
|
17
|
+
fin: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Layer types supported by the assets package
|
|
22
|
+
*/
|
|
23
|
+
export type Layer = 'native' | 'thorchain' | 'fin';
|
|
24
|
+
/**
|
|
25
|
+
* Quote interface for swap operations
|
|
26
|
+
*/
|
|
27
|
+
export type Quote = {
|
|
28
|
+
path: 'thorchain-lp' | 'rujira-fin';
|
|
29
|
+
input: Amount;
|
|
30
|
+
output: Amount;
|
|
31
|
+
minimumOutput: Amount;
|
|
32
|
+
priceImpact: string;
|
|
33
|
+
fees: {
|
|
34
|
+
network: Amount;
|
|
35
|
+
protocol: Amount;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
export type Amount = {
|
|
39
|
+
readonly asset: Asset;
|
|
40
|
+
readonly layer: Layer;
|
|
41
|
+
readonly raw: bigint;
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=asset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../../src/assets/asset.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE;QACR,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,CAAA;QACjB,GAAG,EAAE,MAAM,CAAA;KACZ,CAAA;IACD,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,SAAS,EAAE,MAAM,CAAA;QACjB,GAAG,EAAE,MAAM,CAAA;KACZ,CAAA;CACF,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAA;AAElD;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,IAAI,EAAE,cAAc,GAAG,YAAY,CAAA;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAA;QACf,QAAQ,EAAE,MAAM,CAAA;KACjB,CAAA;CACF,CAAA;AAGD,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CACrB,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Asset } from './asset.js';
|
|
2
|
+
/**
|
|
3
|
+
* Convert asset to THORChain format
|
|
4
|
+
*/
|
|
5
|
+
export declare function toThorchainFormat(asset: Asset): string;
|
|
6
|
+
/**
|
|
7
|
+
* Convert asset to FIN format
|
|
8
|
+
*/
|
|
9
|
+
export declare function toFinFormat(asset: Asset): string;
|
|
10
|
+
/**
|
|
11
|
+
* Convert asset to L1/native format
|
|
12
|
+
*/
|
|
13
|
+
export declare function toL1Format(asset: Asset): string;
|
|
14
|
+
/**
|
|
15
|
+
* Parse asset identifier from any format
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseAsset(input: string): Asset | null;
|
|
18
|
+
/**
|
|
19
|
+
* Normalize THORChain pool name to standard format
|
|
20
|
+
*/
|
|
21
|
+
export declare function normalizeThorchainPool(pool: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Normalize FIN format to lowercase with hyphens
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeFinFormat(format: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Extract chain from THORChain format
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractChainFromThorchain(thorchainFormat: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Extract symbol from THORChain format
|
|
32
|
+
*/
|
|
33
|
+
export declare function extractSymbolFromThorchain(thorchainFormat: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* Extract contract from THORChain format (if present)
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractContractFromThorchain(thorchainFormat: string): string | undefined;
|
|
38
|
+
/**
|
|
39
|
+
* Build THORChain format from components
|
|
40
|
+
*/
|
|
41
|
+
export declare function buildThorchainFormat(chain: string, symbol: string, contract?: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Build FIN format from components
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildFinFormat(chain: string, symbol: string, contract?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Convert between any two formats
|
|
48
|
+
*/
|
|
49
|
+
export declare function convertFormat(input: string, targetFormat: 'l1' | 'thorchain' | 'fin'): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Detect format type of input string
|
|
52
|
+
*/
|
|
53
|
+
export declare function detectFormat(input: string): 'l1' | 'thorchain' | 'fin' | 'unknown';
|
|
54
|
+
//# sourceMappingURL=formats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formats.d.ts","sourceRoot":"","sources":["../../src/assets/formats.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAGlC;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEhD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI,CAEtD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB3D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAqBzE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAa1E;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAUxF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ7F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAQvF;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,GAAG,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,IAAI,CAcpG;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,WAAW,GAAG,KAAK,GAAG,SAAS,CAyBlF"}
|