@gearbox-protocol/sdk 10.4.4 → 10.5.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.
|
@@ -29,11 +29,11 @@ class NoAvailableTransportsError extends import_viem.BaseError {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
class RevolverTransport {
|
|
32
|
-
#transports;
|
|
33
|
-
#index = 0;
|
|
34
32
|
#config;
|
|
35
|
-
#rotating = false;
|
|
36
33
|
#requests = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
#selector;
|
|
35
|
+
#rotating = false;
|
|
36
|
+
#isSingle;
|
|
37
37
|
overrides;
|
|
38
38
|
/**
|
|
39
39
|
* Create a new RevolverTransport
|
|
@@ -57,9 +57,9 @@ class RevolverTransport {
|
|
|
57
57
|
...config,
|
|
58
58
|
shouldRetry: config.shouldRetry ?? defaultShouldRetry
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
const transports = config.providers.map(
|
|
61
61
|
({ url, name, cooldown }) => ({
|
|
62
|
-
|
|
62
|
+
name,
|
|
63
63
|
transport: (0, import_viem.http)(url, {
|
|
64
64
|
retryCount: config.retryCount,
|
|
65
65
|
retryDelay: config.retryDelay,
|
|
@@ -73,40 +73,45 @@ class RevolverTransport {
|
|
|
73
73
|
cooldown: cooldown ?? 0
|
|
74
74
|
})
|
|
75
75
|
);
|
|
76
|
-
if (
|
|
76
|
+
if (transports.length === 0) {
|
|
77
77
|
throw new NoAvailableTransportsError();
|
|
78
78
|
}
|
|
79
|
+
this.#isSingle = transports.length === 1;
|
|
80
|
+
const selectionStrategy = config.selectionStrategy ?? "simple";
|
|
81
|
+
this.#selector = selectionStrategy === "simple" ? new SimpleTransportSelector(transports, config.cooldown) : new OrderedTransportSelector(transports, config.cooldown);
|
|
79
82
|
}
|
|
80
83
|
get value() {
|
|
81
84
|
return {
|
|
82
85
|
rotate: (reason) => this.rotate(reason),
|
|
83
|
-
currentTransportName: () => this.
|
|
84
|
-
statuses: () => this.statuses()
|
|
86
|
+
currentTransportName: () => this.#selector.transportName(),
|
|
87
|
+
statuses: () => this.#selector.statuses()
|
|
85
88
|
};
|
|
86
89
|
}
|
|
87
90
|
request = async (r) => {
|
|
88
|
-
if (this.#
|
|
89
|
-
return this.#
|
|
91
|
+
if (this.#isSingle) {
|
|
92
|
+
return this.#selector.select()({
|
|
93
|
+
...this.overrides
|
|
94
|
+
}).request(r);
|
|
90
95
|
}
|
|
91
96
|
let error;
|
|
92
97
|
do {
|
|
93
98
|
try {
|
|
94
|
-
this.#requests.set(r, this.
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
);
|
|
99
|
+
this.#requests.set(r, this.#selector.transportName());
|
|
100
|
+
const transport = this.#selector.select()({
|
|
101
|
+
...this.overrides
|
|
102
|
+
});
|
|
103
|
+
const resp = await (0, import_viem.withRetry)(() => transport.request(r), {
|
|
104
|
+
delay: this.#config.retryDelay,
|
|
105
|
+
retryCount: this.#config.retryCount,
|
|
106
|
+
shouldRetry: this.#config.shouldRetry
|
|
107
|
+
});
|
|
103
108
|
this.#requests.delete(r);
|
|
104
109
|
return resp;
|
|
105
110
|
} catch (e) {
|
|
106
111
|
error = error ?? e;
|
|
107
112
|
if (e instanceof import_viem.RpcError || e instanceof import_viem.HttpRequestError) {
|
|
108
113
|
const reqTransport = this.#requests.get(r);
|
|
109
|
-
if (reqTransport === this.
|
|
114
|
+
if (reqTransport === this.#selector.transportName()) {
|
|
110
115
|
await this.rotate(e);
|
|
111
116
|
} else {
|
|
112
117
|
this.#logger?.debug(
|
|
@@ -118,7 +123,7 @@ class RevolverTransport {
|
|
|
118
123
|
throw e;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
|
-
} while (this.#
|
|
126
|
+
} while (this.#selector.canRotate());
|
|
122
127
|
this.#requests.delete(r);
|
|
123
128
|
throw new NoAvailableTransportsError(error);
|
|
124
129
|
};
|
|
@@ -140,82 +145,34 @@ class RevolverTransport {
|
|
|
140
145
|
* @returns true if rotation was successful
|
|
141
146
|
*/
|
|
142
147
|
async rotate(reason) {
|
|
143
|
-
if (this.#transports.length === 1) {
|
|
144
|
-
return true;
|
|
145
|
-
}
|
|
146
148
|
if (this.#rotating) {
|
|
147
149
|
this.#logger?.debug("already rotating, skipping");
|
|
148
|
-
return
|
|
150
|
+
return;
|
|
149
151
|
}
|
|
150
152
|
this.#rotating = true;
|
|
151
|
-
this.#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"rotating transport"
|
|
158
|
-
);
|
|
159
|
-
const oldTransportName = this.currentTransportName();
|
|
160
|
-
const now = Date.now();
|
|
161
|
-
this.#transports[this.#index].cooldown = now + (this.#config.cooldown ?? 6e4);
|
|
162
|
-
for (let i = 0; i < this.#transports.length - 1; i++) {
|
|
163
|
-
this.#index = (this.#index + 1) % this.#transports.length;
|
|
164
|
-
if (this.#transports[this.#index].cooldown < now) {
|
|
165
|
-
this.#logger?.info(
|
|
166
|
-
{
|
|
167
|
-
current: this.currentTransportName,
|
|
168
|
-
total: this.#transports.length
|
|
169
|
-
},
|
|
170
|
-
"switched to next transport"
|
|
171
|
-
);
|
|
153
|
+
const from = this.#selector.transportName();
|
|
154
|
+
const success = this.#selector.rotate();
|
|
155
|
+
const to = this.#selector.transportName();
|
|
156
|
+
if (success) {
|
|
157
|
+
if (from !== to) {
|
|
158
|
+
this.#logger?.debug({ from, to, reason }, "transport rotated");
|
|
172
159
|
try {
|
|
173
|
-
await this.#config.onRotateSuccess?.(
|
|
174
|
-
oldTransportName,
|
|
175
|
-
this.currentTransportName(),
|
|
176
|
-
reason
|
|
177
|
-
);
|
|
160
|
+
await this.#config.onRotateSuccess?.(from, to, reason);
|
|
178
161
|
} catch {
|
|
179
162
|
}
|
|
180
|
-
this.#rotating = false;
|
|
181
|
-
return true;
|
|
182
|
-
} else {
|
|
183
|
-
this.#logger?.warn(
|
|
184
|
-
{
|
|
185
|
-
current: this.currentTransportName,
|
|
186
|
-
total: this.#transports.length
|
|
187
|
-
},
|
|
188
|
-
"transport is still on cooldown"
|
|
189
|
-
);
|
|
190
163
|
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
164
|
+
} else {
|
|
165
|
+
this.#logger?.warn({ from, reason }, "transport rotation failed");
|
|
166
|
+
try {
|
|
167
|
+
await this.#config.onRotateFailed?.(from, reason);
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
195
170
|
}
|
|
196
171
|
this.#rotating = false;
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
currentTransportName() {
|
|
200
|
-
return this.#transport({}).config.name;
|
|
201
|
-
}
|
|
202
|
-
statuses() {
|
|
203
|
-
const now = Date.now();
|
|
204
|
-
return this.#transports.map((t, i) => ({
|
|
205
|
-
id: t.transport({}).config.name,
|
|
206
|
-
status: t.cooldown < now ? this.#index === i ? "active" : "standby" : "cooldown"
|
|
207
|
-
}));
|
|
208
172
|
}
|
|
209
173
|
get #logger() {
|
|
210
174
|
return this.#config.logger;
|
|
211
175
|
}
|
|
212
|
-
get #transport() {
|
|
213
|
-
return this.#transports[this.#index].transport;
|
|
214
|
-
}
|
|
215
|
-
get #hasAvailableTransports() {
|
|
216
|
-
const now = Date.now();
|
|
217
|
-
return this.#transports.some((t) => t.cooldown < now);
|
|
218
|
-
}
|
|
219
176
|
}
|
|
220
177
|
const retryCodes = /* @__PURE__ */ new Set([
|
|
221
178
|
import_viem.InvalidRequestRpcError.code,
|
|
@@ -232,6 +189,87 @@ const defaultShouldRetry = ({
|
|
|
232
189
|
}
|
|
233
190
|
return false;
|
|
234
191
|
};
|
|
192
|
+
class AbstractTransportSelector {
|
|
193
|
+
transports;
|
|
194
|
+
cooldown;
|
|
195
|
+
index = 0;
|
|
196
|
+
constructor(transports, cooldown = 6e4) {
|
|
197
|
+
this.transports = transports;
|
|
198
|
+
this.cooldown = cooldown;
|
|
199
|
+
}
|
|
200
|
+
transportName() {
|
|
201
|
+
return this.transports[this.index].name;
|
|
202
|
+
}
|
|
203
|
+
canRotate() {
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
return this.transports.some((t) => t.cooldown < now);
|
|
206
|
+
}
|
|
207
|
+
statuses() {
|
|
208
|
+
const now = Date.now();
|
|
209
|
+
return this.transports.map((t, i) => ({
|
|
210
|
+
id: t.name,
|
|
211
|
+
status: t.cooldown < now ? this.index === i ? "active" : "standby" : "cooldown"
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
class SimpleTransportSelector extends AbstractTransportSelector {
|
|
216
|
+
/**
|
|
217
|
+
* For simple selector, transport status is not re-evaluated on each request
|
|
218
|
+
* @returns
|
|
219
|
+
*/
|
|
220
|
+
select() {
|
|
221
|
+
return this.transports[this.index].transport;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Simply selects next transport that is not in cooldown by checking all transports in cyclic order
|
|
225
|
+
* @returns true if rotation was successful
|
|
226
|
+
*/
|
|
227
|
+
rotate() {
|
|
228
|
+
if (this.transports.length === 1) {
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
const now = Date.now();
|
|
232
|
+
this.transports[this.index].cooldown = now + this.cooldown;
|
|
233
|
+
for (let i = 0; i < this.transports.length - 1; i++) {
|
|
234
|
+
this.index = (this.index + 1) % this.transports.length;
|
|
235
|
+
if (this.transports[this.index].cooldown < now) {
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
class OrderedTransportSelector extends AbstractTransportSelector {
|
|
243
|
+
/**
|
|
244
|
+
* Will select first transport that is not in cooldown
|
|
245
|
+
* @returns
|
|
246
|
+
*/
|
|
247
|
+
select() {
|
|
248
|
+
this.#updateIndex(Date.now());
|
|
249
|
+
return this.transports[this.index].transport;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Will put current transport into cooldown and select first available transport that is not in cooldown
|
|
253
|
+
* @returns true if rotation was successful
|
|
254
|
+
*/
|
|
255
|
+
rotate() {
|
|
256
|
+
if (this.transports.length === 1) {
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
const now = Date.now();
|
|
260
|
+
this.transports[this.index].cooldown = now + this.cooldown;
|
|
261
|
+
return this.#updateIndex(now);
|
|
262
|
+
}
|
|
263
|
+
#updateIndex(now) {
|
|
264
|
+
for (let i = 0; i < this.transports.length; i++) {
|
|
265
|
+
if (this.transports[i].cooldown < now) {
|
|
266
|
+
this.index = i;
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
235
273
|
// Annotate the CommonJS export names for ESM import in node:
|
|
236
274
|
0 && (module.exports = {
|
|
237
275
|
NoAvailableTransportsError,
|
|
@@ -146,11 +146,6 @@ class RouterV310Contract extends import_AbstractRouterContract.AbstractRouterCon
|
|
|
146
146
|
* Implements {@link IRouterContract.findClaimAllRewards}
|
|
147
147
|
*/
|
|
148
148
|
async findClaimAllRewards(props) {
|
|
149
|
-
if (props.calls.length > 0 && !!props.forceCalls) {
|
|
150
|
-
return {
|
|
151
|
-
calls: [...props.calls]
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
149
|
const tData = props.tokensToClaim.map((a) => ({
|
|
155
150
|
balance: 0n,
|
|
156
151
|
claimRewards: true,
|
|
@@ -162,6 +157,11 @@ class RouterV310Contract extends import_AbstractRouterContract.AbstractRouterCon
|
|
|
162
157
|
props.creditAccount.creditAccount,
|
|
163
158
|
tData
|
|
164
159
|
]);
|
|
160
|
+
if (props.calls.length > 0 && result.length === 0 && !!props.forceCalls) {
|
|
161
|
+
return {
|
|
162
|
+
calls: [...props.calls]
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
165
|
return {
|
|
166
166
|
calls: [...result]
|
|
167
167
|
};
|
|
@@ -16,11 +16,11 @@ class NoAvailableTransportsError extends BaseError {
|
|
|
16
16
|
}
|
|
17
17
|
}
|
|
18
18
|
class RevolverTransport {
|
|
19
|
-
#transports;
|
|
20
|
-
#index = 0;
|
|
21
19
|
#config;
|
|
22
|
-
#rotating = false;
|
|
23
20
|
#requests = /* @__PURE__ */ new WeakMap();
|
|
21
|
+
#selector;
|
|
22
|
+
#rotating = false;
|
|
23
|
+
#isSingle;
|
|
24
24
|
overrides;
|
|
25
25
|
/**
|
|
26
26
|
* Create a new RevolverTransport
|
|
@@ -44,9 +44,9 @@ class RevolverTransport {
|
|
|
44
44
|
...config,
|
|
45
45
|
shouldRetry: config.shouldRetry ?? defaultShouldRetry
|
|
46
46
|
};
|
|
47
|
-
|
|
47
|
+
const transports = config.providers.map(
|
|
48
48
|
({ url, name, cooldown }) => ({
|
|
49
|
-
|
|
49
|
+
name,
|
|
50
50
|
transport: http(url, {
|
|
51
51
|
retryCount: config.retryCount,
|
|
52
52
|
retryDelay: config.retryDelay,
|
|
@@ -60,40 +60,45 @@ class RevolverTransport {
|
|
|
60
60
|
cooldown: cooldown ?? 0
|
|
61
61
|
})
|
|
62
62
|
);
|
|
63
|
-
if (
|
|
63
|
+
if (transports.length === 0) {
|
|
64
64
|
throw new NoAvailableTransportsError();
|
|
65
65
|
}
|
|
66
|
+
this.#isSingle = transports.length === 1;
|
|
67
|
+
const selectionStrategy = config.selectionStrategy ?? "simple";
|
|
68
|
+
this.#selector = selectionStrategy === "simple" ? new SimpleTransportSelector(transports, config.cooldown) : new OrderedTransportSelector(transports, config.cooldown);
|
|
66
69
|
}
|
|
67
70
|
get value() {
|
|
68
71
|
return {
|
|
69
72
|
rotate: (reason) => this.rotate(reason),
|
|
70
|
-
currentTransportName: () => this.
|
|
71
|
-
statuses: () => this.statuses()
|
|
73
|
+
currentTransportName: () => this.#selector.transportName(),
|
|
74
|
+
statuses: () => this.#selector.statuses()
|
|
72
75
|
};
|
|
73
76
|
}
|
|
74
77
|
request = async (r) => {
|
|
75
|
-
if (this.#
|
|
76
|
-
return this.#
|
|
78
|
+
if (this.#isSingle) {
|
|
79
|
+
return this.#selector.select()({
|
|
80
|
+
...this.overrides
|
|
81
|
+
}).request(r);
|
|
77
82
|
}
|
|
78
83
|
let error;
|
|
79
84
|
do {
|
|
80
85
|
try {
|
|
81
|
-
this.#requests.set(r, this.
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
);
|
|
86
|
+
this.#requests.set(r, this.#selector.transportName());
|
|
87
|
+
const transport = this.#selector.select()({
|
|
88
|
+
...this.overrides
|
|
89
|
+
});
|
|
90
|
+
const resp = await withRetry(() => transport.request(r), {
|
|
91
|
+
delay: this.#config.retryDelay,
|
|
92
|
+
retryCount: this.#config.retryCount,
|
|
93
|
+
shouldRetry: this.#config.shouldRetry
|
|
94
|
+
});
|
|
90
95
|
this.#requests.delete(r);
|
|
91
96
|
return resp;
|
|
92
97
|
} catch (e) {
|
|
93
98
|
error = error ?? e;
|
|
94
99
|
if (e instanceof RpcError || e instanceof HttpRequestError) {
|
|
95
100
|
const reqTransport = this.#requests.get(r);
|
|
96
|
-
if (reqTransport === this.
|
|
101
|
+
if (reqTransport === this.#selector.transportName()) {
|
|
97
102
|
await this.rotate(e);
|
|
98
103
|
} else {
|
|
99
104
|
this.#logger?.debug(
|
|
@@ -105,7 +110,7 @@ class RevolverTransport {
|
|
|
105
110
|
throw e;
|
|
106
111
|
}
|
|
107
112
|
}
|
|
108
|
-
} while (this.#
|
|
113
|
+
} while (this.#selector.canRotate());
|
|
109
114
|
this.#requests.delete(r);
|
|
110
115
|
throw new NoAvailableTransportsError(error);
|
|
111
116
|
};
|
|
@@ -127,82 +132,34 @@ class RevolverTransport {
|
|
|
127
132
|
* @returns true if rotation was successful
|
|
128
133
|
*/
|
|
129
134
|
async rotate(reason) {
|
|
130
|
-
if (this.#transports.length === 1) {
|
|
131
|
-
return true;
|
|
132
|
-
}
|
|
133
135
|
if (this.#rotating) {
|
|
134
136
|
this.#logger?.debug("already rotating, skipping");
|
|
135
|
-
return
|
|
137
|
+
return;
|
|
136
138
|
}
|
|
137
139
|
this.#rotating = true;
|
|
138
|
-
this.#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"rotating transport"
|
|
145
|
-
);
|
|
146
|
-
const oldTransportName = this.currentTransportName();
|
|
147
|
-
const now = Date.now();
|
|
148
|
-
this.#transports[this.#index].cooldown = now + (this.#config.cooldown ?? 6e4);
|
|
149
|
-
for (let i = 0; i < this.#transports.length - 1; i++) {
|
|
150
|
-
this.#index = (this.#index + 1) % this.#transports.length;
|
|
151
|
-
if (this.#transports[this.#index].cooldown < now) {
|
|
152
|
-
this.#logger?.info(
|
|
153
|
-
{
|
|
154
|
-
current: this.currentTransportName,
|
|
155
|
-
total: this.#transports.length
|
|
156
|
-
},
|
|
157
|
-
"switched to next transport"
|
|
158
|
-
);
|
|
140
|
+
const from = this.#selector.transportName();
|
|
141
|
+
const success = this.#selector.rotate();
|
|
142
|
+
const to = this.#selector.transportName();
|
|
143
|
+
if (success) {
|
|
144
|
+
if (from !== to) {
|
|
145
|
+
this.#logger?.debug({ from, to, reason }, "transport rotated");
|
|
159
146
|
try {
|
|
160
|
-
await this.#config.onRotateSuccess?.(
|
|
161
|
-
oldTransportName,
|
|
162
|
-
this.currentTransportName(),
|
|
163
|
-
reason
|
|
164
|
-
);
|
|
147
|
+
await this.#config.onRotateSuccess?.(from, to, reason);
|
|
165
148
|
} catch {
|
|
166
149
|
}
|
|
167
|
-
this.#rotating = false;
|
|
168
|
-
return true;
|
|
169
|
-
} else {
|
|
170
|
-
this.#logger?.warn(
|
|
171
|
-
{
|
|
172
|
-
current: this.currentTransportName,
|
|
173
|
-
total: this.#transports.length
|
|
174
|
-
},
|
|
175
|
-
"transport is still on cooldown"
|
|
176
|
-
);
|
|
177
150
|
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
151
|
+
} else {
|
|
152
|
+
this.#logger?.warn({ from, reason }, "transport rotation failed");
|
|
153
|
+
try {
|
|
154
|
+
await this.#config.onRotateFailed?.(from, reason);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
182
157
|
}
|
|
183
158
|
this.#rotating = false;
|
|
184
|
-
return false;
|
|
185
|
-
}
|
|
186
|
-
currentTransportName() {
|
|
187
|
-
return this.#transport({}).config.name;
|
|
188
|
-
}
|
|
189
|
-
statuses() {
|
|
190
|
-
const now = Date.now();
|
|
191
|
-
return this.#transports.map((t, i) => ({
|
|
192
|
-
id: t.transport({}).config.name,
|
|
193
|
-
status: t.cooldown < now ? this.#index === i ? "active" : "standby" : "cooldown"
|
|
194
|
-
}));
|
|
195
159
|
}
|
|
196
160
|
get #logger() {
|
|
197
161
|
return this.#config.logger;
|
|
198
162
|
}
|
|
199
|
-
get #transport() {
|
|
200
|
-
return this.#transports[this.#index].transport;
|
|
201
|
-
}
|
|
202
|
-
get #hasAvailableTransports() {
|
|
203
|
-
const now = Date.now();
|
|
204
|
-
return this.#transports.some((t) => t.cooldown < now);
|
|
205
|
-
}
|
|
206
163
|
}
|
|
207
164
|
const retryCodes = /* @__PURE__ */ new Set([
|
|
208
165
|
InvalidRequestRpcError.code,
|
|
@@ -219,6 +176,87 @@ const defaultShouldRetry = ({
|
|
|
219
176
|
}
|
|
220
177
|
return false;
|
|
221
178
|
};
|
|
179
|
+
class AbstractTransportSelector {
|
|
180
|
+
transports;
|
|
181
|
+
cooldown;
|
|
182
|
+
index = 0;
|
|
183
|
+
constructor(transports, cooldown = 6e4) {
|
|
184
|
+
this.transports = transports;
|
|
185
|
+
this.cooldown = cooldown;
|
|
186
|
+
}
|
|
187
|
+
transportName() {
|
|
188
|
+
return this.transports[this.index].name;
|
|
189
|
+
}
|
|
190
|
+
canRotate() {
|
|
191
|
+
const now = Date.now();
|
|
192
|
+
return this.transports.some((t) => t.cooldown < now);
|
|
193
|
+
}
|
|
194
|
+
statuses() {
|
|
195
|
+
const now = Date.now();
|
|
196
|
+
return this.transports.map((t, i) => ({
|
|
197
|
+
id: t.name,
|
|
198
|
+
status: t.cooldown < now ? this.index === i ? "active" : "standby" : "cooldown"
|
|
199
|
+
}));
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
class SimpleTransportSelector extends AbstractTransportSelector {
|
|
203
|
+
/**
|
|
204
|
+
* For simple selector, transport status is not re-evaluated on each request
|
|
205
|
+
* @returns
|
|
206
|
+
*/
|
|
207
|
+
select() {
|
|
208
|
+
return this.transports[this.index].transport;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Simply selects next transport that is not in cooldown by checking all transports in cyclic order
|
|
212
|
+
* @returns true if rotation was successful
|
|
213
|
+
*/
|
|
214
|
+
rotate() {
|
|
215
|
+
if (this.transports.length === 1) {
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
const now = Date.now();
|
|
219
|
+
this.transports[this.index].cooldown = now + this.cooldown;
|
|
220
|
+
for (let i = 0; i < this.transports.length - 1; i++) {
|
|
221
|
+
this.index = (this.index + 1) % this.transports.length;
|
|
222
|
+
if (this.transports[this.index].cooldown < now) {
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
class OrderedTransportSelector extends AbstractTransportSelector {
|
|
230
|
+
/**
|
|
231
|
+
* Will select first transport that is not in cooldown
|
|
232
|
+
* @returns
|
|
233
|
+
*/
|
|
234
|
+
select() {
|
|
235
|
+
this.#updateIndex(Date.now());
|
|
236
|
+
return this.transports[this.index].transport;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Will put current transport into cooldown and select first available transport that is not in cooldown
|
|
240
|
+
* @returns true if rotation was successful
|
|
241
|
+
*/
|
|
242
|
+
rotate() {
|
|
243
|
+
if (this.transports.length === 1) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
const now = Date.now();
|
|
247
|
+
this.transports[this.index].cooldown = now + this.cooldown;
|
|
248
|
+
return this.#updateIndex(now);
|
|
249
|
+
}
|
|
250
|
+
#updateIndex(now) {
|
|
251
|
+
for (let i = 0; i < this.transports.length; i++) {
|
|
252
|
+
if (this.transports[i].cooldown < now) {
|
|
253
|
+
this.index = i;
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
222
260
|
export {
|
|
223
261
|
NoAvailableTransportsError,
|
|
224
262
|
RevolverTransport
|
|
@@ -123,11 +123,6 @@ class RouterV310Contract extends AbstractRouterContract {
|
|
|
123
123
|
* Implements {@link IRouterContract.findClaimAllRewards}
|
|
124
124
|
*/
|
|
125
125
|
async findClaimAllRewards(props) {
|
|
126
|
-
if (props.calls.length > 0 && !!props.forceCalls) {
|
|
127
|
-
return {
|
|
128
|
-
calls: [...props.calls]
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
126
|
const tData = props.tokensToClaim.map((a) => ({
|
|
132
127
|
balance: 0n,
|
|
133
128
|
claimRewards: true,
|
|
@@ -139,6 +134,11 @@ class RouterV310Contract extends AbstractRouterContract {
|
|
|
139
134
|
props.creditAccount.creditAccount,
|
|
140
135
|
tData
|
|
141
136
|
]);
|
|
137
|
+
if (props.calls.length > 0 && result.length === 0 && !!props.forceCalls) {
|
|
138
|
+
return {
|
|
139
|
+
calls: [...props.calls]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
142
|
return {
|
|
143
143
|
calls: [...result]
|
|
144
144
|
};
|
|
@@ -12,6 +12,12 @@ export interface ProviderStatus {
|
|
|
12
12
|
}
|
|
13
13
|
type OnRequestFn = (providerName: string, ...args: Parameters<Required<HttpRpcClientOptions>["onRequest"]>) => ReturnType<Required<HttpRpcClientOptions>["onRequest"]>;
|
|
14
14
|
type OnResponseFn = (providerName: string, ...args: Parameters<Required<HttpRpcClientOptions>["onResponse"]>) => ReturnType<Required<HttpRpcClientOptions>["onResponse"]>;
|
|
15
|
+
/**
|
|
16
|
+
* How to select the next transport
|
|
17
|
+
* - simple: selects next transport after current that is not in cooldown by checking all transports in cyclic order
|
|
18
|
+
* - ordered: will select first available transport that is not in cooldown
|
|
19
|
+
*/
|
|
20
|
+
export type SelectionStrategy = "simple" | "ordered";
|
|
15
21
|
export interface RevolverTransportConfig {
|
|
16
22
|
network: NetworkType;
|
|
17
23
|
providers: ProviderConfig[];
|
|
@@ -59,17 +65,21 @@ export interface RevolverTransportConfig {
|
|
|
59
65
|
* How long, in milliseconds, to wait before try this transport again
|
|
60
66
|
*/
|
|
61
67
|
cooldown?: number | undefined;
|
|
68
|
+
/**
|
|
69
|
+
* How to select the next transport
|
|
70
|
+
* Defaults to "simple"
|
|
71
|
+
*/
|
|
72
|
+
selectionStrategy?: SelectionStrategy;
|
|
62
73
|
}
|
|
63
74
|
export declare class NoAvailableTransportsError extends BaseError {
|
|
64
75
|
constructor(cause?: Error);
|
|
65
76
|
}
|
|
66
77
|
export interface RevolverTransportValue {
|
|
67
78
|
/**
|
|
68
|
-
* Manually rotate the transport
|
|
79
|
+
* Manually try to rotate the transport
|
|
69
80
|
* @param reason
|
|
70
|
-
* @returns true if rotation was successful
|
|
71
81
|
*/
|
|
72
|
-
rotate: (reason?: BaseError) => Promise<
|
|
82
|
+
rotate: (reason?: BaseError) => Promise<void>;
|
|
73
83
|
/**
|
|
74
84
|
* Returns the name of the current transport
|
|
75
85
|
*/
|
|
@@ -100,8 +110,6 @@ export declare class RevolverTransport implements ReturnType<Transport<"revolver
|
|
|
100
110
|
* @param reason
|
|
101
111
|
* @returns true if rotation was successful
|
|
102
112
|
*/
|
|
103
|
-
rotate(reason?: BaseError): Promise<
|
|
104
|
-
currentTransportName(): string;
|
|
105
|
-
statuses(): ProviderStatus[];
|
|
113
|
+
rotate(reason?: BaseError): Promise<void>;
|
|
106
114
|
}
|
|
107
115
|
export {};
|