@gearbox-protocol/sdk 9.0.4 → 9.1.1
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/cjs/dev/RevolverTransport.js +195 -0
- package/dist/cjs/dev/index.js +4 -2
- package/dist/cjs/dev/{createTransport.js → providers.js} +22 -38
- package/dist/cjs/sdk/chain/chains.js +5 -15
- package/dist/esm/dev/RevolverTransport.js +177 -0
- package/dist/esm/dev/index.js +2 -1
- package/dist/esm/dev/{createTransport.js → providers.js} +19 -34
- package/dist/esm/sdk/chain/chains.js +5 -15
- package/dist/types/dev/RevolverTransport.d.ts +77 -0
- package/dist/types/dev/index.d.ts +2 -1
- package/dist/types/dev/{createTransport.d.ts → providers.d.ts} +2 -8
- package/dist/types/sdk/chain/chains.d.ts +0 -4
- package/package.json +1 -1
- package/dist/types/sdk/chain/detectNetwork.test.d.ts +0 -1
- package/dist/types/sdk/market/pricefeeds/updates/RedstoneUpdater.test.d.ts +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var RevolverTransport_exports = {};
|
|
20
|
+
__export(RevolverTransport_exports, {
|
|
21
|
+
NoAvailableTransportsError: () => NoAvailableTransportsError,
|
|
22
|
+
RevolverTransport: () => RevolverTransport
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(RevolverTransport_exports);
|
|
25
|
+
var import_viem = require("viem");
|
|
26
|
+
var import_providers = require("./providers.js");
|
|
27
|
+
class NoAvailableTransportsError extends import_viem.BaseError {
|
|
28
|
+
constructor() {
|
|
29
|
+
super("no available transports");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class RevolverTransport {
|
|
33
|
+
#transports;
|
|
34
|
+
#index = 0;
|
|
35
|
+
#config;
|
|
36
|
+
overrides;
|
|
37
|
+
/**
|
|
38
|
+
* Create a new RevolverTransport
|
|
39
|
+
* RevolverTransport usues several RPC providers and rotates between them when requests fail
|
|
40
|
+
* Failed transport goes into temporary cooldown, after which it can be tried again
|
|
41
|
+
* When all transports are on cooldown, the transport will throw a NoAvailableTransportsError
|
|
42
|
+
* @param config
|
|
43
|
+
* @returns
|
|
44
|
+
*/
|
|
45
|
+
static create(config) {
|
|
46
|
+
const transport = new RevolverTransport({
|
|
47
|
+
...config
|
|
48
|
+
});
|
|
49
|
+
return (...args) => {
|
|
50
|
+
transport.overrides = args[0];
|
|
51
|
+
return transport;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.#config = { ...config };
|
|
56
|
+
const rpcUrls = /* @__PURE__ */ new Map();
|
|
57
|
+
const cooldowns = /* @__PURE__ */ new Map();
|
|
58
|
+
for (const { provider, keys, cooldown } of config.providers) {
|
|
59
|
+
for (const key of keys) {
|
|
60
|
+
const url = (0, import_providers.getProviderUrl)(provider, config.network, key, "http");
|
|
61
|
+
if (url) {
|
|
62
|
+
rpcUrls.set(url, provider);
|
|
63
|
+
if (cooldown) {
|
|
64
|
+
cooldowns.set(url, cooldown);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
this.#transports = Array.from(rpcUrls.entries()).map(
|
|
70
|
+
([url, provider], i) => ({
|
|
71
|
+
provider,
|
|
72
|
+
transport: (0, import_viem.http)(url, {
|
|
73
|
+
retryCount: config.retryCount,
|
|
74
|
+
retryDelay: config.retryDelay,
|
|
75
|
+
timeout: config.timeout,
|
|
76
|
+
batch: false,
|
|
77
|
+
key: `${provider}-${i}`,
|
|
78
|
+
name: `${provider}-${i}`,
|
|
79
|
+
onFetchRequest: this.#config.onRequest ? (...args) => this.#config.onRequest?.(`${provider}-${i}`, ...args) : void 0,
|
|
80
|
+
onFetchResponse: this.#config.onResponse ? (...args) => this.#config.onResponse?.(`${provider}-${i}`, ...args) : void 0
|
|
81
|
+
}),
|
|
82
|
+
cooldown: cooldowns.get(url) ?? 0
|
|
83
|
+
})
|
|
84
|
+
);
|
|
85
|
+
if (this.#transports.length === 0) {
|
|
86
|
+
throw new NoAvailableTransportsError();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
get value() {
|
|
90
|
+
return {
|
|
91
|
+
rotate: (reason) => this.rotate(reason),
|
|
92
|
+
currentTransportName: this.currentTransportName
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
request = async (r) => {
|
|
96
|
+
if (this.#transports.length === 1) {
|
|
97
|
+
return this.#transport({ ...this.overrides }).request(r);
|
|
98
|
+
}
|
|
99
|
+
do {
|
|
100
|
+
try {
|
|
101
|
+
const resp = await this.#transport({ ...this.overrides }).request(r);
|
|
102
|
+
return resp;
|
|
103
|
+
} catch (e) {
|
|
104
|
+
if (e instanceof import_viem.RpcError || e instanceof import_viem.HttpRequestError) {
|
|
105
|
+
await this.rotate(e);
|
|
106
|
+
} else {
|
|
107
|
+
throw e;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} while (this.#hasAvailableTransports);
|
|
111
|
+
throw new NoAvailableTransportsError();
|
|
112
|
+
};
|
|
113
|
+
get config() {
|
|
114
|
+
return {
|
|
115
|
+
key: "revolver",
|
|
116
|
+
name: "revolver",
|
|
117
|
+
type: "revolver",
|
|
118
|
+
request: this.request,
|
|
119
|
+
retryCount: this.#config.retryCount,
|
|
120
|
+
methods: void 0,
|
|
121
|
+
retryDelay: this.#config.retryDelay,
|
|
122
|
+
timeout: this.#config.timeout
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Manually rotate the transport
|
|
127
|
+
* @param reason
|
|
128
|
+
* @returns true if rotation was successful
|
|
129
|
+
*/
|
|
130
|
+
async rotate(reason) {
|
|
131
|
+
if (this.#transports.length === 1) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
this.#logger?.debug(
|
|
135
|
+
{
|
|
136
|
+
reason,
|
|
137
|
+
current: this.currentTransportName,
|
|
138
|
+
index: this.#index,
|
|
139
|
+
total: this.#transports.length
|
|
140
|
+
},
|
|
141
|
+
"rotating transport"
|
|
142
|
+
);
|
|
143
|
+
const oldTransportName = this.currentTransportName;
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
this.#transports[this.#index].cooldown = now + (this.#config.cooldown ?? 6e4);
|
|
146
|
+
for (let i = 0; i < this.#transports.length - 1; i++) {
|
|
147
|
+
this.#index = (this.#index + 1) % this.#transports.length;
|
|
148
|
+
if (this.#transports[this.#index].cooldown < now) {
|
|
149
|
+
this.#logger?.info(
|
|
150
|
+
{
|
|
151
|
+
current: this.currentTransportName,
|
|
152
|
+
index: this.#index,
|
|
153
|
+
total: this.#transports.length
|
|
154
|
+
},
|
|
155
|
+
"switched to next transport"
|
|
156
|
+
);
|
|
157
|
+
await this.#config.onRotateSuccess?.(
|
|
158
|
+
oldTransportName,
|
|
159
|
+
this.currentTransportName,
|
|
160
|
+
reason
|
|
161
|
+
);
|
|
162
|
+
return true;
|
|
163
|
+
} else {
|
|
164
|
+
this.#logger?.warn(
|
|
165
|
+
{
|
|
166
|
+
current: this.currentTransportName,
|
|
167
|
+
index: this.#index,
|
|
168
|
+
total: this.#transports.length
|
|
169
|
+
},
|
|
170
|
+
"transport is still on cooldown"
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
await this.#config.onRotateFailed?.(reason);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
get currentTransportName() {
|
|
178
|
+
return this.#transport({}).config.name;
|
|
179
|
+
}
|
|
180
|
+
get #logger() {
|
|
181
|
+
return this.#config.logger;
|
|
182
|
+
}
|
|
183
|
+
get #transport() {
|
|
184
|
+
return this.#transports[this.#index].transport;
|
|
185
|
+
}
|
|
186
|
+
get #hasAvailableTransports() {
|
|
187
|
+
const now = Date.now();
|
|
188
|
+
return this.#transports.some((t) => t.cooldown < now);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
192
|
+
0 && (module.exports = {
|
|
193
|
+
NoAvailableTransportsError,
|
|
194
|
+
RevolverTransport
|
|
195
|
+
});
|
package/dist/cjs/dev/index.js
CHANGED
|
@@ -21,11 +21,12 @@ __reExport(dev_exports, require("./calcLiquidatableLTs.js"), module.exports);
|
|
|
21
21
|
__reExport(dev_exports, require("./claimFromFaucet.js"), module.exports);
|
|
22
22
|
__reExport(dev_exports, require("./create2.js"), module.exports);
|
|
23
23
|
__reExport(dev_exports, require("./createAnvilClient.js"), module.exports);
|
|
24
|
-
__reExport(dev_exports, require("./createTransport.js"), module.exports);
|
|
25
24
|
__reExport(dev_exports, require("./detectChain.js"), module.exports);
|
|
26
25
|
__reExport(dev_exports, require("./EthCallSpy.js"), module.exports);
|
|
27
26
|
__reExport(dev_exports, require("./ltUtils.js"), module.exports);
|
|
28
27
|
__reExport(dev_exports, require("./migrateFaucet.js"), module.exports);
|
|
28
|
+
__reExport(dev_exports, require("./providers.js"), module.exports);
|
|
29
|
+
__reExport(dev_exports, require("./RevolverTransport.js"), module.exports);
|
|
29
30
|
__reExport(dev_exports, require("./types.js"), module.exports);
|
|
30
31
|
// Annotate the CommonJS export names for ESM import in node:
|
|
31
32
|
0 && (module.exports = {
|
|
@@ -35,10 +36,11 @@ __reExport(dev_exports, require("./types.js"), module.exports);
|
|
|
35
36
|
...require("./claimFromFaucet.js"),
|
|
36
37
|
...require("./create2.js"),
|
|
37
38
|
...require("./createAnvilClient.js"),
|
|
38
|
-
...require("./createTransport.js"),
|
|
39
39
|
...require("./detectChain.js"),
|
|
40
40
|
...require("./EthCallSpy.js"),
|
|
41
41
|
...require("./ltUtils.js"),
|
|
42
42
|
...require("./migrateFaucet.js"),
|
|
43
|
+
...require("./providers.js"),
|
|
44
|
+
...require("./RevolverTransport.js"),
|
|
43
45
|
...require("./types.js")
|
|
44
46
|
});
|
|
@@ -16,43 +16,13 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
var
|
|
20
|
-
__export(
|
|
21
|
-
createTransport: () => createTransport,
|
|
19
|
+
var providers_exports = {};
|
|
20
|
+
__export(providers_exports, {
|
|
22
21
|
getAlchemyUrl: () => getAlchemyUrl,
|
|
23
22
|
getDrpcUrl: () => getDrpcUrl,
|
|
24
23
|
getProviderUrl: () => getProviderUrl
|
|
25
24
|
});
|
|
26
|
-
module.exports = __toCommonJS(
|
|
27
|
-
var import_viem = require("viem");
|
|
28
|
-
var import_sdk = require("../sdk/index.js");
|
|
29
|
-
function createTransport(config) {
|
|
30
|
-
const { rpcProviders = [], protocol, network, ...rest } = config;
|
|
31
|
-
const rpcUrls = /* @__PURE__ */ new Map();
|
|
32
|
-
for (const { provider, keys } of rpcProviders) {
|
|
33
|
-
for (const key of keys) {
|
|
34
|
-
const url = getProviderUrl(provider, network, key, protocol);
|
|
35
|
-
if (url) {
|
|
36
|
-
rpcUrls.set(url, provider);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
const transports = Array.from(rpcUrls.entries()).map(
|
|
41
|
-
([url, provider], index) => protocol === "http" ? (0, import_viem.http)(url, {
|
|
42
|
-
...rest,
|
|
43
|
-
key: `${provider}-${index}`,
|
|
44
|
-
name: `${provider}-${index}`
|
|
45
|
-
}) : (0, import_viem.webSocket)(url, {
|
|
46
|
-
...rest,
|
|
47
|
-
key: `${provider}-${index}`,
|
|
48
|
-
name: `${provider}-${index}`
|
|
49
|
-
})
|
|
50
|
-
);
|
|
51
|
-
if (transports.length === 0) {
|
|
52
|
-
throw new Error("no fitting rpc urls found");
|
|
53
|
-
}
|
|
54
|
-
return transports.length > 1 ? (0, import_viem.fallback)(transports) : transports[0];
|
|
55
|
-
}
|
|
25
|
+
module.exports = __toCommonJS(providers_exports);
|
|
56
26
|
function getProviderUrl(provider, network, apiKey, protocol) {
|
|
57
27
|
switch (provider) {
|
|
58
28
|
case "alchemy":
|
|
@@ -68,7 +38,7 @@ function getProviderUrl(provider, network, apiKey, protocol) {
|
|
|
68
38
|
}
|
|
69
39
|
}
|
|
70
40
|
function getAlchemyUrl(network, apiKey, protocol) {
|
|
71
|
-
const
|
|
41
|
+
const alchemyDomain = ALCHEMY_DOMAINS[network];
|
|
72
42
|
if (!alchemyDomain) {
|
|
73
43
|
return void 0;
|
|
74
44
|
}
|
|
@@ -87,9 +57,24 @@ const DRPC_NETS = {
|
|
|
87
57
|
Monad: "monad-testnet",
|
|
88
58
|
Hemi: "hemi",
|
|
89
59
|
Lisk: "lisk",
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
60
|
+
MegaETH: null,
|
|
61
|
+
Etherlink: null
|
|
62
|
+
};
|
|
63
|
+
const ALCHEMY_DOMAINS = {
|
|
64
|
+
Mainnet: "eth-mainnet",
|
|
65
|
+
Arbitrum: "arb-mainnet",
|
|
66
|
+
Optimism: "opt-mainnet",
|
|
67
|
+
Base: "base-mainnet",
|
|
68
|
+
Sonic: "sonic-mainnet",
|
|
69
|
+
Monad: "monad-testnet",
|
|
70
|
+
Berachain: "berachain-mainnet",
|
|
71
|
+
Avalanche: "avax-mainnet",
|
|
72
|
+
BNB: "bnb-mainnet",
|
|
73
|
+
WorldChain: "worldchain-mainnet",
|
|
74
|
+
MegaETH: null,
|
|
75
|
+
Etherlink: null,
|
|
76
|
+
Hemi: null,
|
|
77
|
+
Lisk: null
|
|
93
78
|
};
|
|
94
79
|
function getDrpcUrl(network, apiKey, protocol) {
|
|
95
80
|
const net = DRPC_NETS[network];
|
|
@@ -97,7 +82,6 @@ function getDrpcUrl(network, apiKey, protocol) {
|
|
|
97
82
|
}
|
|
98
83
|
// Annotate the CommonJS export names for ESM import in node:
|
|
99
84
|
0 && (module.exports = {
|
|
100
|
-
createTransport,
|
|
101
85
|
getAlchemyUrl,
|
|
102
86
|
getDrpcUrl,
|
|
103
87
|
getProviderUrl
|
|
@@ -81,7 +81,6 @@ const chains = {
|
|
|
81
81
|
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
82
82
|
symbol: "USDC"
|
|
83
83
|
},
|
|
84
|
-
alchemyDomain: "eth-mainnet",
|
|
85
84
|
firstBlock: 18433056n
|
|
86
85
|
// AddressProvderV3 0x9ea7b04Da02a5373317D745c1571c84aaD03321D
|
|
87
86
|
},
|
|
@@ -99,7 +98,6 @@ const chains = {
|
|
|
99
98
|
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
100
99
|
symbol: "USDC"
|
|
101
100
|
},
|
|
102
|
-
alchemyDomain: "arb-mainnet",
|
|
103
101
|
firstBlock: 184650310n
|
|
104
102
|
},
|
|
105
103
|
"arbitrum-one-rpc"
|
|
@@ -116,7 +114,6 @@ const chains = {
|
|
|
116
114
|
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
117
115
|
symbol: "USDC"
|
|
118
116
|
},
|
|
119
|
-
alchemyDomain: "opt-mainnet",
|
|
120
117
|
firstBlock: 118410666n
|
|
121
118
|
},
|
|
122
119
|
"optimism-rpc"
|
|
@@ -130,8 +127,7 @@ const chains = {
|
|
|
130
127
|
wellKnownToken: {
|
|
131
128
|
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
132
129
|
symbol: "USDC"
|
|
133
|
-
}
|
|
134
|
-
alchemyDomain: "base-mainnet"
|
|
130
|
+
}
|
|
135
131
|
},
|
|
136
132
|
"base-rpc"
|
|
137
133
|
),
|
|
@@ -154,7 +150,6 @@ const chains = {
|
|
|
154
150
|
address: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
|
|
155
151
|
symbol: "USDC"
|
|
156
152
|
},
|
|
157
|
-
alchemyDomain: "sonic-mainnet",
|
|
158
153
|
firstBlock: 9779380n
|
|
159
154
|
}),
|
|
160
155
|
"sonic-rpc"
|
|
@@ -180,9 +175,8 @@ const chains = {
|
|
|
180
175
|
wellKnownToken: {
|
|
181
176
|
address: "0xf817257fed379853cDe0fa4F97AB987181B1E5Ea",
|
|
182
177
|
symbol: "USDC"
|
|
183
|
-
}
|
|
178
|
+
}
|
|
184
179
|
// TODO: has no block explorer API
|
|
185
|
-
alchemyDomain: "monad-testnet"
|
|
186
180
|
}),
|
|
187
181
|
Berachain: withPublicNode(
|
|
188
182
|
{
|
|
@@ -200,8 +194,7 @@ const chains = {
|
|
|
200
194
|
wellKnownToken: {
|
|
201
195
|
address: "0x549943e04f40284185054145c6e4e9568c1d3241",
|
|
202
196
|
symbol: "USDC.e"
|
|
203
|
-
}
|
|
204
|
-
alchemyDomain: "berachain-mainnet"
|
|
197
|
+
}
|
|
205
198
|
},
|
|
206
199
|
"berachain-rpc"
|
|
207
200
|
),
|
|
@@ -214,8 +207,7 @@ const chains = {
|
|
|
214
207
|
wellKnownToken: {
|
|
215
208
|
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
216
209
|
symbol: "USDC"
|
|
217
|
-
}
|
|
218
|
-
alchemyDomain: "avax-mainnet"
|
|
210
|
+
}
|
|
219
211
|
},
|
|
220
212
|
"avalanche-c-chain-rpc"
|
|
221
213
|
),
|
|
@@ -233,7 +225,6 @@ const chains = {
|
|
|
233
225
|
address: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
|
|
234
226
|
symbol: "USDC"
|
|
235
227
|
},
|
|
236
|
-
alchemyDomain: "bnb-mainnet",
|
|
237
228
|
firstBlock: 48761804n
|
|
238
229
|
},
|
|
239
230
|
"bsc-rpc"
|
|
@@ -246,8 +237,7 @@ const chains = {
|
|
|
246
237
|
wellKnownToken: {
|
|
247
238
|
address: "0x79a02482a880bce3f13e09da970dc34db4cd24d1",
|
|
248
239
|
symbol: "USDC"
|
|
249
|
-
}
|
|
250
|
-
alchemyDomain: "worldchain-mainnet"
|
|
240
|
+
}
|
|
251
241
|
// TODO: has no block explorer API
|
|
252
242
|
}),
|
|
253
243
|
Etherlink: (0, import_viem.defineChain)({
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BaseError,
|
|
3
|
+
HttpRequestError,
|
|
4
|
+
http,
|
|
5
|
+
RpcError
|
|
6
|
+
} from "viem";
|
|
7
|
+
import {
|
|
8
|
+
getProviderUrl
|
|
9
|
+
} from "./providers.js";
|
|
10
|
+
class NoAvailableTransportsError extends BaseError {
|
|
11
|
+
constructor() {
|
|
12
|
+
super("no available transports");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
class RevolverTransport {
|
|
16
|
+
#transports;
|
|
17
|
+
#index = 0;
|
|
18
|
+
#config;
|
|
19
|
+
overrides;
|
|
20
|
+
/**
|
|
21
|
+
* Create a new RevolverTransport
|
|
22
|
+
* RevolverTransport usues several RPC providers and rotates between them when requests fail
|
|
23
|
+
* Failed transport goes into temporary cooldown, after which it can be tried again
|
|
24
|
+
* When all transports are on cooldown, the transport will throw a NoAvailableTransportsError
|
|
25
|
+
* @param config
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
static create(config) {
|
|
29
|
+
const transport = new RevolverTransport({
|
|
30
|
+
...config
|
|
31
|
+
});
|
|
32
|
+
return (...args) => {
|
|
33
|
+
transport.overrides = args[0];
|
|
34
|
+
return transport;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.#config = { ...config };
|
|
39
|
+
const rpcUrls = /* @__PURE__ */ new Map();
|
|
40
|
+
const cooldowns = /* @__PURE__ */ new Map();
|
|
41
|
+
for (const { provider, keys, cooldown } of config.providers) {
|
|
42
|
+
for (const key of keys) {
|
|
43
|
+
const url = getProviderUrl(provider, config.network, key, "http");
|
|
44
|
+
if (url) {
|
|
45
|
+
rpcUrls.set(url, provider);
|
|
46
|
+
if (cooldown) {
|
|
47
|
+
cooldowns.set(url, cooldown);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
this.#transports = Array.from(rpcUrls.entries()).map(
|
|
53
|
+
([url, provider], i) => ({
|
|
54
|
+
provider,
|
|
55
|
+
transport: http(url, {
|
|
56
|
+
retryCount: config.retryCount,
|
|
57
|
+
retryDelay: config.retryDelay,
|
|
58
|
+
timeout: config.timeout,
|
|
59
|
+
batch: false,
|
|
60
|
+
key: `${provider}-${i}`,
|
|
61
|
+
name: `${provider}-${i}`,
|
|
62
|
+
onFetchRequest: this.#config.onRequest ? (...args) => this.#config.onRequest?.(`${provider}-${i}`, ...args) : void 0,
|
|
63
|
+
onFetchResponse: this.#config.onResponse ? (...args) => this.#config.onResponse?.(`${provider}-${i}`, ...args) : void 0
|
|
64
|
+
}),
|
|
65
|
+
cooldown: cooldowns.get(url) ?? 0
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
if (this.#transports.length === 0) {
|
|
69
|
+
throw new NoAvailableTransportsError();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
get value() {
|
|
73
|
+
return {
|
|
74
|
+
rotate: (reason) => this.rotate(reason),
|
|
75
|
+
currentTransportName: this.currentTransportName
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
request = async (r) => {
|
|
79
|
+
if (this.#transports.length === 1) {
|
|
80
|
+
return this.#transport({ ...this.overrides }).request(r);
|
|
81
|
+
}
|
|
82
|
+
do {
|
|
83
|
+
try {
|
|
84
|
+
const resp = await this.#transport({ ...this.overrides }).request(r);
|
|
85
|
+
return resp;
|
|
86
|
+
} catch (e) {
|
|
87
|
+
if (e instanceof RpcError || e instanceof HttpRequestError) {
|
|
88
|
+
await this.rotate(e);
|
|
89
|
+
} else {
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} while (this.#hasAvailableTransports);
|
|
94
|
+
throw new NoAvailableTransportsError();
|
|
95
|
+
};
|
|
96
|
+
get config() {
|
|
97
|
+
return {
|
|
98
|
+
key: "revolver",
|
|
99
|
+
name: "revolver",
|
|
100
|
+
type: "revolver",
|
|
101
|
+
request: this.request,
|
|
102
|
+
retryCount: this.#config.retryCount,
|
|
103
|
+
methods: void 0,
|
|
104
|
+
retryDelay: this.#config.retryDelay,
|
|
105
|
+
timeout: this.#config.timeout
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Manually rotate the transport
|
|
110
|
+
* @param reason
|
|
111
|
+
* @returns true if rotation was successful
|
|
112
|
+
*/
|
|
113
|
+
async rotate(reason) {
|
|
114
|
+
if (this.#transports.length === 1) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
this.#logger?.debug(
|
|
118
|
+
{
|
|
119
|
+
reason,
|
|
120
|
+
current: this.currentTransportName,
|
|
121
|
+
index: this.#index,
|
|
122
|
+
total: this.#transports.length
|
|
123
|
+
},
|
|
124
|
+
"rotating transport"
|
|
125
|
+
);
|
|
126
|
+
const oldTransportName = this.currentTransportName;
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
this.#transports[this.#index].cooldown = now + (this.#config.cooldown ?? 6e4);
|
|
129
|
+
for (let i = 0; i < this.#transports.length - 1; i++) {
|
|
130
|
+
this.#index = (this.#index + 1) % this.#transports.length;
|
|
131
|
+
if (this.#transports[this.#index].cooldown < now) {
|
|
132
|
+
this.#logger?.info(
|
|
133
|
+
{
|
|
134
|
+
current: this.currentTransportName,
|
|
135
|
+
index: this.#index,
|
|
136
|
+
total: this.#transports.length
|
|
137
|
+
},
|
|
138
|
+
"switched to next transport"
|
|
139
|
+
);
|
|
140
|
+
await this.#config.onRotateSuccess?.(
|
|
141
|
+
oldTransportName,
|
|
142
|
+
this.currentTransportName,
|
|
143
|
+
reason
|
|
144
|
+
);
|
|
145
|
+
return true;
|
|
146
|
+
} else {
|
|
147
|
+
this.#logger?.warn(
|
|
148
|
+
{
|
|
149
|
+
current: this.currentTransportName,
|
|
150
|
+
index: this.#index,
|
|
151
|
+
total: this.#transports.length
|
|
152
|
+
},
|
|
153
|
+
"transport is still on cooldown"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
await this.#config.onRotateFailed?.(reason);
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
get currentTransportName() {
|
|
161
|
+
return this.#transport({}).config.name;
|
|
162
|
+
}
|
|
163
|
+
get #logger() {
|
|
164
|
+
return this.#config.logger;
|
|
165
|
+
}
|
|
166
|
+
get #transport() {
|
|
167
|
+
return this.#transports[this.#index].transport;
|
|
168
|
+
}
|
|
169
|
+
get #hasAvailableTransports() {
|
|
170
|
+
const now = Date.now();
|
|
171
|
+
return this.#transports.some((t) => t.cooldown < now);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
export {
|
|
175
|
+
NoAvailableTransportsError,
|
|
176
|
+
RevolverTransport
|
|
177
|
+
};
|
package/dist/esm/dev/index.js
CHANGED
|
@@ -4,9 +4,10 @@ export * from "./calcLiquidatableLTs.js";
|
|
|
4
4
|
export * from "./claimFromFaucet.js";
|
|
5
5
|
export * from "./create2.js";
|
|
6
6
|
export * from "./createAnvilClient.js";
|
|
7
|
-
export * from "./createTransport.js";
|
|
8
7
|
export * from "./detectChain.js";
|
|
9
8
|
export * from "./EthCallSpy.js";
|
|
10
9
|
export * from "./ltUtils.js";
|
|
11
10
|
export * from "./migrateFaucet.js";
|
|
11
|
+
export * from "./providers.js";
|
|
12
|
+
export * from "./RevolverTransport.js";
|
|
12
13
|
export * from "./types.js";
|
|
@@ -1,32 +1,3 @@
|
|
|
1
|
-
import { fallback, http, webSocket } from "viem";
|
|
2
|
-
import { getChain } from "../sdk/index.js";
|
|
3
|
-
function createTransport(config) {
|
|
4
|
-
const { rpcProviders = [], protocol, network, ...rest } = config;
|
|
5
|
-
const rpcUrls = /* @__PURE__ */ new Map();
|
|
6
|
-
for (const { provider, keys } of rpcProviders) {
|
|
7
|
-
for (const key of keys) {
|
|
8
|
-
const url = getProviderUrl(provider, network, key, protocol);
|
|
9
|
-
if (url) {
|
|
10
|
-
rpcUrls.set(url, provider);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
const transports = Array.from(rpcUrls.entries()).map(
|
|
15
|
-
([url, provider], index) => protocol === "http" ? http(url, {
|
|
16
|
-
...rest,
|
|
17
|
-
key: `${provider}-${index}`,
|
|
18
|
-
name: `${provider}-${index}`
|
|
19
|
-
}) : webSocket(url, {
|
|
20
|
-
...rest,
|
|
21
|
-
key: `${provider}-${index}`,
|
|
22
|
-
name: `${provider}-${index}`
|
|
23
|
-
})
|
|
24
|
-
);
|
|
25
|
-
if (transports.length === 0) {
|
|
26
|
-
throw new Error("no fitting rpc urls found");
|
|
27
|
-
}
|
|
28
|
-
return transports.length > 1 ? fallback(transports) : transports[0];
|
|
29
|
-
}
|
|
30
1
|
function getProviderUrl(provider, network, apiKey, protocol) {
|
|
31
2
|
switch (provider) {
|
|
32
3
|
case "alchemy":
|
|
@@ -42,7 +13,7 @@ function getProviderUrl(provider, network, apiKey, protocol) {
|
|
|
42
13
|
}
|
|
43
14
|
}
|
|
44
15
|
function getAlchemyUrl(network, apiKey, protocol) {
|
|
45
|
-
const
|
|
16
|
+
const alchemyDomain = ALCHEMY_DOMAINS[network];
|
|
46
17
|
if (!alchemyDomain) {
|
|
47
18
|
return void 0;
|
|
48
19
|
}
|
|
@@ -61,16 +32,30 @@ const DRPC_NETS = {
|
|
|
61
32
|
Monad: "monad-testnet",
|
|
62
33
|
Hemi: "hemi",
|
|
63
34
|
Lisk: "lisk",
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
35
|
+
MegaETH: null,
|
|
36
|
+
Etherlink: null
|
|
37
|
+
};
|
|
38
|
+
const ALCHEMY_DOMAINS = {
|
|
39
|
+
Mainnet: "eth-mainnet",
|
|
40
|
+
Arbitrum: "arb-mainnet",
|
|
41
|
+
Optimism: "opt-mainnet",
|
|
42
|
+
Base: "base-mainnet",
|
|
43
|
+
Sonic: "sonic-mainnet",
|
|
44
|
+
Monad: "monad-testnet",
|
|
45
|
+
Berachain: "berachain-mainnet",
|
|
46
|
+
Avalanche: "avax-mainnet",
|
|
47
|
+
BNB: "bnb-mainnet",
|
|
48
|
+
WorldChain: "worldchain-mainnet",
|
|
49
|
+
MegaETH: null,
|
|
50
|
+
Etherlink: null,
|
|
51
|
+
Hemi: null,
|
|
52
|
+
Lisk: null
|
|
67
53
|
};
|
|
68
54
|
function getDrpcUrl(network, apiKey, protocol) {
|
|
69
55
|
const net = DRPC_NETS[network];
|
|
70
56
|
return net ? `${protocol}s://lb.drpc.org/${net}/${apiKey}` : void 0;
|
|
71
57
|
}
|
|
72
58
|
export {
|
|
73
|
-
createTransport,
|
|
74
59
|
getAlchemyUrl,
|
|
75
60
|
getDrpcUrl,
|
|
76
61
|
getProviderUrl
|
|
@@ -65,7 +65,6 @@ const chains = {
|
|
|
65
65
|
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
66
66
|
symbol: "USDC"
|
|
67
67
|
},
|
|
68
|
-
alchemyDomain: "eth-mainnet",
|
|
69
68
|
firstBlock: 18433056n
|
|
70
69
|
// AddressProvderV3 0x9ea7b04Da02a5373317D745c1571c84aaD03321D
|
|
71
70
|
},
|
|
@@ -83,7 +82,6 @@ const chains = {
|
|
|
83
82
|
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
84
83
|
symbol: "USDC"
|
|
85
84
|
},
|
|
86
|
-
alchemyDomain: "arb-mainnet",
|
|
87
85
|
firstBlock: 184650310n
|
|
88
86
|
},
|
|
89
87
|
"arbitrum-one-rpc"
|
|
@@ -100,7 +98,6 @@ const chains = {
|
|
|
100
98
|
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
101
99
|
symbol: "USDC"
|
|
102
100
|
},
|
|
103
|
-
alchemyDomain: "opt-mainnet",
|
|
104
101
|
firstBlock: 118410666n
|
|
105
102
|
},
|
|
106
103
|
"optimism-rpc"
|
|
@@ -114,8 +111,7 @@ const chains = {
|
|
|
114
111
|
wellKnownToken: {
|
|
115
112
|
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
116
113
|
symbol: "USDC"
|
|
117
|
-
}
|
|
118
|
-
alchemyDomain: "base-mainnet"
|
|
114
|
+
}
|
|
119
115
|
},
|
|
120
116
|
"base-rpc"
|
|
121
117
|
),
|
|
@@ -138,7 +134,6 @@ const chains = {
|
|
|
138
134
|
address: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
|
|
139
135
|
symbol: "USDC"
|
|
140
136
|
},
|
|
141
|
-
alchemyDomain: "sonic-mainnet",
|
|
142
137
|
firstBlock: 9779380n
|
|
143
138
|
}),
|
|
144
139
|
"sonic-rpc"
|
|
@@ -164,9 +159,8 @@ const chains = {
|
|
|
164
159
|
wellKnownToken: {
|
|
165
160
|
address: "0xf817257fed379853cDe0fa4F97AB987181B1E5Ea",
|
|
166
161
|
symbol: "USDC"
|
|
167
|
-
}
|
|
162
|
+
}
|
|
168
163
|
// TODO: has no block explorer API
|
|
169
|
-
alchemyDomain: "monad-testnet"
|
|
170
164
|
}),
|
|
171
165
|
Berachain: withPublicNode(
|
|
172
166
|
{
|
|
@@ -184,8 +178,7 @@ const chains = {
|
|
|
184
178
|
wellKnownToken: {
|
|
185
179
|
address: "0x549943e04f40284185054145c6e4e9568c1d3241",
|
|
186
180
|
symbol: "USDC.e"
|
|
187
|
-
}
|
|
188
|
-
alchemyDomain: "berachain-mainnet"
|
|
181
|
+
}
|
|
189
182
|
},
|
|
190
183
|
"berachain-rpc"
|
|
191
184
|
),
|
|
@@ -198,8 +191,7 @@ const chains = {
|
|
|
198
191
|
wellKnownToken: {
|
|
199
192
|
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
200
193
|
symbol: "USDC"
|
|
201
|
-
}
|
|
202
|
-
alchemyDomain: "avax-mainnet"
|
|
194
|
+
}
|
|
203
195
|
},
|
|
204
196
|
"avalanche-c-chain-rpc"
|
|
205
197
|
),
|
|
@@ -217,7 +209,6 @@ const chains = {
|
|
|
217
209
|
address: "0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d",
|
|
218
210
|
symbol: "USDC"
|
|
219
211
|
},
|
|
220
|
-
alchemyDomain: "bnb-mainnet",
|
|
221
212
|
firstBlock: 48761804n
|
|
222
213
|
},
|
|
223
214
|
"bsc-rpc"
|
|
@@ -230,8 +221,7 @@ const chains = {
|
|
|
230
221
|
wellKnownToken: {
|
|
231
222
|
address: "0x79a02482a880bce3f13e09da970dc34db4cd24d1",
|
|
232
223
|
symbol: "USDC"
|
|
233
|
-
}
|
|
234
|
-
alchemyDomain: "worldchain-mainnet"
|
|
224
|
+
}
|
|
235
225
|
// TODO: has no block explorer API
|
|
236
226
|
}),
|
|
237
227
|
Etherlink: defineChain({
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BaseError, type ClientConfig, type EIP1193RequestFn, type Transport, type TransportConfig } from "viem";
|
|
2
|
+
import type { HttpRpcClientOptions } from "viem/utils";
|
|
3
|
+
import type { ILogger, NetworkType } from "../sdk/index.js";
|
|
4
|
+
import { type ProviderConfig } from "./providers.js";
|
|
5
|
+
type OnRequestFn = (providerName: string, ...args: Parameters<Required<HttpRpcClientOptions>["onRequest"]>) => ReturnType<Required<HttpRpcClientOptions>["onRequest"]>;
|
|
6
|
+
type OnResponseFn = (providerName: string, ...args: Parameters<Required<HttpRpcClientOptions>["onResponse"]>) => ReturnType<Required<HttpRpcClientOptions>["onResponse"]>;
|
|
7
|
+
export interface RevolverTransportConfig {
|
|
8
|
+
network: NetworkType;
|
|
9
|
+
providers: ProviderConfig[];
|
|
10
|
+
logger?: ILogger;
|
|
11
|
+
key?: TransportConfig["key"] | undefined;
|
|
12
|
+
name?: TransportConfig["name"] | undefined;
|
|
13
|
+
pollingInterval?: ClientConfig["pollingInterval"] | undefined;
|
|
14
|
+
retryCount?: TransportConfig["retryCount"] | undefined;
|
|
15
|
+
retryDelay?: TransportConfig["retryDelay"] | undefined;
|
|
16
|
+
timeout?: TransportConfig["timeout"] | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Spying function that also returns provider name in additional to the request
|
|
19
|
+
*/
|
|
20
|
+
onRequest?: OnRequestFn;
|
|
21
|
+
/**
|
|
22
|
+
* Spying function that also returns provider name in additional to the response
|
|
23
|
+
*/
|
|
24
|
+
onResponse?: OnResponseFn;
|
|
25
|
+
/**
|
|
26
|
+
* Callback that is called when the transport is rotated
|
|
27
|
+
*/
|
|
28
|
+
onRotateSuccess?: (oldTransportName: string, newTransportName: string, reason?: BaseError) => void | Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Callback that is called when the transport cannot be rotated
|
|
31
|
+
*/
|
|
32
|
+
onRotateFailed?: (reason?: BaseError) => void | Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* How long, in milliseconds, to wait before try this transport again
|
|
35
|
+
*/
|
|
36
|
+
cooldown?: number | undefined;
|
|
37
|
+
}
|
|
38
|
+
export declare class NoAvailableTransportsError extends BaseError {
|
|
39
|
+
constructor();
|
|
40
|
+
}
|
|
41
|
+
export interface RevolverTransportValue {
|
|
42
|
+
/**
|
|
43
|
+
* Manually rotate the transport
|
|
44
|
+
* @param reason
|
|
45
|
+
* @returns true if rotation was successful
|
|
46
|
+
*/
|
|
47
|
+
rotate: (reason?: BaseError) => Promise<boolean>;
|
|
48
|
+
/**
|
|
49
|
+
* The name of the current transport
|
|
50
|
+
*/
|
|
51
|
+
currentTransportName: string;
|
|
52
|
+
}
|
|
53
|
+
export declare class RevolverTransport implements ReturnType<Transport<"revolver", RevolverTransportValue>> {
|
|
54
|
+
#private;
|
|
55
|
+
private overrides?;
|
|
56
|
+
/**
|
|
57
|
+
* Create a new RevolverTransport
|
|
58
|
+
* RevolverTransport usues several RPC providers and rotates between them when requests fail
|
|
59
|
+
* Failed transport goes into temporary cooldown, after which it can be tried again
|
|
60
|
+
* When all transports are on cooldown, the transport will throw a NoAvailableTransportsError
|
|
61
|
+
* @param config
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
static create(config: RevolverTransportConfig): Transport<"revolver">;
|
|
65
|
+
constructor(config: RevolverTransportConfig);
|
|
66
|
+
get value(): RevolverTransportValue;
|
|
67
|
+
request: EIP1193RequestFn;
|
|
68
|
+
get config(): TransportConfig<"revolver">;
|
|
69
|
+
/**
|
|
70
|
+
* Manually rotate the transport
|
|
71
|
+
* @param reason
|
|
72
|
+
* @returns true if rotation was successful
|
|
73
|
+
*/
|
|
74
|
+
rotate(reason?: BaseError): Promise<boolean>;
|
|
75
|
+
get currentTransportName(): string;
|
|
76
|
+
}
|
|
77
|
+
export {};
|
|
@@ -4,9 +4,10 @@ export * from "./calcLiquidatableLTs.js";
|
|
|
4
4
|
export * from "./claimFromFaucet.js";
|
|
5
5
|
export * from "./create2.js";
|
|
6
6
|
export * from "./createAnvilClient.js";
|
|
7
|
-
export * from "./createTransport.js";
|
|
8
7
|
export * from "./detectChain.js";
|
|
9
8
|
export * from "./EthCallSpy.js";
|
|
10
9
|
export * from "./ltUtils.js";
|
|
11
10
|
export * from "./migrateFaucet.js";
|
|
11
|
+
export * from "./providers.js";
|
|
12
|
+
export * from "./RevolverTransport.js";
|
|
12
13
|
export * from "./types.js";
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { HttpTransportConfig,
|
|
1
|
+
import type { HttpTransportConfig, WebSocketTransportConfig } from "viem";
|
|
2
2
|
import type { NetworkType } from "../sdk/index.js";
|
|
3
3
|
export type RpcProvider = "alchemy" | "drpc" | "custom";
|
|
4
4
|
export interface ProviderConfig {
|
|
5
5
|
provider: RpcProvider;
|
|
6
6
|
keys: string[];
|
|
7
|
+
cooldown?: number;
|
|
7
8
|
}
|
|
8
9
|
export interface CreateTransportURLOptions {
|
|
9
10
|
/**
|
|
@@ -19,13 +20,6 @@ export type CreateWSTransportConfig = {
|
|
|
19
20
|
protocol: "ws";
|
|
20
21
|
} & WebSocketTransportConfig & CreateTransportURLOptions;
|
|
21
22
|
export type CreateTransportConfig = CreateHTTPTransportConfig | CreateWSTransportConfig;
|
|
22
|
-
/**
|
|
23
|
-
* Helper method to create viem Transport using API keys of well-known RPC providers and explicit fallback URLs
|
|
24
|
-
* Currently only supports Alchemy
|
|
25
|
-
* @param config
|
|
26
|
-
* @returns
|
|
27
|
-
*/
|
|
28
|
-
export declare function createTransport(config: CreateTransportConfig): Transport;
|
|
29
23
|
export declare function getProviderUrl(provider: RpcProvider, network: NetworkType, apiKey: string, protocol: "http" | "ws"): string | undefined;
|
|
30
24
|
export declare function getAlchemyUrl(network: NetworkType, apiKey: string, protocol: "http" | "ws"): string | undefined;
|
|
31
25
|
export declare function getDrpcUrl(network: NetworkType, apiKey: string, protocol: "http" | "ws"): string | undefined;
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|