@waiaas/actions 2.13.0 → 2.14.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/providers/polymarket/market-schemas.d.ts +2 -2
- package/dist/providers/xrpl-dex/__tests__/offer-builder.test.d.ts +2 -0
- package/dist/providers/xrpl-dex/__tests__/offer-builder.test.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/__tests__/offer-builder.test.js +225 -0
- package/dist/providers/xrpl-dex/__tests__/offer-builder.test.js.map +1 -0
- package/dist/providers/xrpl-dex/__tests__/orderbook-client.test.d.ts +2 -0
- package/dist/providers/xrpl-dex/__tests__/orderbook-client.test.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/__tests__/orderbook-client.test.js +321 -0
- package/dist/providers/xrpl-dex/__tests__/orderbook-client.test.js.map +1 -0
- package/dist/providers/xrpl-dex/__tests__/xrpl-dex-provider.test.d.ts +2 -0
- package/dist/providers/xrpl-dex/__tests__/xrpl-dex-provider.test.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/__tests__/xrpl-dex-provider.test.js +255 -0
- package/dist/providers/xrpl-dex/__tests__/xrpl-dex-provider.test.js.map +1 -0
- package/dist/providers/xrpl-dex/index.d.ts +39 -0
- package/dist/providers/xrpl-dex/index.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/index.js +262 -0
- package/dist/providers/xrpl-dex/index.js.map +1 -0
- package/dist/providers/xrpl-dex/offer-builder.d.ts +67 -0
- package/dist/providers/xrpl-dex/offer-builder.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/offer-builder.js +166 -0
- package/dist/providers/xrpl-dex/offer-builder.js.map +1 -0
- package/dist/providers/xrpl-dex/orderbook-client.d.ts +94 -0
- package/dist/providers/xrpl-dex/orderbook-client.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/orderbook-client.js +220 -0
- package/dist/providers/xrpl-dex/orderbook-client.js.map +1 -0
- package/dist/providers/xrpl-dex/schemas.d.ts +95 -0
- package/dist/providers/xrpl-dex/schemas.d.ts.map +1 -0
- package/dist/providers/xrpl-dex/schemas.js +73 -0
- package/dist/providers/xrpl-dex/schemas.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IActionProvider, ActionProviderMetadata, ActionDefinition, ActionContext, ContractCallRequest, ApiDirectResult } from '@waiaas/core';
|
|
2
|
+
import type { XrplOrderbookClient } from './orderbook-client.js';
|
|
3
|
+
export declare class XrplDexProvider implements IActionProvider {
|
|
4
|
+
private readonly orderbookClient;
|
|
5
|
+
readonly metadata: ActionProviderMetadata;
|
|
6
|
+
readonly actions: readonly ActionDefinition[];
|
|
7
|
+
constructor(orderbookClient: XrplOrderbookClient);
|
|
8
|
+
resolve(actionName: string, params: Record<string, unknown>, context: ActionContext): Promise<ContractCallRequest | ContractCallRequest[] | ApiDirectResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Immediate swap with tfImmediateOrCancel.
|
|
11
|
+
* If TakerPays is an IOU and no trust line exists, returns 2-step
|
|
12
|
+
* [TrustSet, OfferCreate] array for automatic trust line setup (DEX-07).
|
|
13
|
+
*/
|
|
14
|
+
private resolveSwap;
|
|
15
|
+
/**
|
|
16
|
+
* Limit order -- passive order on the orderbook.
|
|
17
|
+
* Validates reserve before placing (DEX-08).
|
|
18
|
+
* Checks trust line for IOU TakerPays (DEX-07).
|
|
19
|
+
*/
|
|
20
|
+
private resolveLimitOrder;
|
|
21
|
+
/**
|
|
22
|
+
* Cancel an existing offer by OfferSequence.
|
|
23
|
+
*/
|
|
24
|
+
private resolveCancelOrder;
|
|
25
|
+
/**
|
|
26
|
+
* Query orderbook depth for a trading pair.
|
|
27
|
+
*/
|
|
28
|
+
private resolveGetOrderbook;
|
|
29
|
+
/**
|
|
30
|
+
* List active offers for the wallet account.
|
|
31
|
+
*/
|
|
32
|
+
private resolveGetOffers;
|
|
33
|
+
/**
|
|
34
|
+
* If token is IOU and trust line doesn't exist, build a TrustSet
|
|
35
|
+
* ContractCallRequest to be prepended as the first step.
|
|
36
|
+
*/
|
|
37
|
+
private maybeBuildTrustSet;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/xrpl-dex/index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACnB,eAAe,EAChB,MAAM,cAAc,CAAC;AAgBtB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAMjE,qBAAa,eAAgB,YAAW,eAAe;IAIzC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAH5C,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;gBAEjB,eAAe,EAAE,mBAAmB;IAyD3D,OAAO,CACX,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,mBAAmB,GAAG,mBAAmB,EAAE,GAAG,eAAe,CAAC;IAuBzE;;;;OAIG;YACW,WAAW;IA4BzB;;;;OAIG;YACW,iBAAiB;IAkC/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAmB1B;;OAEG;YACW,mBAAmB;IAyBjC;;OAEG;YACW,gBAAgB;IA0B9B;;;OAGG;YACW,kBAAkB;CAuCjC"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XRPL DEX Action Provider.
|
|
3
|
+
*
|
|
4
|
+
* Implements IActionProvider with 5 actions:
|
|
5
|
+
* - swap: Immediate-or-Cancel swap (tfImmediateOrCancel)
|
|
6
|
+
* - limit_order: Passive limit order on the orderbook
|
|
7
|
+
* - cancel_order: Cancel an existing offer by OfferSequence
|
|
8
|
+
* - get_orderbook: Query orderbook depth (ApiDirectResult)
|
|
9
|
+
* - get_offers: Query account's active offers (ApiDirectResult)
|
|
10
|
+
*
|
|
11
|
+
* On-chain actions return ContractCallRequest with calldata JSON
|
|
12
|
+
* containing xrplTxType for RippleAdapter.buildContractCall() routing.
|
|
13
|
+
*
|
|
14
|
+
* @see Phase 02-02 Task 1
|
|
15
|
+
*/
|
|
16
|
+
import { ChainError } from '@waiaas/core';
|
|
17
|
+
import { SwapInputSchema, LimitOrderInputSchema, CancelOrderInputSchema, GetOrderbookInputSchema, GetOffersInputSchema, } from './schemas.js';
|
|
18
|
+
import { buildSwapParams, buildLimitOrderParams, buildCancelParams, validateReserve, parseTokenToBookOfferCurrency, } from './offer-builder.js';
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Provider implementation
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
export class XrplDexProvider {
|
|
23
|
+
orderbookClient;
|
|
24
|
+
metadata;
|
|
25
|
+
actions;
|
|
26
|
+
constructor(orderbookClient) {
|
|
27
|
+
this.orderbookClient = orderbookClient;
|
|
28
|
+
this.metadata = {
|
|
29
|
+
name: 'xrpl_dex',
|
|
30
|
+
displayName: 'XRPL DEX',
|
|
31
|
+
description: 'XRPL native orderbook DEX for token swaps and limit orders',
|
|
32
|
+
version: '1.0.0',
|
|
33
|
+
chains: ['ripple'],
|
|
34
|
+
mcpExpose: true,
|
|
35
|
+
requiresApiKey: false,
|
|
36
|
+
requiredApis: [],
|
|
37
|
+
requiresSigningKey: false,
|
|
38
|
+
};
|
|
39
|
+
this.actions = [
|
|
40
|
+
{
|
|
41
|
+
name: 'swap',
|
|
42
|
+
description: 'Immediate swap on XRPL DEX with slippage protection (tfImmediateOrCancel)',
|
|
43
|
+
chain: 'ripple',
|
|
44
|
+
inputSchema: SwapInputSchema,
|
|
45
|
+
riskLevel: 'medium',
|
|
46
|
+
defaultTier: 'INSTANT',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'limit_order',
|
|
50
|
+
description: 'Place a limit order on the XRPL DEX orderbook with expiration',
|
|
51
|
+
chain: 'ripple',
|
|
52
|
+
inputSchema: LimitOrderInputSchema,
|
|
53
|
+
riskLevel: 'medium',
|
|
54
|
+
defaultTier: 'DELAY',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'cancel_order',
|
|
58
|
+
description: 'Cancel an existing XRPL DEX offer by its sequence number',
|
|
59
|
+
chain: 'ripple',
|
|
60
|
+
inputSchema: CancelOrderInputSchema,
|
|
61
|
+
riskLevel: 'low',
|
|
62
|
+
defaultTier: 'INSTANT',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'get_orderbook',
|
|
66
|
+
description: 'Query the XRPL DEX orderbook depth for a trading pair (bids and asks)',
|
|
67
|
+
chain: 'ripple',
|
|
68
|
+
inputSchema: GetOrderbookInputSchema,
|
|
69
|
+
riskLevel: 'low',
|
|
70
|
+
defaultTier: 'INSTANT',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'get_offers',
|
|
74
|
+
description: 'List active XRPL DEX offers for the current wallet account',
|
|
75
|
+
chain: 'ripple',
|
|
76
|
+
inputSchema: GetOffersInputSchema,
|
|
77
|
+
riskLevel: 'low',
|
|
78
|
+
defaultTier: 'INSTANT',
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
async resolve(actionName, params, context) {
|
|
83
|
+
switch (actionName) {
|
|
84
|
+
case 'swap':
|
|
85
|
+
return this.resolveSwap(params, context);
|
|
86
|
+
case 'limit_order':
|
|
87
|
+
return this.resolveLimitOrder(params, context);
|
|
88
|
+
case 'cancel_order':
|
|
89
|
+
return this.resolveCancelOrder(params);
|
|
90
|
+
case 'get_orderbook':
|
|
91
|
+
return this.resolveGetOrderbook(params);
|
|
92
|
+
case 'get_offers':
|
|
93
|
+
return this.resolveGetOffers(context);
|
|
94
|
+
default:
|
|
95
|
+
throw new ChainError('INVALID_INSTRUCTION', 'ripple', {
|
|
96
|
+
message: `Unknown XRPL DEX action: ${actionName}`,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// -------------------------------------------------------------------------
|
|
101
|
+
// On-chain actions
|
|
102
|
+
// -------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Immediate swap with tfImmediateOrCancel.
|
|
105
|
+
* If TakerPays is an IOU and no trust line exists, returns 2-step
|
|
106
|
+
* [TrustSet, OfferCreate] array for automatic trust line setup (DEX-07).
|
|
107
|
+
*/
|
|
108
|
+
async resolveSwap(params, context) {
|
|
109
|
+
const input = SwapInputSchema.parse(params);
|
|
110
|
+
const calldataObj = buildSwapParams(input);
|
|
111
|
+
// Check trust line for IOU TakerPays (what we receive)
|
|
112
|
+
const trustSetRequest = await this.maybeBuildTrustSet(input.takerPays, context.walletAddress);
|
|
113
|
+
const swapRequest = {
|
|
114
|
+
type: 'CONTRACT_CALL',
|
|
115
|
+
to: getIssuerAddress(input.takerPays),
|
|
116
|
+
value: input.takerGets === 'XRP' ? input.takerGetsAmount : undefined,
|
|
117
|
+
calldata: JSON.stringify(calldataObj),
|
|
118
|
+
actionProvider: 'xrpl_dex',
|
|
119
|
+
actionName: 'swap',
|
|
120
|
+
};
|
|
121
|
+
if (trustSetRequest) {
|
|
122
|
+
return [trustSetRequest, swapRequest];
|
|
123
|
+
}
|
|
124
|
+
return swapRequest;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Limit order -- passive order on the orderbook.
|
|
128
|
+
* Validates reserve before placing (DEX-08).
|
|
129
|
+
* Checks trust line for IOU TakerPays (DEX-07).
|
|
130
|
+
*/
|
|
131
|
+
async resolveLimitOrder(params, context) {
|
|
132
|
+
const input = LimitOrderInputSchema.parse(params);
|
|
133
|
+
// Validate reserve for new offer object
|
|
134
|
+
const reserve = await this.orderbookClient.getAccountReserve(context.walletAddress);
|
|
135
|
+
const offers = await this.orderbookClient.getAccountOffers(context.walletAddress, 400);
|
|
136
|
+
validateReserve(reserve.availableBalance, offers.length);
|
|
137
|
+
const calldataObj = buildLimitOrderParams(input);
|
|
138
|
+
// Check trust line for IOU TakerPays
|
|
139
|
+
const trustSetRequest = await this.maybeBuildTrustSet(input.takerPays, context.walletAddress);
|
|
140
|
+
const limitRequest = {
|
|
141
|
+
type: 'CONTRACT_CALL',
|
|
142
|
+
to: getIssuerAddress(input.takerPays),
|
|
143
|
+
value: input.takerGets === 'XRP' ? input.takerGetsAmount : undefined,
|
|
144
|
+
calldata: JSON.stringify(calldataObj),
|
|
145
|
+
actionProvider: 'xrpl_dex',
|
|
146
|
+
actionName: 'limit_order',
|
|
147
|
+
};
|
|
148
|
+
if (trustSetRequest) {
|
|
149
|
+
return [trustSetRequest, limitRequest];
|
|
150
|
+
}
|
|
151
|
+
return limitRequest;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Cancel an existing offer by OfferSequence.
|
|
155
|
+
*/
|
|
156
|
+
resolveCancelOrder(params) {
|
|
157
|
+
const input = CancelOrderInputSchema.parse(params);
|
|
158
|
+
const calldataObj = buildCancelParams(input.offerSequence);
|
|
159
|
+
return {
|
|
160
|
+
type: 'CONTRACT_CALL',
|
|
161
|
+
to: 'native',
|
|
162
|
+
calldata: JSON.stringify(calldataObj),
|
|
163
|
+
actionProvider: 'xrpl_dex',
|
|
164
|
+
actionName: 'cancel_order',
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
// Read-only queries (ApiDirectResult -- pipeline bypass)
|
|
169
|
+
// -------------------------------------------------------------------------
|
|
170
|
+
/**
|
|
171
|
+
* Query orderbook depth for a trading pair.
|
|
172
|
+
*/
|
|
173
|
+
async resolveGetOrderbook(params) {
|
|
174
|
+
const input = GetOrderbookInputSchema.parse(params);
|
|
175
|
+
const base = parseTokenToBookOfferCurrency(input.base);
|
|
176
|
+
const counter = parseTokenToBookOfferCurrency(input.counter);
|
|
177
|
+
const orderbook = await this.orderbookClient.getOrderbook(base, counter, input.limit);
|
|
178
|
+
return {
|
|
179
|
+
__apiDirect: true,
|
|
180
|
+
externalId: `orderbook-${Date.now()}`,
|
|
181
|
+
status: 'success',
|
|
182
|
+
provider: 'xrpl_dex',
|
|
183
|
+
action: 'get_orderbook',
|
|
184
|
+
data: {
|
|
185
|
+
base: input.base,
|
|
186
|
+
counter: input.counter,
|
|
187
|
+
bids: orderbook.bids,
|
|
188
|
+
asks: orderbook.asks,
|
|
189
|
+
spread: orderbook.spread,
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* List active offers for the wallet account.
|
|
195
|
+
*/
|
|
196
|
+
async resolveGetOffers(context) {
|
|
197
|
+
const offers = await this.orderbookClient.getAccountOffers(context.walletAddress, 50);
|
|
198
|
+
return {
|
|
199
|
+
__apiDirect: true,
|
|
200
|
+
externalId: `offers-${Date.now()}`,
|
|
201
|
+
status: 'success',
|
|
202
|
+
provider: 'xrpl_dex',
|
|
203
|
+
action: 'get_offers',
|
|
204
|
+
data: {
|
|
205
|
+
account: context.walletAddress,
|
|
206
|
+
offers,
|
|
207
|
+
count: offers.length,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
// -------------------------------------------------------------------------
|
|
212
|
+
// Trust line auto-setup (DEX-07)
|
|
213
|
+
// -------------------------------------------------------------------------
|
|
214
|
+
/**
|
|
215
|
+
* If token is IOU and trust line doesn't exist, build a TrustSet
|
|
216
|
+
* ContractCallRequest to be prepended as the first step.
|
|
217
|
+
*/
|
|
218
|
+
async maybeBuildTrustSet(token, walletAddress) {
|
|
219
|
+
if (token === 'XRP')
|
|
220
|
+
return null;
|
|
221
|
+
const dotIndex = token.indexOf('.');
|
|
222
|
+
if (dotIndex === -1)
|
|
223
|
+
return null;
|
|
224
|
+
const currency = token.slice(0, dotIndex);
|
|
225
|
+
const issuer = token.slice(dotIndex + 1);
|
|
226
|
+
const hasTrustLine = await this.orderbookClient.checkTrustLine(walletAddress, currency, issuer);
|
|
227
|
+
if (hasTrustLine)
|
|
228
|
+
return null;
|
|
229
|
+
// Build TrustSet calldata for buildContractCall routing
|
|
230
|
+
const trustSetCalldata = {
|
|
231
|
+
xrplTxType: 'TrustSet',
|
|
232
|
+
LimitAmount: {
|
|
233
|
+
currency,
|
|
234
|
+
issuer,
|
|
235
|
+
value: '1000000000000000', // max trust line limit
|
|
236
|
+
},
|
|
237
|
+
Flags: 0x00020000, // tfSetNoRipple
|
|
238
|
+
};
|
|
239
|
+
return {
|
|
240
|
+
type: 'CONTRACT_CALL',
|
|
241
|
+
to: issuer,
|
|
242
|
+
calldata: JSON.stringify(trustSetCalldata),
|
|
243
|
+
actionProvider: 'xrpl_dex',
|
|
244
|
+
actionName: 'trust_set_auto',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Helpers
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
/**
|
|
252
|
+
* Extract issuer address from token string for ContractCallRequest.to field.
|
|
253
|
+
* "XRP" -> "native"
|
|
254
|
+
* "USD.rIssuer" -> "rIssuer"
|
|
255
|
+
*/
|
|
256
|
+
function getIssuerAddress(token) {
|
|
257
|
+
if (token === 'XRP')
|
|
258
|
+
return 'native';
|
|
259
|
+
const dotIndex = token.indexOf('.');
|
|
260
|
+
return dotIndex > 0 ? token.slice(dotIndex + 1) : 'native';
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/xrpl-dex/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { SwapInput, LimitOrderInput } from './schemas.js';
|
|
2
|
+
/** Ripple epoch offset: 2000-01-01 00:00:00 UTC in Unix seconds. */
|
|
3
|
+
export declare const RIPPLE_EPOCH = 946684800;
|
|
4
|
+
/** tfImmediateOrCancel flag for instant swaps. */
|
|
5
|
+
export declare const TF_IMMEDIATE_OR_CANCEL = 131072;
|
|
6
|
+
/** Owner reserve per ledger object in drops (0.2 XRP). */
|
|
7
|
+
export declare const OWNER_RESERVE_DROPS = 200000;
|
|
8
|
+
export interface IssuedCurrencyAmount {
|
|
9
|
+
currency: string;
|
|
10
|
+
issuer: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
13
|
+
export interface BookOfferCurrency {
|
|
14
|
+
currency: string;
|
|
15
|
+
issuer?: string;
|
|
16
|
+
}
|
|
17
|
+
/** XRPL Amount: string (XRP drops) or IssuedCurrencyAmount (IOU). */
|
|
18
|
+
export type XrplAmount = string | IssuedCurrencyAmount;
|
|
19
|
+
/**
|
|
20
|
+
* Convert a token identifier + amount to XRPL Amount format.
|
|
21
|
+
*
|
|
22
|
+
* - "XRP" + amount -> string drops (e.g., "1000000")
|
|
23
|
+
* - "USD.rIssuer" + amount -> { currency: "USD", issuer: "rIssuer", value: "100" }
|
|
24
|
+
*
|
|
25
|
+
* @param token Token identifier ("XRP" or "CURRENCY.ISSUER")
|
|
26
|
+
* @param amount Amount string (drops for XRP, decimal for IOU)
|
|
27
|
+
* @throws ChainError if amount is zero/negative or token format invalid
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatXrplAmount(token: string, amount: string): XrplAmount;
|
|
30
|
+
/**
|
|
31
|
+
* Parse a token string into a BookOfferCurrency for book_offers RPC.
|
|
32
|
+
* "XRP" -> { currency: "XRP" }
|
|
33
|
+
* "USD.rIssuer" -> { currency: "USD", issuer: "rIssuer" }
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseTokenToBookOfferCurrency(token: string): BookOfferCurrency;
|
|
36
|
+
/** Calldata JSON for OfferCreate (swap/limit) or OfferCancel. */
|
|
37
|
+
export interface OfferCalldata {
|
|
38
|
+
xrplTxType: 'OfferCreate' | 'OfferCancel';
|
|
39
|
+
TakerGets?: XrplAmount;
|
|
40
|
+
TakerPays?: XrplAmount;
|
|
41
|
+
Flags?: number;
|
|
42
|
+
Expiration?: number;
|
|
43
|
+
OfferSequence?: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build OfferCreate calldata for an immediate swap (tfImmediateOrCancel).
|
|
47
|
+
* Slippage is applied to TakerPays (reduce minimum receive).
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildSwapParams(input: SwapInput): OfferCalldata;
|
|
50
|
+
/**
|
|
51
|
+
* Build OfferCreate calldata for a limit order (no IoC).
|
|
52
|
+
* Expiration is converted to Ripple epoch.
|
|
53
|
+
*/
|
|
54
|
+
export declare function buildLimitOrderParams(input: LimitOrderInput): OfferCalldata;
|
|
55
|
+
/**
|
|
56
|
+
* Build OfferCancel calldata.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildCancelParams(offerSequence: number): OfferCalldata;
|
|
59
|
+
/**
|
|
60
|
+
* Validate that the account has sufficient XRP for a new offer's owner reserve.
|
|
61
|
+
*
|
|
62
|
+
* @param availableXrpDrops Available XRP balance in drops (after existing reserves)
|
|
63
|
+
* @param existingOfferCount Number of existing offers (for context in error message)
|
|
64
|
+
* @throws ChainError if available balance < OWNER_RESERVE_DROPS
|
|
65
|
+
*/
|
|
66
|
+
export declare function validateReserve(availableXrpDrops: string, existingOfferCount: number): void;
|
|
67
|
+
//# sourceMappingURL=offer-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-builder.d.ts","sourceRoot":"","sources":["../../../src/providers/xrpl-dex/offer-builder.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/D,oEAAoE;AACpE,eAAO,MAAM,YAAY,YAAY,CAAC;AAEtC,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,SAAa,CAAC;AAEjD,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAM1C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qEAAqE;AACrE,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,oBAAoB,CAAC;AAMvD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CA8B1E;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAgB9E;AA6BD,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,aAAa,GAAG,aAAa,CAAC;IAC1C,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,aAAa,CAc/D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,GAAG,aAAa,CAa3E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,aAAa,CAKtE;AAMD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,iBAAiB,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAW3F"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XRPL DEX OfferBuilder -- builds OfferCreate/OfferCancel calldata JSON
|
|
3
|
+
* for the RippleAdapter.buildContractCall() pipeline.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - Currency amount conversion (XRP drops vs IOU object)
|
|
7
|
+
* - Slippage application for IoC swaps
|
|
8
|
+
* - Ripple epoch expiration for limit orders
|
|
9
|
+
* - Owner reserve validation for new offers
|
|
10
|
+
*
|
|
11
|
+
* @see Phase 02-01 Task 2
|
|
12
|
+
*/
|
|
13
|
+
import { ChainError } from '@waiaas/core';
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Constants
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/** Ripple epoch offset: 2000-01-01 00:00:00 UTC in Unix seconds. */
|
|
18
|
+
export const RIPPLE_EPOCH = 946684800;
|
|
19
|
+
/** tfImmediateOrCancel flag for instant swaps. */
|
|
20
|
+
export const TF_IMMEDIATE_OR_CANCEL = 0x00020000;
|
|
21
|
+
/** Owner reserve per ledger object in drops (0.2 XRP). */
|
|
22
|
+
export const OWNER_RESERVE_DROPS = 200000;
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Amount formatting
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
/**
|
|
27
|
+
* Convert a token identifier + amount to XRPL Amount format.
|
|
28
|
+
*
|
|
29
|
+
* - "XRP" + amount -> string drops (e.g., "1000000")
|
|
30
|
+
* - "USD.rIssuer" + amount -> { currency: "USD", issuer: "rIssuer", value: "100" }
|
|
31
|
+
*
|
|
32
|
+
* @param token Token identifier ("XRP" or "CURRENCY.ISSUER")
|
|
33
|
+
* @param amount Amount string (drops for XRP, decimal for IOU)
|
|
34
|
+
* @throws ChainError if amount is zero/negative or token format invalid
|
|
35
|
+
*/
|
|
36
|
+
export function formatXrplAmount(token, amount) {
|
|
37
|
+
// Validate amount is not zero or negative
|
|
38
|
+
const numericAmount = Number(amount);
|
|
39
|
+
if (isNaN(numericAmount) || numericAmount <= 0) {
|
|
40
|
+
throw new ChainError('INVALID_INSTRUCTION', 'ripple', {
|
|
41
|
+
message: `Invalid amount: ${amount}. Must be a positive number.`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (token === 'XRP') {
|
|
45
|
+
// XRP native: amount is already in drops (string)
|
|
46
|
+
return amount;
|
|
47
|
+
}
|
|
48
|
+
// IOU: parse "CURRENCY.ISSUER" format
|
|
49
|
+
const dotIndex = token.indexOf('.');
|
|
50
|
+
if (dotIndex === -1 || dotIndex === 0 || dotIndex === token.length - 1) {
|
|
51
|
+
throw new ChainError('INVALID_INSTRUCTION', 'ripple', {
|
|
52
|
+
message: `Invalid IOU token format: "${token}". Expected "CURRENCY.ISSUER" (e.g., "USD.rIssuer...").`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const currency = token.slice(0, dotIndex);
|
|
56
|
+
const issuer = token.slice(dotIndex + 1);
|
|
57
|
+
return {
|
|
58
|
+
currency,
|
|
59
|
+
issuer,
|
|
60
|
+
value: amount,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Parse a token string into a BookOfferCurrency for book_offers RPC.
|
|
65
|
+
* "XRP" -> { currency: "XRP" }
|
|
66
|
+
* "USD.rIssuer" -> { currency: "USD", issuer: "rIssuer" }
|
|
67
|
+
*/
|
|
68
|
+
export function parseTokenToBookOfferCurrency(token) {
|
|
69
|
+
if (token === 'XRP') {
|
|
70
|
+
return { currency: 'XRP' };
|
|
71
|
+
}
|
|
72
|
+
const dotIndex = token.indexOf('.');
|
|
73
|
+
if (dotIndex === -1 || dotIndex === 0 || dotIndex === token.length - 1) {
|
|
74
|
+
throw new ChainError('INVALID_INSTRUCTION', 'ripple', {
|
|
75
|
+
message: `Invalid IOU token format: "${token}". Expected "CURRENCY.ISSUER".`,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
currency: token.slice(0, dotIndex),
|
|
80
|
+
issuer: token.slice(dotIndex + 1),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Slippage helpers
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
/**
|
|
87
|
+
* Apply slippage to an amount (reduce by slippageBps).
|
|
88
|
+
* For IoC swaps, slippage reduces TakerPays (minimum receive).
|
|
89
|
+
*/
|
|
90
|
+
function applySlippage(amount, slippageBps, isIou) {
|
|
91
|
+
if (isIou) {
|
|
92
|
+
// IOU: decimal arithmetic
|
|
93
|
+
const value = parseFloat(amount);
|
|
94
|
+
const adjusted = value * (1 - slippageBps / 10000);
|
|
95
|
+
// Preserve precision (max 15 significant digits for XRPL IOU)
|
|
96
|
+
return adjusted.toPrecision(15).replace(/0+$/, '').replace(/\.$/, '');
|
|
97
|
+
}
|
|
98
|
+
// XRP drops: integer arithmetic
|
|
99
|
+
const drops = BigInt(amount);
|
|
100
|
+
const adjusted = drops - (drops * BigInt(slippageBps)) / 10000n;
|
|
101
|
+
return adjusted.toString();
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Build OfferCreate calldata for an immediate swap (tfImmediateOrCancel).
|
|
105
|
+
* Slippage is applied to TakerPays (reduce minimum receive).
|
|
106
|
+
*/
|
|
107
|
+
export function buildSwapParams(input) {
|
|
108
|
+
const takerGets = formatXrplAmount(input.takerGets, input.takerGetsAmount);
|
|
109
|
+
const isPayIou = input.takerPays !== 'XRP';
|
|
110
|
+
// Apply slippage to TakerPays (what we want = minimum acceptable)
|
|
111
|
+
const adjustedPayAmount = applySlippage(input.takerPaysAmount, input.slippageBps, isPayIou);
|
|
112
|
+
const takerPays = formatXrplAmount(input.takerPays, adjustedPayAmount);
|
|
113
|
+
return {
|
|
114
|
+
xrplTxType: 'OfferCreate',
|
|
115
|
+
TakerGets: takerGets,
|
|
116
|
+
TakerPays: takerPays,
|
|
117
|
+
Flags: TF_IMMEDIATE_OR_CANCEL,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build OfferCreate calldata for a limit order (no IoC).
|
|
122
|
+
* Expiration is converted to Ripple epoch.
|
|
123
|
+
*/
|
|
124
|
+
export function buildLimitOrderParams(input) {
|
|
125
|
+
const takerGets = formatXrplAmount(input.takerGets, input.takerGetsAmount);
|
|
126
|
+
const takerPays = formatXrplAmount(input.takerPays, input.takerPaysAmount);
|
|
127
|
+
// Convert expiration to Ripple epoch (current time + seconds - RIPPLE_EPOCH offset)
|
|
128
|
+
const expirationRipple = Math.floor(Date.now() / 1000) + input.expirationSeconds - RIPPLE_EPOCH;
|
|
129
|
+
return {
|
|
130
|
+
xrplTxType: 'OfferCreate',
|
|
131
|
+
TakerGets: takerGets,
|
|
132
|
+
TakerPays: takerPays,
|
|
133
|
+
Expiration: expirationRipple,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Build OfferCancel calldata.
|
|
138
|
+
*/
|
|
139
|
+
export function buildCancelParams(offerSequence) {
|
|
140
|
+
return {
|
|
141
|
+
xrplTxType: 'OfferCancel',
|
|
142
|
+
OfferSequence: offerSequence,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Reserve validation
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
/**
|
|
149
|
+
* Validate that the account has sufficient XRP for a new offer's owner reserve.
|
|
150
|
+
*
|
|
151
|
+
* @param availableXrpDrops Available XRP balance in drops (after existing reserves)
|
|
152
|
+
* @param existingOfferCount Number of existing offers (for context in error message)
|
|
153
|
+
* @throws ChainError if available balance < OWNER_RESERVE_DROPS
|
|
154
|
+
*/
|
|
155
|
+
export function validateReserve(availableXrpDrops, existingOfferCount) {
|
|
156
|
+
const available = BigInt(availableXrpDrops);
|
|
157
|
+
const required = BigInt(OWNER_RESERVE_DROPS);
|
|
158
|
+
if (available < required) {
|
|
159
|
+
const availableXrp = Number(available) / 1_000_000;
|
|
160
|
+
const requiredXrp = Number(required) / 1_000_000;
|
|
161
|
+
throw new ChainError('INSUFFICIENT_BALANCE', 'ripple', {
|
|
162
|
+
message: `Insufficient XRP for new offer: need ${requiredXrp} XRP reserve (available: ${availableXrp} XRP, current offers: ${existingOfferCount}). Each open offer requires ${requiredXrp} XRP owner reserve.`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=offer-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"offer-builder.js","sourceRoot":"","sources":["../../../src/providers/xrpl-dex/offer-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XrplOrderbookClient -- XRPL RPC wrapper for orderbook and account queries.
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - getOrderbook: book_offers for asks + bids with normalized prices
|
|
6
|
+
* - getAccountOffers: account_offers for active orders
|
|
7
|
+
* - checkTrustLine: account_lines for trust line existence
|
|
8
|
+
* - getAccountReserve: account_info for reserve calculation
|
|
9
|
+
*
|
|
10
|
+
* Uses lazy connection (ensureConnected) and explicit disconnect.
|
|
11
|
+
*
|
|
12
|
+
* @see Phase 02-01 Task 3
|
|
13
|
+
*/
|
|
14
|
+
import { Client } from 'xrpl';
|
|
15
|
+
import type { BookOfferCurrency } from './offer-builder.js';
|
|
16
|
+
export interface OrderbookEntry {
|
|
17
|
+
/** Price in counter/base units. */
|
|
18
|
+
price: number;
|
|
19
|
+
/** Amount in base units. */
|
|
20
|
+
amount: number;
|
|
21
|
+
/** Total in counter units (price * amount). */
|
|
22
|
+
total: number;
|
|
23
|
+
/** Available balance of the offer owner. */
|
|
24
|
+
ownerFunds: string;
|
|
25
|
+
/** Offer sequence number. */
|
|
26
|
+
sequence: number;
|
|
27
|
+
}
|
|
28
|
+
export interface OrderbookResult {
|
|
29
|
+
/** Sell orders (ascending by price -- best ask first). */
|
|
30
|
+
asks: OrderbookEntry[];
|
|
31
|
+
/** Buy orders (descending by price -- best bid first). */
|
|
32
|
+
bids: OrderbookEntry[];
|
|
33
|
+
/** Spread between best ask and best bid (NaN if no orders). */
|
|
34
|
+
spread: number;
|
|
35
|
+
}
|
|
36
|
+
export interface AccountOffer {
|
|
37
|
+
/** Offer sequence number (use for cancel_order). */
|
|
38
|
+
seq: number;
|
|
39
|
+
/** What the offer gives away. */
|
|
40
|
+
takerGets: string;
|
|
41
|
+
/** What the offer wants. */
|
|
42
|
+
takerPays: string;
|
|
43
|
+
/** Offer flags. */
|
|
44
|
+
flags: number;
|
|
45
|
+
/** Expiration (Ripple epoch, optional). */
|
|
46
|
+
expiration?: number;
|
|
47
|
+
}
|
|
48
|
+
export interface ReserveInfo {
|
|
49
|
+
/** Total balance in drops. */
|
|
50
|
+
balance: string;
|
|
51
|
+
/** Number of owned ledger objects. */
|
|
52
|
+
ownerCount: number;
|
|
53
|
+
/** Base reserve in drops (10 XRP). */
|
|
54
|
+
baseReserve: number;
|
|
55
|
+
/** Per-object owner reserve in drops (0.2 XRP). */
|
|
56
|
+
ownerReserve: number;
|
|
57
|
+
/** Available balance for new objects (balance - baseReserve - ownerCount * ownerReserve) in drops. */
|
|
58
|
+
availableBalance: string;
|
|
59
|
+
}
|
|
60
|
+
export declare class XrplOrderbookClient {
|
|
61
|
+
private readonly rpcUrl;
|
|
62
|
+
private client;
|
|
63
|
+
private connectingPromise;
|
|
64
|
+
constructor(rpcUrl: string);
|
|
65
|
+
/**
|
|
66
|
+
* Ensure WebSocket connection is established (lazy connect).
|
|
67
|
+
*/
|
|
68
|
+
ensureConnected(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Close WebSocket connection.
|
|
71
|
+
*/
|
|
72
|
+
disconnect(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Get the underlying xrpl.Client (for testing/direct access).
|
|
75
|
+
*/
|
|
76
|
+
getClient(): Client;
|
|
77
|
+
/**
|
|
78
|
+
* Query orderbook for a trading pair (both ask and bid sides).
|
|
79
|
+
*/
|
|
80
|
+
getOrderbook(base: BookOfferCurrency, counter: BookOfferCurrency, limit: number): Promise<OrderbookResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Query account's active offers.
|
|
83
|
+
*/
|
|
84
|
+
getAccountOffers(account: string, limit: number): Promise<AccountOffer[]>;
|
|
85
|
+
/**
|
|
86
|
+
* Check if a trust line exists for a specific currency/issuer pair.
|
|
87
|
+
*/
|
|
88
|
+
checkTrustLine(account: string, currency: string, issuer: string): Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Get account reserve information.
|
|
91
|
+
*/
|
|
92
|
+
getAccountReserve(account: string): Promise<ReserveInfo>;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=orderbook-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orderbook-client.d.ts","sourceRoot":"","sources":["../../../src/providers/xrpl-dex/orderbook-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAE9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAM5D,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,4BAA4B;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,0DAA0D;IAC1D,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,0DAA0D;IAC1D,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,oDAAoD;IACpD,GAAG,EAAE,MAAM,CAAC;IACZ,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,4BAA4B;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,sGAAsG;IACtG,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAMD,qBAAa,mBAAmB;IAIlB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,iBAAiB,CAA8B;gBAE1B,MAAM,EAAE,MAAM;IAE3C;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBtC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;OAEG;IACH,SAAS,IAAI,MAAM;IAWnB;;OAEG;IACG,YAAY,CAChB,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,iBAAiB,EAC1B,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,eAAe,CAAC;IAsD3B;;OAEG;IACG,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAuB/E;;OAEG;IACG,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBzF;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;CA8B/D"}
|