@drift-labs/sdk 2.49.0-beta.16 → 2.49.0-beta.18
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/VERSION +1 -1
- package/lib/adminClient.d.ts +1 -0
- package/lib/adminClient.js +8 -0
- package/lib/decode/user.d.ts +3 -0
- package/lib/decode/user.js +328 -0
- package/lib/idl/drift.json +31 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/math/state.d.ts +5 -0
- package/lib/math/state.js +27 -0
- package/lib/orderSubscriber/OrderSubscriber.d.ts +3 -0
- package/lib/orderSubscriber/OrderSubscriber.js +10 -2
- package/lib/orderSubscriber/WebsocketSubscription.js +1 -1
- package/lib/orderSubscriber/types.d.ts +1 -0
- package/lib/types.d.ts +1 -1
- package/lib/user.d.ts +1 -1
- package/lib/user.js +7 -2
- package/lib/userMap/PollingSubscription.js +3 -1
- package/lib/userMap/WebsocketSubscription.d.ts +5 -1
- package/lib/userMap/WebsocketSubscription.js +3 -2
- package/lib/userMap/userMap.d.ts +1 -0
- package/lib/userMap/userMap.js +14 -6
- package/lib/userMap/userMapConfig.d.ts +1 -0
- package/package.json +2 -1
- package/src/adminClient.ts +14 -0
- package/src/decode/user.ts +357 -0
- package/src/idl/drift.json +32 -2
- package/src/index.ts +1 -0
- package/src/math/state.ts +26 -0
- package/src/orderSubscriber/OrderSubscriber.ts +11 -5
- package/src/orderSubscriber/WebsocketSubscription.ts +1 -3
- package/src/orderSubscriber/types.ts +1 -0
- package/src/types.ts +1 -1
- package/src/user.ts +12 -2
- package/src/userMap/PollingSubscription.ts +6 -4
- package/src/userMap/WebsocketSubscription.ts +5 -3
- package/src/userMap/userMap.ts +17 -12
- package/src/userMap/userMapConfig.ts +3 -0
- package/tests/decode/test.ts +266 -0
- package/tests/decode/userAccountBufferStrings.ts +102 -0
- package/tests/dlob/helpers.ts +1 -0
- package/tests/dlob/test.ts +4 -6
- package/tests/user/helpers.ts +0 -1
|
@@ -10,6 +10,7 @@ export class WebsocketSubscription {
|
|
|
10
10
|
private skipInitialLoad: boolean;
|
|
11
11
|
private resubTimeoutMs?: number;
|
|
12
12
|
private includeIdle?: boolean;
|
|
13
|
+
private decodeFn: (name: string, data: Buffer) => UserAccount;
|
|
13
14
|
|
|
14
15
|
private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
|
|
15
16
|
|
|
@@ -19,18 +20,21 @@ export class WebsocketSubscription {
|
|
|
19
20
|
skipInitialLoad = false,
|
|
20
21
|
resubTimeoutMs,
|
|
21
22
|
includeIdle = false,
|
|
23
|
+
decodeFn,
|
|
22
24
|
}: {
|
|
23
25
|
userMap: UserMap;
|
|
24
26
|
commitment: Commitment;
|
|
25
27
|
skipInitialLoad?: boolean;
|
|
26
28
|
resubTimeoutMs?: number;
|
|
27
29
|
includeIdle?: boolean;
|
|
30
|
+
decodeFn: (name: string, data: Buffer) => UserAccount;
|
|
28
31
|
}) {
|
|
29
32
|
this.userMap = userMap;
|
|
30
33
|
this.commitment = commitment;
|
|
31
34
|
this.skipInitialLoad = skipInitialLoad;
|
|
32
35
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
33
36
|
this.includeIdle = includeIdle || false;
|
|
37
|
+
this.decodeFn = decodeFn;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
public async subscribe(): Promise<void> {
|
|
@@ -43,9 +47,7 @@ export class WebsocketSubscription {
|
|
|
43
47
|
'UserMap',
|
|
44
48
|
'User',
|
|
45
49
|
this.userMap.driftClient.program,
|
|
46
|
-
this.
|
|
47
|
-
this.userMap.driftClient.program.account.user.coder.accounts
|
|
48
|
-
),
|
|
50
|
+
this.decodeFn,
|
|
49
51
|
{
|
|
50
52
|
filters,
|
|
51
53
|
commitment: this.commitment,
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from './userMapConfig';
|
|
32
32
|
import { WebsocketSubscription } from './WebsocketSubscription';
|
|
33
33
|
import { PollingSubscription } from './PollingSubscription';
|
|
34
|
+
import { decodeUser } from '../decode/user';
|
|
34
35
|
|
|
35
36
|
export interface UserMapInterface {
|
|
36
37
|
subscribe(): Promise<void>;
|
|
@@ -58,6 +59,7 @@ export class UserMap implements UserMapInterface {
|
|
|
58
59
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
59
60
|
}
|
|
60
61
|
};
|
|
62
|
+
private decode;
|
|
61
63
|
|
|
62
64
|
private syncPromise?: Promise<void>;
|
|
63
65
|
private syncPromiseResolver: () => void;
|
|
@@ -75,6 +77,18 @@ export class UserMap implements UserMapInterface {
|
|
|
75
77
|
this.commitment =
|
|
76
78
|
config.subscriptionConfig.commitment ?? this.driftClient.opts.commitment;
|
|
77
79
|
this.includeIdle = config.includeIdle ?? false;
|
|
80
|
+
|
|
81
|
+
let decodeFn;
|
|
82
|
+
if (config.fastDecode ?? true) {
|
|
83
|
+
decodeFn = (name, buffer) => decodeUser(buffer);
|
|
84
|
+
} else {
|
|
85
|
+
decodeFn =
|
|
86
|
+
this.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(
|
|
87
|
+
this.driftClient.program.account.user.coder.accounts
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
this.decode = decodeFn;
|
|
91
|
+
|
|
78
92
|
if (config.subscriptionConfig.type === 'polling') {
|
|
79
93
|
this.subscription = new PollingSubscription({
|
|
80
94
|
userMap: this,
|
|
@@ -87,6 +101,7 @@ export class UserMap implements UserMapInterface {
|
|
|
87
101
|
commitment: this.commitment,
|
|
88
102
|
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
89
103
|
skipInitialLoad: config.skipInitialLoad,
|
|
104
|
+
decodeFn,
|
|
90
105
|
});
|
|
91
106
|
}
|
|
92
107
|
}
|
|
@@ -309,12 +324,9 @@ export class UserMap implements UserMapInterface {
|
|
|
309
324
|
|
|
310
325
|
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
311
326
|
if (!this.has(key)) {
|
|
312
|
-
const userAccount =
|
|
313
|
-
this.driftClient.program.account.user.coder.accounts.decodeUnchecked(
|
|
314
|
-
'User',
|
|
315
|
-
buffer
|
|
316
|
-
);
|
|
327
|
+
const userAccount = this.decode('User', buffer);
|
|
317
328
|
await this.addPubkey(new PublicKey(key), userAccount);
|
|
329
|
+
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
318
330
|
}
|
|
319
331
|
// give event loop a chance to breathe
|
|
320
332
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -324,13 +336,6 @@ export class UserMap implements UserMapInterface {
|
|
|
324
336
|
if (!programAccountBufferMap.has(key)) {
|
|
325
337
|
await user.unsubscribe();
|
|
326
338
|
this.userMap.delete(key);
|
|
327
|
-
} else {
|
|
328
|
-
const userAccount =
|
|
329
|
-
this.driftClient.program.account.user.coder.accounts.decodeUnchecked(
|
|
330
|
-
'User',
|
|
331
|
-
programAccountBufferMap.get(key)
|
|
332
|
-
);
|
|
333
|
-
user.accountSubscriber.updateData(userAccount, slot);
|
|
334
339
|
}
|
|
335
340
|
// give event loop a chance to breathe
|
|
336
341
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
@@ -28,4 +28,7 @@ export type UserMapConfig = {
|
|
|
28
28
|
|
|
29
29
|
// True to include idle users when loading. Defaults to false to decrease # of accounts subscribed to.
|
|
30
30
|
includeIdle?: boolean;
|
|
31
|
+
|
|
32
|
+
// Whether to skip loading available perp/spot positions and open orders
|
|
33
|
+
fastDecode?: boolean;
|
|
31
34
|
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { AnchorProvider, Idl, Program } from '@coral-xyz/anchor';
|
|
2
|
+
import driftIDL from '../../src/idl/drift.json';
|
|
3
|
+
import { Connection, Keypair } from '@solana/web3.js';
|
|
4
|
+
import { Wallet } from '../../src';
|
|
5
|
+
import {
|
|
6
|
+
DRIFT_PROGRAM_ID,
|
|
7
|
+
isSpotPositionAvailable,
|
|
8
|
+
isVariant,
|
|
9
|
+
Order,
|
|
10
|
+
PerpPosition,
|
|
11
|
+
positionIsAvailable,
|
|
12
|
+
SpotPosition,
|
|
13
|
+
} from '../../lib';
|
|
14
|
+
import { decodeUser } from '../../lib/decode/user';
|
|
15
|
+
import { assert } from 'chai';
|
|
16
|
+
import { userAccountBufferStrings } from './userAccountBufferStrings';
|
|
17
|
+
const sizeof = require('object-sizeof');
|
|
18
|
+
|
|
19
|
+
describe('Custom user decode', () => {
|
|
20
|
+
it('test', async () => {
|
|
21
|
+
const connection = new Connection('http://localhost:8899');
|
|
22
|
+
const wallet = new Wallet(new Keypair());
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
const provider = new AnchorProvider(connection, wallet);
|
|
25
|
+
const program = new Program(driftIDL as Idl, DRIFT_PROGRAM_ID, provider);
|
|
26
|
+
|
|
27
|
+
let totalAnchorSize = 0;
|
|
28
|
+
let totalCustomSize = 0;
|
|
29
|
+
let totalAnchorTime = 0;
|
|
30
|
+
let totalCustomTime = 0;
|
|
31
|
+
for (const [
|
|
32
|
+
i,
|
|
33
|
+
userAccountBufferString,
|
|
34
|
+
] of userAccountBufferStrings.entries()) {
|
|
35
|
+
const userAccountBuffer = Buffer.from(userAccountBufferString, 'base64');
|
|
36
|
+
const [anchorSize, customSize, anchorTime, customTime] =
|
|
37
|
+
testUserAccountDecode(program, userAccountBuffer, i);
|
|
38
|
+
totalAnchorSize += anchorSize;
|
|
39
|
+
totalCustomSize += customSize;
|
|
40
|
+
totalAnchorTime += anchorTime;
|
|
41
|
+
totalCustomTime += customTime;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log(`Total anchor size: ${totalAnchorSize}`);
|
|
45
|
+
console.log(`Total custom size: ${totalCustomSize}`);
|
|
46
|
+
console.log(`Total anchor time: ${totalAnchorTime}`);
|
|
47
|
+
console.log(`Total custom size: ${totalCustomTime}`);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
function testUserAccountDecode(program: Program, buffer: Buffer, i: number) {
|
|
52
|
+
console.log(`Testing user account decode ${i}`);
|
|
53
|
+
|
|
54
|
+
const anchorStartTimestamp = Date.now();
|
|
55
|
+
const anchorUserAccount = program.coder.accounts.decode('User', buffer);
|
|
56
|
+
const anchorEndTimestamp = Date.now();
|
|
57
|
+
const anchorTime = anchorEndTimestamp - anchorStartTimestamp;
|
|
58
|
+
|
|
59
|
+
const customStartTimestamp = Date.now();
|
|
60
|
+
const customUserAccount = decodeUser(buffer);
|
|
61
|
+
const customEndTimestamp = Date.now();
|
|
62
|
+
const customTime = customEndTimestamp - customStartTimestamp;
|
|
63
|
+
|
|
64
|
+
const anchorSize = sizeof(anchorUserAccount);
|
|
65
|
+
const customSize = sizeof(customUserAccount);
|
|
66
|
+
|
|
67
|
+
assert(anchorUserAccount.authority.equals(customUserAccount.authority));
|
|
68
|
+
assert(anchorUserAccount.delegate.equals(customUserAccount.delegate));
|
|
69
|
+
assert(arraysAreEqual(anchorUserAccount.name, customUserAccount.name));
|
|
70
|
+
|
|
71
|
+
const anchorSpotPositionGenerator = getSpotPositions(
|
|
72
|
+
anchorUserAccount.spotPositions
|
|
73
|
+
);
|
|
74
|
+
const customSpotPositionGenerator = getSpotPositions(
|
|
75
|
+
customUserAccount.spotPositions
|
|
76
|
+
);
|
|
77
|
+
for (const [anchorSpotPosition, customSpotPosition] of zipGenerator(
|
|
78
|
+
anchorSpotPositionGenerator,
|
|
79
|
+
customSpotPositionGenerator
|
|
80
|
+
)) {
|
|
81
|
+
testSpotPosition(anchorSpotPosition, customSpotPosition);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const anchorPerpPositionGenerator = getPerpPositions(
|
|
85
|
+
anchorUserAccount.perpPositions
|
|
86
|
+
);
|
|
87
|
+
const customPerpPositionGenerator = getPerpPositions(
|
|
88
|
+
customUserAccount.perpPositions
|
|
89
|
+
);
|
|
90
|
+
for (const [anchorPerpPosition, customPerpPosition] of zipGenerator(
|
|
91
|
+
anchorPerpPositionGenerator,
|
|
92
|
+
customPerpPositionGenerator
|
|
93
|
+
)) {
|
|
94
|
+
testPerpPosition(anchorPerpPosition, customPerpPosition);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const anchorOrderGenerator = getOrders(anchorUserAccount.orders);
|
|
98
|
+
const customOrderGenerator = getOrders(customUserAccount.orders);
|
|
99
|
+
for (const [anchorOrder, customOrder] of zipGenerator(
|
|
100
|
+
anchorOrderGenerator,
|
|
101
|
+
customOrderGenerator
|
|
102
|
+
)) {
|
|
103
|
+
testOrder(anchorOrder, customOrder);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
assert(
|
|
107
|
+
anchorUserAccount.lastAddPerpLpSharesTs.eq(
|
|
108
|
+
customUserAccount.lastAddPerpLpSharesTs
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
assert(anchorUserAccount.totalDeposits.eq(customUserAccount.totalDeposits));
|
|
112
|
+
assert(anchorUserAccount.totalWithdraws.eq(customUserAccount.totalWithdraws));
|
|
113
|
+
assert(
|
|
114
|
+
anchorUserAccount.totalSocialLoss.eq(customUserAccount.totalSocialLoss)
|
|
115
|
+
);
|
|
116
|
+
assert(anchorUserAccount.settledPerpPnl.eq(customUserAccount.settledPerpPnl));
|
|
117
|
+
assert(
|
|
118
|
+
anchorUserAccount.cumulativeSpotFees.eq(
|
|
119
|
+
customUserAccount.cumulativeSpotFees
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
assert(
|
|
123
|
+
anchorUserAccount.cumulativePerpFunding.eq(
|
|
124
|
+
customUserAccount.cumulativePerpFunding
|
|
125
|
+
)
|
|
126
|
+
);
|
|
127
|
+
assert(
|
|
128
|
+
anchorUserAccount.liquidationMarginFreed.eq(
|
|
129
|
+
customUserAccount.liquidationMarginFreed
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
assert(anchorUserAccount.lastActiveSlot.eq(customUserAccount.lastActiveSlot));
|
|
133
|
+
assert(anchorUserAccount.subAccountId === customUserAccount.subAccountId);
|
|
134
|
+
assert(anchorUserAccount.status === customUserAccount.status);
|
|
135
|
+
assert(
|
|
136
|
+
anchorUserAccount.nextLiquidationId === customUserAccount.nextLiquidationId
|
|
137
|
+
);
|
|
138
|
+
assert(anchorUserAccount.nextOrderId === customUserAccount.nextOrderId);
|
|
139
|
+
assert(anchorUserAccount.maxMarginRatio === customUserAccount.maxMarginRatio);
|
|
140
|
+
assert(
|
|
141
|
+
anchorUserAccount.isMarginTradingEnabled ===
|
|
142
|
+
customUserAccount.isMarginTradingEnabled
|
|
143
|
+
);
|
|
144
|
+
assert(anchorUserAccount.idle === customUserAccount.idle);
|
|
145
|
+
assert(anchorUserAccount.openOrders === customUserAccount.openOrders);
|
|
146
|
+
assert(anchorUserAccount.hasOpenOrder === customUserAccount.hasOpenOrder);
|
|
147
|
+
assert(anchorUserAccount.openAuctions === customUserAccount.openAuctions);
|
|
148
|
+
assert(anchorUserAccount.hasOpenAuction === customUserAccount.hasOpenAuction);
|
|
149
|
+
|
|
150
|
+
return [anchorSize, customSize, anchorTime, customTime];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function* getSpotPositions(spotPositions: SpotPosition[]) {
|
|
154
|
+
for (const spotPosition of spotPositions) {
|
|
155
|
+
if (!isSpotPositionAvailable(spotPosition)) {
|
|
156
|
+
yield spotPosition;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function testSpotPosition(anchor: SpotPosition, custom: SpotPosition) {
|
|
162
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
163
|
+
assert(enumsAreEqual(anchor.balanceType, custom.balanceType));
|
|
164
|
+
assert(anchor.openOrders === custom.openOrders);
|
|
165
|
+
assert(anchor.scaledBalance.eq(custom.scaledBalance));
|
|
166
|
+
assert(anchor.openBids.eq(custom.openBids));
|
|
167
|
+
assert(anchor.openAsks.eq(custom.openAsks));
|
|
168
|
+
assert(anchor.cumulativeDeposits.eq(custom.cumulativeDeposits));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function* getPerpPositions(perpPositions: PerpPosition[]) {
|
|
172
|
+
for (const perpPosition of perpPositions) {
|
|
173
|
+
if (!positionIsAvailable(perpPosition)) {
|
|
174
|
+
yield perpPosition;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function testPerpPosition(anchor: PerpPosition, custom: PerpPosition) {
|
|
180
|
+
assert(anchor.baseAssetAmount.eq(custom.baseAssetAmount));
|
|
181
|
+
assert(anchor.lastCumulativeFundingRate.eq(custom.lastCumulativeFundingRate));
|
|
182
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
183
|
+
assert(anchor.quoteAssetAmount.eq(custom.quoteAssetAmount));
|
|
184
|
+
assert(anchor.quoteEntryAmount.eq(custom.quoteEntryAmount));
|
|
185
|
+
assert(anchor.quoteBreakEvenAmount.eq(custom.quoteBreakEvenAmount));
|
|
186
|
+
assert(anchor.openBids.eq(custom.openBids));
|
|
187
|
+
assert(anchor.openAsks.eq(custom.openAsks));
|
|
188
|
+
assert(anchor.settledPnl.eq(custom.settledPnl));
|
|
189
|
+
assert(anchor.lpShares.eq(custom.lpShares));
|
|
190
|
+
assert(anchor.lastBaseAssetAmountPerLp.eq(custom.lastBaseAssetAmountPerLp));
|
|
191
|
+
assert(anchor.lastQuoteAssetAmountPerLp.eq(custom.lastQuoteAssetAmountPerLp));
|
|
192
|
+
assert(anchor.openOrders === custom.openOrders);
|
|
193
|
+
assert(anchor.perLpBase === custom.perLpBase);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function* getOrders(orders: Order[]) {
|
|
197
|
+
for (const order of orders) {
|
|
198
|
+
if (isVariant(order.status, 'open')) {
|
|
199
|
+
yield order;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function testOrder(anchor: Order, custom: Order) {
|
|
205
|
+
assert(enumsAreEqual(anchor.status, custom.status));
|
|
206
|
+
assert(enumsAreEqual(anchor.orderType, custom.orderType));
|
|
207
|
+
assert(enumsAreEqual(anchor.marketType, custom.marketType));
|
|
208
|
+
assert(anchor.slot.eq(custom.slot));
|
|
209
|
+
assert(anchor.orderId === custom.orderId);
|
|
210
|
+
assert(anchor.userOrderId === custom.userOrderId);
|
|
211
|
+
assert(anchor.marketIndex === custom.marketIndex);
|
|
212
|
+
assert(anchor.price.eq(custom.price));
|
|
213
|
+
assert(anchor.baseAssetAmount.eq(custom.baseAssetAmount));
|
|
214
|
+
assert(anchor.baseAssetAmountFilled.eq(custom.baseAssetAmountFilled));
|
|
215
|
+
assert(anchor.quoteAssetAmountFilled.eq(custom.quoteAssetAmountFilled));
|
|
216
|
+
assert(enumsAreEqual(anchor.direction, custom.direction));
|
|
217
|
+
assert(anchor.reduceOnly === custom.reduceOnly);
|
|
218
|
+
assert(anchor.triggerPrice.eq(custom.triggerPrice));
|
|
219
|
+
assert(enumsAreEqual(anchor.triggerCondition, custom.triggerCondition));
|
|
220
|
+
assert(
|
|
221
|
+
enumsAreEqual(
|
|
222
|
+
anchor.existingPositionDirection,
|
|
223
|
+
custom.existingPositionDirection
|
|
224
|
+
)
|
|
225
|
+
);
|
|
226
|
+
assert(anchor.postOnly === custom.postOnly);
|
|
227
|
+
assert(anchor.immediateOrCancel === custom.immediateOrCancel);
|
|
228
|
+
assert(anchor.oraclePriceOffset === custom.oraclePriceOffset);
|
|
229
|
+
assert(anchor.auctionDuration === custom.auctionDuration);
|
|
230
|
+
assert(anchor.auctionStartPrice.eq(custom.auctionStartPrice));
|
|
231
|
+
assert(anchor.auctionEndPrice.eq(custom.auctionEndPrice));
|
|
232
|
+
assert(anchor.maxTs.eq(custom.maxTs));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function enumsAreEqual(e1: any, e2: any) {
|
|
236
|
+
return JSON.stringify(e1) === JSON.stringify(e2);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function arraysAreEqual(arr1, arr2) {
|
|
240
|
+
if (arr1.length !== arr2.length) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
245
|
+
if (arr1[i] !== arr2[i]) {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function* zipGenerator(gen1, gen2) {
|
|
254
|
+
let iter1 = gen1.next();
|
|
255
|
+
let iter2 = gen2.next();
|
|
256
|
+
|
|
257
|
+
while (!iter1.done && !iter2.done) {
|
|
258
|
+
yield [iter1.value, iter2.value];
|
|
259
|
+
iter1 = gen1.next();
|
|
260
|
+
iter2 = gen2.next();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (iter1.done !== iter2.done) {
|
|
264
|
+
throw new Error('Generators have different lengths');
|
|
265
|
+
}
|
|
266
|
+
}
|