@stryke-xyz/premarket-sdk 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/dist/abi/AggregationRouterV6.abi.json +1186 -0
- package/dist/abi/NativeOrderFactory.abi.json +291 -0
- package/dist/abi/NativeOrderImpl.abi.json +381 -0
- package/dist/abi/limitOrderProtocol.json +63 -0
- package/dist/address.d.ts +13 -0
- package/dist/address.js +32 -0
- package/dist/api/filler.d.ts +109 -0
- package/dist/api/filler.js +289 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +3 -0
- package/dist/api/order-helper.d.ts +59 -0
- package/dist/api/order-helper.js +112 -0
- package/dist/api/orderbook-api.d.ts +36 -0
- package/dist/api/orderbook-api.js +69 -0
- package/dist/bps.d.ts +35 -0
- package/dist/bps.js +52 -0
- package/dist/config/chains.d.ts +50 -0
- package/dist/config/chains.js +21 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +103 -0
- package/dist/config/markets.d.ts +53 -0
- package/dist/config/markets.js +125 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +30 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +21 -0
- package/dist/limit-order/amounts.d.ts +14 -0
- package/dist/limit-order/amounts.js +19 -0
- package/dist/limit-order/eip712/domain.d.ts +10 -0
- package/dist/limit-order/eip712/domain.js +18 -0
- package/dist/limit-order/eip712/eip712.types.d.ts +23 -0
- package/dist/limit-order/eip712/eip712.types.js +1 -0
- package/dist/limit-order/eip712/index.d.ts +3 -0
- package/dist/limit-order/eip712/index.js +3 -0
- package/dist/limit-order/eip712/order-typed-data-builder.d.ts +6 -0
- package/dist/limit-order/eip712/order-typed-data-builder.js +25 -0
- package/dist/limit-order/extensions/extension-builder.d.ts +35 -0
- package/dist/limit-order/extensions/extension-builder.js +83 -0
- package/dist/limit-order/extensions/extension.d.ts +45 -0
- package/dist/limit-order/extensions/extension.js +108 -0
- package/dist/limit-order/extensions/extension.spec.d.ts +1 -0
- package/dist/limit-order/extensions/extension.spec.js +17 -0
- package/dist/limit-order/extensions/fee-taker/errors.d.ts +2 -0
- package/dist/limit-order/extensions/fee-taker/errors.js +2 -0
- package/dist/limit-order/extensions/fee-taker/fee-calculator.d.ts +40 -0
- package/dist/limit-order/extensions/fee-taker/fee-calculator.js +83 -0
- package/dist/limit-order/extensions/fee-taker/fee-calculator.spec.d.ts +1 -0
- package/dist/limit-order/extensions/fee-taker/fee-calculator.spec.js +14 -0
- package/dist/limit-order/extensions/fee-taker/fee-taker.extension.d.ts +130 -0
- package/dist/limit-order/extensions/fee-taker/fee-taker.extension.js +249 -0
- package/dist/limit-order/extensions/fee-taker/fee-taker.extension.spec.d.ts +1 -0
- package/dist/limit-order/extensions/fee-taker/fee-taker.extension.spec.js +72 -0
- package/dist/limit-order/extensions/fee-taker/fees.d.ts +19 -0
- package/dist/limit-order/extensions/fee-taker/fees.js +36 -0
- package/dist/limit-order/extensions/fee-taker/index.d.ts +7 -0
- package/dist/limit-order/extensions/fee-taker/index.js +7 -0
- package/dist/limit-order/extensions/fee-taker/integrator-fee.d.ts +13 -0
- package/dist/limit-order/extensions/fee-taker/integrator-fee.js +28 -0
- package/dist/limit-order/extensions/fee-taker/resolver-fee.d.ts +19 -0
- package/dist/limit-order/extensions/fee-taker/resolver-fee.js +28 -0
- package/dist/limit-order/extensions/fee-taker/types.d.ts +4 -0
- package/dist/limit-order/extensions/fee-taker/types.js +1 -0
- package/dist/limit-order/extensions/fee-taker/whitelist-half-address.d.ts +18 -0
- package/dist/limit-order/extensions/fee-taker/whitelist-half-address.js +26 -0
- package/dist/limit-order/extensions/index.d.ts +3 -0
- package/dist/limit-order/extensions/index.js +3 -0
- package/dist/limit-order/index.d.ts +10 -0
- package/dist/limit-order/index.js +10 -0
- package/dist/limit-order/interaction.d.ts +16 -0
- package/dist/limit-order/interaction.js +25 -0
- package/dist/limit-order/interaction.spec.d.ts +1 -0
- package/dist/limit-order/interaction.spec.js +8 -0
- package/dist/limit-order/limit-order-with-fee.d.ts +59 -0
- package/dist/limit-order/limit-order-with-fee.js +94 -0
- package/dist/limit-order/limit-order-with-fee.spec.d.ts +1 -0
- package/dist/limit-order/limit-order-with-fee.spec.js +31 -0
- package/dist/limit-order/limit-order.d.ts +63 -0
- package/dist/limit-order/limit-order.js +211 -0
- package/dist/limit-order/limit-order.spec.d.ts +1 -0
- package/dist/limit-order/limit-order.spec.js +103 -0
- package/dist/limit-order/maker-traits.d.ts +200 -0
- package/dist/limit-order/maker-traits.js +309 -0
- package/dist/limit-order/maker-traits.spec.d.ts +1 -0
- package/dist/limit-order/maker-traits.spec.js +102 -0
- package/dist/limit-order/source-track.d.ts +1 -0
- package/dist/limit-order/source-track.js +22 -0
- package/dist/limit-order/taker-traits.d.ts +141 -0
- package/dist/limit-order/taker-traits.js +207 -0
- package/dist/limit-order/types.d.ts +24 -0
- package/dist/limit-order/types.js +1 -0
- package/dist/limit-order/verification.d.ts +16 -0
- package/dist/limit-order/verification.js +108 -0
- package/dist/limit-order-contract/index.d.ts +4 -0
- package/dist/limit-order-contract/index.js +4 -0
- package/dist/limit-order-contract/limit-order-contract.d.ts +34 -0
- package/dist/limit-order-contract/limit-order-contract.js +79 -0
- package/dist/limit-order-contract/native-order-factory.d.ts +10 -0
- package/dist/limit-order-contract/native-order-factory.js +22 -0
- package/dist/limit-order-contract/native-order-impl.d.ts +10 -0
- package/dist/limit-order-contract/native-order-impl.js +24 -0
- package/dist/limit-order-contract/proxy-factory.d.ts +20 -0
- package/dist/limit-order-contract/proxy-factory.js +32 -0
- package/dist/limit-order-contract/proxy-factory.spec.d.ts +1 -0
- package/dist/limit-order-contract/proxy-factory.spec.js +16 -0
- package/dist/limit-order-contract/types.d.ts +6 -0
- package/dist/limit-order-contract/types.js +1 -0
- package/dist/ponder/client/index.d.ts +23 -0
- package/dist/ponder/client/index.js +44 -0
- package/dist/ponder/client/queries/markets.d.ts +30 -0
- package/dist/ponder/client/queries/markets.js +200 -0
- package/dist/ponder/client/queries/positions.d.ts +13 -0
- package/dist/ponder/client/queries/positions.js +406 -0
- package/dist/ponder/client/types/history.d.ts +94 -0
- package/dist/ponder/client/types/history.js +1 -0
- package/dist/ponder/client/types/index.d.ts +5 -0
- package/dist/ponder/client/types/index.js +5 -0
- package/dist/ponder/client/types/market.d.ts +45 -0
- package/dist/ponder/client/types/market.js +1 -0
- package/dist/ponder/client/types/position.d.ts +32 -0
- package/dist/ponder/client/types/position.js +1 -0
- package/dist/ponder/client/types/serializers.d.ts +57 -0
- package/dist/ponder/client/types/serializers.js +248 -0
- package/dist/ponder/client/types/user.d.ts +5 -0
- package/dist/ponder/client/types/user.js +1 -0
- package/dist/ponder/client/utils.d.ts +1 -0
- package/dist/ponder/client/utils.js +32 -0
- package/dist/ponder/generated/index.d.ts +18 -0
- package/dist/ponder/generated/index.js +20 -0
- package/dist/ponder/generated/runtime/batcher.d.ts +105 -0
- package/dist/ponder/generated/runtime/batcher.js +188 -0
- package/dist/ponder/generated/runtime/createClient.d.ts +17 -0
- package/dist/ponder/generated/runtime/createClient.js +24 -0
- package/dist/ponder/generated/runtime/error.d.ts +18 -0
- package/dist/ponder/generated/runtime/error.js +15 -0
- package/dist/ponder/generated/runtime/fetcher.d.ts +10 -0
- package/dist/ponder/generated/runtime/fetcher.js +67 -0
- package/dist/ponder/generated/runtime/generateGraphqlOperation.d.ts +30 -0
- package/dist/ponder/generated/runtime/generateGraphqlOperation.js +128 -0
- package/dist/ponder/generated/runtime/index.d.ts +11 -0
- package/dist/ponder/generated/runtime/index.js +10 -0
- package/dist/ponder/generated/runtime/linkTypeMap.d.ts +9 -0
- package/dist/ponder/generated/runtime/linkTypeMap.js +83 -0
- package/dist/ponder/generated/runtime/typeSelection.d.ts +28 -0
- package/dist/ponder/generated/runtime/typeSelection.js +3 -0
- package/dist/ponder/generated/runtime/types.d.ts +55 -0
- package/dist/ponder/generated/runtime/types.js +2 -0
- package/dist/ponder/generated/schema.d.ts +3026 -0
- package/dist/ponder/generated/schema.js +222 -0
- package/dist/ponder/generated/types.d.ts +2393 -0
- package/dist/ponder/generated/types.js +6915 -0
- package/dist/ponder/index.d.ts +2 -0
- package/dist/ponder/index.js +2 -0
- package/dist/ponder/types.d.ts +3 -0
- package/dist/ponder/types.js +1 -0
- package/dist/rfq-order/index.d.ts +1 -0
- package/dist/rfq-order/index.js +1 -0
- package/dist/rfq-order/rfq-order.d.ts +21 -0
- package/dist/rfq-order/rfq-order.js +22 -0
- package/dist/rfq-order/rfq-order.spec.d.ts +1 -0
- package/dist/rfq-order/rfq-order.spec.js +27 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/types.d.ts +79 -0
- package/dist/shared/types.js +12 -0
- package/dist/shared/utils.d.ts +7 -0
- package/dist/shared/utils.js +19 -0
- package/dist/sync/clients/balance-client.d.ts +41 -0
- package/dist/sync/clients/balance-client.js +139 -0
- package/dist/sync/clients/base-client.d.ts +47 -0
- package/dist/sync/clients/base-client.js +154 -0
- package/dist/sync/clients/order-client.d.ts +18 -0
- package/dist/sync/clients/order-client.js +151 -0
- package/dist/sync/index.d.ts +5 -0
- package/dist/sync/index.js +3 -0
- package/dist/sync/redis-ws-client.d.ts +18 -0
- package/dist/sync/redis-ws-client.js +88 -0
- package/dist/sync/types.d.ts +20 -0
- package/dist/sync/types.js +1 -0
- package/dist/utils/mul-div.d.ts +5 -0
- package/dist/utils/mul-div.js +13 -0
- package/dist/utils/orderUtils.d.ts +19 -0
- package/dist/utils/orderUtils.js +51 -0
- package/dist/utils/rand-bigint.d.ts +1 -0
- package/dist/utils/rand-bigint.js +13 -0
- package/dist/utils/rand-bigint.spec.d.ts +1 -0
- package/dist/utils/rand-bigint.spec.js +11 -0
- package/dist/validations.d.ts +1 -0
- package/dist/validations.js +3 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rfq-order.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rfq-order.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { LimitOrder, OrderInfoData } from '../limit-order/index.js';
|
|
2
|
+
import { Address } from '../address.js';
|
|
3
|
+
/**
|
|
4
|
+
* Light, gas efficient version of LimitOrder
|
|
5
|
+
* It does not support multiple fills and extension
|
|
6
|
+
*/
|
|
7
|
+
export declare class RfqOrder extends LimitOrder {
|
|
8
|
+
constructor(orderInfo: Omit<OrderInfoData, 'salt' | 'receiver'>, options: {
|
|
9
|
+
allowedSender?: Address;
|
|
10
|
+
/**
|
|
11
|
+
* Timestamp in seconds
|
|
12
|
+
*/
|
|
13
|
+
expiration: bigint;
|
|
14
|
+
/**
|
|
15
|
+
* Unique id among all maker orders
|
|
16
|
+
* Max value is UINT_40_MAX
|
|
17
|
+
*/
|
|
18
|
+
nonce: bigint;
|
|
19
|
+
usePermit2?: boolean;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { LimitOrder, MakerTraits } from '../limit-order/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Light, gas efficient version of LimitOrder
|
|
4
|
+
* It does not support multiple fills and extension
|
|
5
|
+
*/
|
|
6
|
+
export class RfqOrder extends LimitOrder {
|
|
7
|
+
constructor(orderInfo, options) {
|
|
8
|
+
const { allowedSender, nonce, expiration, usePermit2 } = options;
|
|
9
|
+
const makerTraits = new MakerTraits(0n)
|
|
10
|
+
.disableMultipleFills()
|
|
11
|
+
.allowPartialFills()
|
|
12
|
+
.withExpiration(expiration)
|
|
13
|
+
.withNonce(nonce);
|
|
14
|
+
if (allowedSender) {
|
|
15
|
+
makerTraits.withAllowedSender(allowedSender);
|
|
16
|
+
}
|
|
17
|
+
if (usePermit2) {
|
|
18
|
+
makerTraits.enablePermit2();
|
|
19
|
+
}
|
|
20
|
+
super(orderInfo, makerTraits);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { RfqOrder } from './rfq-order.js';
|
|
2
|
+
import { Address } from '../address.js';
|
|
3
|
+
describe('RfqOrder', () => {
|
|
4
|
+
it('Should validate max nonce', () => {
|
|
5
|
+
expect(() => new RfqOrder({
|
|
6
|
+
makerAsset: new Address('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
|
7
|
+
takerAsset: new Address('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'),
|
|
8
|
+
makingAmount: 1000000000000000000n,
|
|
9
|
+
takingAmount: 1420000000n,
|
|
10
|
+
maker: new Address('0x00000000219ab540356cbb839cbe05303d7705fa')
|
|
11
|
+
}, { nonce: 1n << 41n, expiration: 1000n })).toThrow();
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
it('should create RfqOrder with permit2', () => {
|
|
15
|
+
const order = new RfqOrder({
|
|
16
|
+
makerAsset: new Address('0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'),
|
|
17
|
+
takerAsset: new Address('0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'),
|
|
18
|
+
makingAmount: 1000000000000000000n,
|
|
19
|
+
takingAmount: 1420000000n,
|
|
20
|
+
maker: new Address('0x00000000219ab540356cbb839cbe05303d7705fa')
|
|
21
|
+
}, {
|
|
22
|
+
nonce: 1n,
|
|
23
|
+
expiration: 1000n,
|
|
24
|
+
usePermit2: true
|
|
25
|
+
});
|
|
26
|
+
expect(order.makerTraits.isPermit2()).toEqual(true);
|
|
27
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface Order {
|
|
2
|
+
salt: string;
|
|
3
|
+
maker: string;
|
|
4
|
+
receiver: string;
|
|
5
|
+
makerAsset: string;
|
|
6
|
+
takerAsset: string;
|
|
7
|
+
makingAmount: string;
|
|
8
|
+
takingAmount: string;
|
|
9
|
+
makerTraits: string;
|
|
10
|
+
}
|
|
11
|
+
export interface OrderSignature {
|
|
12
|
+
r: string;
|
|
13
|
+
vs: string;
|
|
14
|
+
}
|
|
15
|
+
export interface Option {
|
|
16
|
+
marketId: string;
|
|
17
|
+
strikeLowerLimit: string;
|
|
18
|
+
strikeUpperLimit: string;
|
|
19
|
+
isPut: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare enum OrderType {
|
|
22
|
+
SELL_OPTIONS = "SELL_OPTIONS",
|
|
23
|
+
BUY_OPTIONS = "BUY_OPTIONS"
|
|
24
|
+
}
|
|
25
|
+
export declare enum OrderStatus {
|
|
26
|
+
ACTIVE = "ACTIVE",
|
|
27
|
+
FILLED = "FILLED",
|
|
28
|
+
CANCELLED = "CANCELLED",
|
|
29
|
+
EXPIRED = "EXPIRED"
|
|
30
|
+
}
|
|
31
|
+
export interface StoredOrder {
|
|
32
|
+
id: string;
|
|
33
|
+
orderHash: string;
|
|
34
|
+
orderType: OrderType;
|
|
35
|
+
order: Order;
|
|
36
|
+
extensionEncoded: string;
|
|
37
|
+
signature: OrderSignature;
|
|
38
|
+
marketId: string;
|
|
39
|
+
optionTokenId: string;
|
|
40
|
+
stableToken: string;
|
|
41
|
+
optionAmount: string;
|
|
42
|
+
stableAmount: string;
|
|
43
|
+
status: OrderStatus;
|
|
44
|
+
filledAmount: string;
|
|
45
|
+
remainingMakerAmount: string;
|
|
46
|
+
maker: string;
|
|
47
|
+
operator?: string;
|
|
48
|
+
createdAt: number;
|
|
49
|
+
expiresAt?: number;
|
|
50
|
+
}
|
|
51
|
+
export interface CreateOrderParams {
|
|
52
|
+
orderType: OrderType;
|
|
53
|
+
marketId: string;
|
|
54
|
+
option: Option;
|
|
55
|
+
order: Order;
|
|
56
|
+
extensionEncoded: string;
|
|
57
|
+
signature: OrderSignature;
|
|
58
|
+
optionAmount: string;
|
|
59
|
+
stableAmount: string;
|
|
60
|
+
stableToken: string;
|
|
61
|
+
makerProxyAddress: string;
|
|
62
|
+
operator?: string;
|
|
63
|
+
expiresAt?: number;
|
|
64
|
+
}
|
|
65
|
+
export type CreateOrderRequest = CreateOrderParams;
|
|
66
|
+
export interface OrderQueryParams {
|
|
67
|
+
marketId?: string;
|
|
68
|
+
orderType?: OrderType;
|
|
69
|
+
maker?: string;
|
|
70
|
+
stableToken?: string;
|
|
71
|
+
status?: OrderStatus;
|
|
72
|
+
limit?: number;
|
|
73
|
+
offset?: number;
|
|
74
|
+
}
|
|
75
|
+
export interface OrderResponse {
|
|
76
|
+
success: boolean;
|
|
77
|
+
data?: any;
|
|
78
|
+
error?: string;
|
|
79
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export var OrderType;
|
|
2
|
+
(function (OrderType) {
|
|
3
|
+
OrderType["SELL_OPTIONS"] = "SELL_OPTIONS";
|
|
4
|
+
OrderType["BUY_OPTIONS"] = "BUY_OPTIONS";
|
|
5
|
+
})(OrderType || (OrderType = {}));
|
|
6
|
+
export var OrderStatus;
|
|
7
|
+
(function (OrderStatus) {
|
|
8
|
+
OrderStatus["ACTIVE"] = "ACTIVE";
|
|
9
|
+
OrderStatus["FILLED"] = "FILLED";
|
|
10
|
+
OrderStatus["CANCELLED"] = "CANCELLED";
|
|
11
|
+
OrderStatus["EXPIRED"] = "EXPIRED";
|
|
12
|
+
})(OrderStatus || (OrderStatus = {}));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Hex } from "viem";
|
|
2
|
+
import type { Option } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Calculate option token ID from option parameters
|
|
5
|
+
* This should match the getTokenId function in OptionTokenFactory
|
|
6
|
+
*/
|
|
7
|
+
export declare function calculateOptionTokenId(option: Option): Hex;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { encodeAbiParameters, keccak256 } from "viem";
|
|
2
|
+
/**
|
|
3
|
+
* Calculate option token ID from option parameters
|
|
4
|
+
* This should match the getTokenId function in OptionTokenFactory
|
|
5
|
+
*/
|
|
6
|
+
export function calculateOptionTokenId(option) {
|
|
7
|
+
const encoded = encodeAbiParameters([
|
|
8
|
+
{ name: "marketId", type: "uint256" },
|
|
9
|
+
{ name: "strikeLowerLimit", type: "uint256" },
|
|
10
|
+
{ name: "strikeUpperLimit", type: "uint256" },
|
|
11
|
+
{ name: "isPut", type: "bool" },
|
|
12
|
+
], [
|
|
13
|
+
BigInt(option.marketId),
|
|
14
|
+
BigInt(option.strikeLowerLimit),
|
|
15
|
+
BigInt(option.strikeUpperLimit),
|
|
16
|
+
option.isPut,
|
|
17
|
+
]);
|
|
18
|
+
return keccak256(encoded);
|
|
19
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { BaseSyncClient } from "./base-client.js";
|
|
2
|
+
export interface BalanceData {
|
|
3
|
+
maker: string;
|
|
4
|
+
token: string;
|
|
5
|
+
usableBalance: string;
|
|
6
|
+
tokenId?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface BalanceUpdateMessage {
|
|
9
|
+
type: "balanceUpdate";
|
|
10
|
+
seq: number;
|
|
11
|
+
marketId: string;
|
|
12
|
+
balances: BalanceData[];
|
|
13
|
+
timestamp: number;
|
|
14
|
+
}
|
|
15
|
+
export interface BalanceSyncClientConfig {
|
|
16
|
+
redisUrl: string;
|
|
17
|
+
marketId: string;
|
|
18
|
+
channel: string;
|
|
19
|
+
gapRecoveryUrl?: string;
|
|
20
|
+
snapshotUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare class BalanceSyncClient extends BaseSyncClient<BalanceUpdateMessage, BalanceData[], BalanceData, BalanceSyncClientConfig> {
|
|
23
|
+
private balanceListeners;
|
|
24
|
+
constructor(config: Omit<BalanceSyncClientConfig, "channel">);
|
|
25
|
+
protected fetchSnapshot(): Promise<void>;
|
|
26
|
+
protected applyMessage(message: BalanceUpdateMessage): void;
|
|
27
|
+
protected recoverGap(fromSeq: number, toSeq: number): Promise<BalanceUpdateMessage[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Get usable balance for a maker/token pair (or maker/token/tokenId for ERC6909)
|
|
30
|
+
*/
|
|
31
|
+
getUsableBalance(maker: string, token: string, tokenId?: string | bigint): string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Get all balances as a map of key -> balance string
|
|
34
|
+
*/
|
|
35
|
+
getBalanceMap(): Map<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Get all balances as BalanceData array
|
|
38
|
+
*/
|
|
39
|
+
getBalances(): BalanceData[];
|
|
40
|
+
onBalanceUpdate(callback: (balances: BalanceData[]) => void): () => void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { BaseSyncClient } from "./base-client.js";
|
|
2
|
+
export class BalanceSyncClient extends BaseSyncClient {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
// Build full config with channel
|
|
5
|
+
const fullConfig = {
|
|
6
|
+
...config,
|
|
7
|
+
channel: `orderbook:market:${config.marketId}:balances`,
|
|
8
|
+
};
|
|
9
|
+
super(fullConfig);
|
|
10
|
+
this.balanceListeners = new Set();
|
|
11
|
+
}
|
|
12
|
+
async fetchSnapshot() {
|
|
13
|
+
try {
|
|
14
|
+
// Use snapshotUrl if available, otherwise fall back to gapRecoveryUrl
|
|
15
|
+
const baseUrl = this.config.snapshotUrl || this.config.gapRecoveryUrl;
|
|
16
|
+
if (!baseUrl) {
|
|
17
|
+
// If no URL, start from 0
|
|
18
|
+
this.lastSeq = 0;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const url = `${baseUrl}/api/sync/balance-snapshot?marketId=${this.config.marketId}`;
|
|
22
|
+
const response = await fetch(url);
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
throw new Error(`Balance snapshot fetch failed: ${response.status} ${response.statusText}`);
|
|
25
|
+
}
|
|
26
|
+
const data = (await response.json());
|
|
27
|
+
if (data.success && data.data) {
|
|
28
|
+
const balances = data.data.balances || [];
|
|
29
|
+
const snapshotSeq = data.data.seq || 0;
|
|
30
|
+
// Clear and populate balance map
|
|
31
|
+
this.dataMap.clear();
|
|
32
|
+
for (const balance of balances) {
|
|
33
|
+
const key = balance.tokenId
|
|
34
|
+
? `${balance.maker.toLowerCase()}:${balance.token.toLowerCase()}:${balance.tokenId}`
|
|
35
|
+
: `${balance.maker.toLowerCase()}:${balance.token.toLowerCase()}`;
|
|
36
|
+
this.dataMap.set(key, balance);
|
|
37
|
+
}
|
|
38
|
+
this.lastSeq = snapshotSeq;
|
|
39
|
+
// Notify snapshot listeners
|
|
40
|
+
this.notifySnapshotListeners(balances);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("Error fetching balance snapshot:", error);
|
|
45
|
+
// Fallback to starting from 0
|
|
46
|
+
this.lastSeq = 0;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
applyMessage(message) {
|
|
50
|
+
// Update balance map
|
|
51
|
+
for (const balance of message.balances) {
|
|
52
|
+
// For ERC6909 tokens, include tokenId in the key
|
|
53
|
+
const key = balance.tokenId
|
|
54
|
+
? `${balance.maker.toLowerCase()}:${balance.token.toLowerCase()}:${balance.tokenId}`
|
|
55
|
+
: `${balance.maker.toLowerCase()}:${balance.token.toLowerCase()}`;
|
|
56
|
+
this.dataMap.set(key, balance);
|
|
57
|
+
}
|
|
58
|
+
// Notify change listeners (from base class)
|
|
59
|
+
this.changeListeners.forEach((listener) => {
|
|
60
|
+
try {
|
|
61
|
+
listener(message.balances);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error("Error in change listener:", error);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
// Notify balance-specific listeners
|
|
68
|
+
this.balanceListeners.forEach((listener) => {
|
|
69
|
+
try {
|
|
70
|
+
listener(message.balances);
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
console.error("Error in balance listener:", error);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async recoverGap(fromSeq, toSeq) {
|
|
78
|
+
if (!this.config.gapRecoveryUrl) {
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
const gapSize = toSeq - fromSeq + 1;
|
|
82
|
+
if (gapSize > 1000) {
|
|
83
|
+
await this.fullResync();
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
this.setStatus("recovering");
|
|
87
|
+
try {
|
|
88
|
+
const url = `${this.config.gapRecoveryUrl}/api/sync/balance-messages?marketId=${this.config.marketId}&fromSeq=${fromSeq}&toSeq=${toSeq}`;
|
|
89
|
+
const response = await fetch(url);
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(`Gap recovery failed: ${response.status} ${response.statusText}`);
|
|
92
|
+
}
|
|
93
|
+
const data = (await response.json());
|
|
94
|
+
if (data.success && data.data) {
|
|
95
|
+
const messages = data.data;
|
|
96
|
+
messages.sort((a, b) => a.seq - b.seq);
|
|
97
|
+
this.setStatus("synced");
|
|
98
|
+
return messages;
|
|
99
|
+
}
|
|
100
|
+
this.setStatus("synced");
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("Error recovering gap:", error);
|
|
105
|
+
await this.fullResync();
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get usable balance for a maker/token pair (or maker/token/tokenId for ERC6909)
|
|
111
|
+
*/
|
|
112
|
+
getUsableBalance(maker, token, tokenId) {
|
|
113
|
+
const key = tokenId
|
|
114
|
+
? `${maker.toLowerCase()}:${token.toLowerCase()}:${tokenId.toString()}`
|
|
115
|
+
: `${maker.toLowerCase()}:${token.toLowerCase()}`;
|
|
116
|
+
const balanceData = this.dataMap.get(key);
|
|
117
|
+
return balanceData?.usableBalance;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get all balances as a map of key -> balance string
|
|
121
|
+
*/
|
|
122
|
+
getBalanceMap() {
|
|
123
|
+
const map = new Map();
|
|
124
|
+
for (const [key, balanceData] of this.dataMap.entries()) {
|
|
125
|
+
map.set(key, balanceData.usableBalance);
|
|
126
|
+
}
|
|
127
|
+
return map;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get all balances as BalanceData array
|
|
131
|
+
*/
|
|
132
|
+
getBalances() {
|
|
133
|
+
return Array.from(this.dataMap.values());
|
|
134
|
+
}
|
|
135
|
+
onBalanceUpdate(callback) {
|
|
136
|
+
this.balanceListeners.add(callback);
|
|
137
|
+
return () => this.balanceListeners.delete(callback);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SyncStatus } from "../types.js";
|
|
2
|
+
import { RedisWsClient } from "../redis-ws-client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Base abstract class for sync clients
|
|
5
|
+
* @template TMessage - The message type (e.g., SequencedMessage, BalanceUpdateMessage)
|
|
6
|
+
* @template TChange - The change type (e.g., OrderChange, BalanceData[])
|
|
7
|
+
* @template TData - The data type stored in the map (e.g., StoredOrder, string)
|
|
8
|
+
* @template TConfig - The config type (must have redisUrl and channel)
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class BaseSyncClient<TMessage extends {
|
|
11
|
+
seq: number;
|
|
12
|
+
}, TChange, TData, TConfig extends {
|
|
13
|
+
redisUrl: string;
|
|
14
|
+
channel: string;
|
|
15
|
+
}> {
|
|
16
|
+
protected wsClient: RedisWsClient | null;
|
|
17
|
+
protected config: TConfig;
|
|
18
|
+
protected status: SyncStatus;
|
|
19
|
+
protected lastSeq: number;
|
|
20
|
+
protected incomingQueue: TMessage[];
|
|
21
|
+
protected isProcessing: boolean;
|
|
22
|
+
protected statusListeners: Set<(status: SyncStatus) => void>;
|
|
23
|
+
protected changeListeners: Set<(change: TChange) => void>;
|
|
24
|
+
protected dataMap: Map<string, TData>;
|
|
25
|
+
protected snapshotListeners: Set<(data: TData[]) => void>;
|
|
26
|
+
constructor(config: TConfig);
|
|
27
|
+
connect(): Promise<void>;
|
|
28
|
+
protected enqueueMessage(message: TMessage): void;
|
|
29
|
+
protected processQueue(): Promise<void>;
|
|
30
|
+
protected fullResync(): Promise<void>;
|
|
31
|
+
protected setStatus(status: SyncStatus): void;
|
|
32
|
+
getStatus(): SyncStatus;
|
|
33
|
+
isSynced(): boolean;
|
|
34
|
+
getLastSequence(): number;
|
|
35
|
+
getBufferedCount(): number;
|
|
36
|
+
onStatus(callback: (status: SyncStatus) => void): () => void;
|
|
37
|
+
onChange(callback: (change: TChange) => void): () => void;
|
|
38
|
+
onSnapshot(callback: (data: TData[]) => void): () => void;
|
|
39
|
+
protected notifySnapshotListeners(data: TData[]): void;
|
|
40
|
+
disconnect(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Abstract methods to be implemented by child classes
|
|
43
|
+
*/
|
|
44
|
+
protected abstract fetchSnapshot(): Promise<void>;
|
|
45
|
+
protected abstract applyMessage(message: TMessage): Promise<void> | void;
|
|
46
|
+
protected abstract recoverGap(fromSeq: number, toSeq: number): Promise<TMessage[]>;
|
|
47
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { RedisWsClient } from "../redis-ws-client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Base abstract class for sync clients
|
|
4
|
+
* @template TMessage - The message type (e.g., SequencedMessage, BalanceUpdateMessage)
|
|
5
|
+
* @template TChange - The change type (e.g., OrderChange, BalanceData[])
|
|
6
|
+
* @template TData - The data type stored in the map (e.g., StoredOrder, string)
|
|
7
|
+
* @template TConfig - The config type (must have redisUrl and channel)
|
|
8
|
+
*/
|
|
9
|
+
export class BaseSyncClient {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.wsClient = null;
|
|
12
|
+
this.status = "disconnected";
|
|
13
|
+
this.lastSeq = 0;
|
|
14
|
+
this.incomingQueue = [];
|
|
15
|
+
this.isProcessing = false;
|
|
16
|
+
this.statusListeners = new Set();
|
|
17
|
+
this.changeListeners = new Set();
|
|
18
|
+
this.dataMap = new Map();
|
|
19
|
+
this.snapshotListeners = new Set();
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
this.setStatus("connecting");
|
|
24
|
+
// Only allow ws:// or wss:// URLs
|
|
25
|
+
const wsUrl = this.config.redisUrl;
|
|
26
|
+
if (!wsUrl.startsWith("ws://") && !wsUrl.startsWith("wss://")) {
|
|
27
|
+
throw new Error(`Invalid WebSocket URL: ${wsUrl}. Only ws:// and wss:// URLs are supported.`);
|
|
28
|
+
}
|
|
29
|
+
this.wsClient = new RedisWsClient(wsUrl);
|
|
30
|
+
this.wsClient.subscribe(this.config.channel, (messageStr) => {
|
|
31
|
+
try {
|
|
32
|
+
const message = JSON.parse(messageStr);
|
|
33
|
+
this.enqueueMessage(message);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error("Error parsing message:", error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
this.setStatus("syncing");
|
|
40
|
+
await this.fetchSnapshot();
|
|
41
|
+
this.setStatus("synced");
|
|
42
|
+
}
|
|
43
|
+
enqueueMessage(message) {
|
|
44
|
+
this.incomingQueue.push(message);
|
|
45
|
+
this.processQueue();
|
|
46
|
+
}
|
|
47
|
+
async processQueue() {
|
|
48
|
+
if (this.isProcessing)
|
|
49
|
+
return;
|
|
50
|
+
this.isProcessing = true;
|
|
51
|
+
try {
|
|
52
|
+
while (this.incomingQueue.length > 0) {
|
|
53
|
+
const message = this.incomingQueue.shift();
|
|
54
|
+
if (message.seq <= this.lastSeq) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const expectedSeq = this.lastSeq + 1;
|
|
58
|
+
if (message.seq === expectedSeq) {
|
|
59
|
+
await this.applyMessage(message);
|
|
60
|
+
this.lastSeq = message.seq;
|
|
61
|
+
}
|
|
62
|
+
else if (message.seq > expectedSeq) {
|
|
63
|
+
const recovered = await this.recoverGap(expectedSeq, message.seq - 1);
|
|
64
|
+
for (const msg of recovered) {
|
|
65
|
+
if (msg.seq === this.lastSeq + 1) {
|
|
66
|
+
await this.applyMessage(msg);
|
|
67
|
+
this.lastSeq = msg.seq;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (message.seq === this.lastSeq + 1) {
|
|
71
|
+
await this.applyMessage(message);
|
|
72
|
+
this.lastSeq = message.seq;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
this.incomingQueue.unshift(message);
|
|
76
|
+
await this.recoverGap(this.lastSeq + 1, message.seq - 1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.isProcessing = false;
|
|
83
|
+
if (this.incomingQueue.length > 0) {
|
|
84
|
+
setImmediate(() => this.processQueue());
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async fullResync() {
|
|
89
|
+
this.isProcessing = true;
|
|
90
|
+
try {
|
|
91
|
+
this.incomingQueue = [];
|
|
92
|
+
this.lastSeq = 0;
|
|
93
|
+
await this.fetchSnapshot();
|
|
94
|
+
this.setStatus("synced");
|
|
95
|
+
}
|
|
96
|
+
finally {
|
|
97
|
+
this.isProcessing = false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
setStatus(status) {
|
|
101
|
+
if (this.status === status)
|
|
102
|
+
return;
|
|
103
|
+
this.status = status;
|
|
104
|
+
this.statusListeners.forEach((listener) => {
|
|
105
|
+
try {
|
|
106
|
+
listener(status);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
console.error("Error in status listener:", error);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
getStatus() {
|
|
114
|
+
return this.status;
|
|
115
|
+
}
|
|
116
|
+
isSynced() {
|
|
117
|
+
return this.status === "synced";
|
|
118
|
+
}
|
|
119
|
+
getLastSequence() {
|
|
120
|
+
return this.lastSeq;
|
|
121
|
+
}
|
|
122
|
+
getBufferedCount() {
|
|
123
|
+
return this.incomingQueue.length;
|
|
124
|
+
}
|
|
125
|
+
onStatus(callback) {
|
|
126
|
+
this.statusListeners.add(callback);
|
|
127
|
+
return () => this.statusListeners.delete(callback);
|
|
128
|
+
}
|
|
129
|
+
onChange(callback) {
|
|
130
|
+
this.changeListeners.add(callback);
|
|
131
|
+
return () => this.changeListeners.delete(callback);
|
|
132
|
+
}
|
|
133
|
+
onSnapshot(callback) {
|
|
134
|
+
this.snapshotListeners.add(callback);
|
|
135
|
+
return () => this.snapshotListeners.delete(callback);
|
|
136
|
+
}
|
|
137
|
+
notifySnapshotListeners(data) {
|
|
138
|
+
this.snapshotListeners.forEach((listener) => {
|
|
139
|
+
try {
|
|
140
|
+
listener(data);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error("Error in snapshot listener:", error);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async disconnect() {
|
|
148
|
+
if (this.wsClient) {
|
|
149
|
+
this.wsClient.close();
|
|
150
|
+
this.wsClient = null;
|
|
151
|
+
}
|
|
152
|
+
this.setStatus("disconnected");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type StoredOrder } from "../../shared/types.js";
|
|
2
|
+
import type { OrderChange, SequencedMessage, SyncClientConfig } from "../types.js";
|
|
3
|
+
import { BaseSyncClient } from "./base-client.js";
|
|
4
|
+
export interface OrderbookSyncClientConfig extends SyncClientConfig {
|
|
5
|
+
channel: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class OrderbookSyncClient extends BaseSyncClient<SequencedMessage, OrderChange, StoredOrder, OrderbookSyncClientConfig> {
|
|
8
|
+
private expireCheckInterval;
|
|
9
|
+
constructor(config: SyncClientConfig);
|
|
10
|
+
connect(): Promise<void>;
|
|
11
|
+
protected fetchSnapshot(): Promise<void>;
|
|
12
|
+
protected applyMessage(message: SequencedMessage): void;
|
|
13
|
+
protected recoverGap(fromSeq: number, toSeq: number): Promise<SequencedMessage[]>;
|
|
14
|
+
getOrders(): StoredOrder[];
|
|
15
|
+
getOrder(orderHash: string): StoredOrder | undefined;
|
|
16
|
+
private startExpireCheck;
|
|
17
|
+
disconnect(): Promise<void>;
|
|
18
|
+
}
|