@gibs/common 1.13.0 → 1.17.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/README.md +34 -4
- package/cache/package.json +6 -0
- package/client/package.json +6 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +27 -0
- package/dist/cjs/assert.d.ts +18 -0
- package/dist/cjs/assert.js +31 -0
- package/dist/cjs/cache.d.ts +39 -0
- package/dist/cjs/cache.js +77 -0
- package/dist/cjs/client.d.ts +48 -0
- package/dist/cjs/client.js +171 -0
- package/dist/cjs/erc20.d.ts +18 -0
- package/dist/cjs/erc20.js +39 -0
- package/dist/cjs/index.d.ts +8 -0
- package/dist/cjs/index.js +24 -0
- package/dist/cjs/multicall.d.ts +56 -0
- package/dist/cjs/multicall.js +217 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/rpcPacer.d.ts +88 -0
- package/dist/cjs/rpcPacer.js +158 -0
- package/dist/cjs/serialize.d.ts +19 -0
- package/dist/cjs/serialize.js +48 -0
- package/dist/cjs/types.d.ts +11 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/url.d.ts +110 -0
- package/dist/cjs/url.js +292 -0
- package/dist/client.js +2 -1
- package/dist/erc20.d.ts +3 -5
- package/dist/erc20.js +5 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/multicall.js +1 -2
- package/dist/url.d.ts +110 -0
- package/dist/url.js +281 -0
- package/erc20/package.json +6 -0
- package/llms.txt +89 -0
- package/multicall/package.json +6 -0
- package/package.json +205 -8
- package/serialize/package.json +6 -0
- package/types/package.json +6 -0
- package/url/package.json +6 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request addresses, built and read with plain string work.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
|
|
5
|
+
* `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
|
|
6
|
+
* `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
|
|
7
|
+
* implemented". `searchParams` is always a new, empty list, so a reader of it
|
|
8
|
+
* silently sees no parameters. `URLSearchParams` can only append and
|
|
9
|
+
* serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
|
|
10
|
+
* its constructor is ignored. A package that uses either class needs a
|
|
11
|
+
* polyfill in a wallet. These functions need nothing from the runtime but
|
|
12
|
+
* `encodeURIComponent` and `decodeURIComponent`.
|
|
13
|
+
*
|
|
14
|
+
* Their output matches what the WHATWG classes produce for the addresses the
|
|
15
|
+
* Gibs packages build: a lowercase `http:` or `https:` origin with a plain
|
|
16
|
+
* path. A query is encoded exactly as `URLSearchParams` encodes it, as
|
|
17
|
+
* `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
|
|
18
|
+
* digits and `*-._` stay as they are. They do not normalize the address
|
|
19
|
+
* itself: an uppercase host or a `..` path segment passes through as written.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Query parameters, as a record or as ordered name and value pairs.
|
|
23
|
+
*
|
|
24
|
+
* In a record, a value of `undefined` leaves that parameter out, so an
|
|
25
|
+
* optional parameter can be written in place. Pairs keep their order and may
|
|
26
|
+
* repeat a name.
|
|
27
|
+
*/
|
|
28
|
+
export type QueryParameters = Readonly<Record<string, string | undefined>> | ReadonlyArray<readonly [string, string]>;
|
|
29
|
+
/** One query parameter: its decoded name and value. */
|
|
30
|
+
export type QueryEntry = readonly [name: string, value: string];
|
|
31
|
+
/** The parts of an address, as the WHATWG `URL` getters name them. */
|
|
32
|
+
export type UrlParts = {
|
|
33
|
+
/** The scheme in lowercase, with no colon, such as `https`. Empty when the address has none. */
|
|
34
|
+
readonly scheme: string;
|
|
35
|
+
/** The host in lowercase, with the port unless it is the scheme's default. Empty when the address has no authority. */
|
|
36
|
+
readonly host: string;
|
|
37
|
+
/** The path. `/` for an address with an authority and no path. */
|
|
38
|
+
readonly path: string;
|
|
39
|
+
/** The query, with no leading `?`. */
|
|
40
|
+
readonly query: string;
|
|
41
|
+
/** The fragment, with no leading `#`. */
|
|
42
|
+
readonly fragment: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Encodes a query name or value exactly as `URLSearchParams.toString` does.
|
|
46
|
+
*
|
|
47
|
+
* @param text - the raw name or value
|
|
48
|
+
* @returns the encoded text
|
|
49
|
+
*/
|
|
50
|
+
export declare const encodeQueryComponent: (text: string) => string;
|
|
51
|
+
/**
|
|
52
|
+
* Decodes a query name or value as the WHATWG URL standard says
|
|
53
|
+
* `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
|
|
54
|
+
* starts no valid sequence stays as it is, and each malformed UTF-8 part, or
|
|
55
|
+
* lone surrogate half, becomes U+FFFD.
|
|
56
|
+
*
|
|
57
|
+
* @param text - the encoded name or value
|
|
58
|
+
* @returns the decoded text
|
|
59
|
+
*/
|
|
60
|
+
export declare const decodeQueryComponent: (text: string) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Lists the parameters of a query, decoded and in order.
|
|
63
|
+
*
|
|
64
|
+
* @param query - a query, with or without its leading `?`
|
|
65
|
+
* @returns each parameter's name and value; a parameter with no `=` has an empty value
|
|
66
|
+
*/
|
|
67
|
+
export declare const parseQuery: (query: string) => readonly QueryEntry[];
|
|
68
|
+
/**
|
|
69
|
+
* Writes a query, as `new URLSearchParams(parameters).toString()` does.
|
|
70
|
+
*
|
|
71
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
72
|
+
* @returns the encoded query, with no leading `?`; empty for no parameters
|
|
73
|
+
*/
|
|
74
|
+
export declare const buildQuery: (parameters: QueryParameters) => string;
|
|
75
|
+
/**
|
|
76
|
+
* Sets one parameter in a query, as `URLSearchParams.set` does: the first
|
|
77
|
+
* parameter of that name takes the value and any later ones are removed, or
|
|
78
|
+
* the parameter is appended when none has the name.
|
|
79
|
+
*
|
|
80
|
+
* @param query - an encoded query, with or without its leading `?`
|
|
81
|
+
* @param name - the parameter's name
|
|
82
|
+
* @param value - its value
|
|
83
|
+
* @returns the encoded query, with no leading `?`
|
|
84
|
+
*/
|
|
85
|
+
export declare const setQueryParameter: (query: string, name: string, value: string) => string;
|
|
86
|
+
/**
|
|
87
|
+
* Adds query parameters to an address, as `url.searchParams.set` does for
|
|
88
|
+
* each one in turn. A query already on the address is kept, and a fragment
|
|
89
|
+
* stays at the end.
|
|
90
|
+
*
|
|
91
|
+
* @param address - the address, with or without a query
|
|
92
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
93
|
+
* @returns the address with the parameters set; the address unchanged when there are none
|
|
94
|
+
*/
|
|
95
|
+
export declare const buildUrl: (address: string, parameters: QueryParameters) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Splits an address into its parts, without the WHATWG `URL` class.
|
|
98
|
+
*
|
|
99
|
+
* @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
|
|
100
|
+
* @returns the parts; see {@link UrlParts}
|
|
101
|
+
*/
|
|
102
|
+
export declare const splitUrl: (address: string) => UrlParts;
|
|
103
|
+
/**
|
|
104
|
+
* Says whether a value is an absolute `http:` or `https:` address with a host.
|
|
105
|
+
* It reads the scheme by its prefix, so it needs no `URL` class.
|
|
106
|
+
*
|
|
107
|
+
* @param value - any string
|
|
108
|
+
* @returns true for an absolute `http:` or `https:` address
|
|
109
|
+
*/
|
|
110
|
+
export declare const isHttpUrl: (value: string) => boolean;
|
package/dist/cjs/url.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHttpUrl = exports.splitUrl = exports.buildUrl = exports.setQueryParameter = exports.buildQuery = exports.parseQuery = exports.decodeQueryComponent = exports.encodeQueryComponent = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Request addresses, built and read with plain string work.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
|
|
8
|
+
* `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
|
|
9
|
+
* `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
|
|
10
|
+
* implemented". `searchParams` is always a new, empty list, so a reader of it
|
|
11
|
+
* silently sees no parameters. `URLSearchParams` can only append and
|
|
12
|
+
* serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
|
|
13
|
+
* its constructor is ignored. A package that uses either class needs a
|
|
14
|
+
* polyfill in a wallet. These functions need nothing from the runtime but
|
|
15
|
+
* `encodeURIComponent` and `decodeURIComponent`.
|
|
16
|
+
*
|
|
17
|
+
* Their output matches what the WHATWG classes produce for the addresses the
|
|
18
|
+
* Gibs packages build: a lowercase `http:` or `https:` origin with a plain
|
|
19
|
+
* path. A query is encoded exactly as `URLSearchParams` encodes it, as
|
|
20
|
+
* `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
|
|
21
|
+
* digits and `*-._` stay as they are. They do not normalize the address
|
|
22
|
+
* itself: an uppercase host or a `..` path segment passes through as written.
|
|
23
|
+
*/
|
|
24
|
+
/** A surrogate pair, or a lone surrogate half. */
|
|
25
|
+
const surrogates = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDFFF]/g;
|
|
26
|
+
/** The characters `encodeURIComponent` keeps that a form encoder escapes. */
|
|
27
|
+
const formEscapedCharacters = /[!'()~]/g;
|
|
28
|
+
/** A run of percent-encoded bytes. */
|
|
29
|
+
const percentEncodedRun = /(?:%[0-9A-Fa-f]{2})+/g;
|
|
30
|
+
/** The port each special scheme leaves out of its host. */
|
|
31
|
+
const defaultPorts = Object.freeze({
|
|
32
|
+
http: '80',
|
|
33
|
+
https: '443',
|
|
34
|
+
ws: '80',
|
|
35
|
+
wss: '443',
|
|
36
|
+
ftp: '21',
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* Replaces each lone surrogate half with U+FFFD, as the WHATWG classes do
|
|
40
|
+
* before they encode. `encodeURIComponent` would throw on one instead.
|
|
41
|
+
*
|
|
42
|
+
* @param text - any string
|
|
43
|
+
* @returns the string, well formed
|
|
44
|
+
*/
|
|
45
|
+
const toWellFormed = (text) => text.replace(surrogates, (match) => (match.length === 2 ? match : '�'));
|
|
46
|
+
/**
|
|
47
|
+
* Percent-encodes one character as two uppercase hex digits.
|
|
48
|
+
*
|
|
49
|
+
* @param character - a single ASCII character
|
|
50
|
+
* @returns the character as `%XX`
|
|
51
|
+
*/
|
|
52
|
+
const percentEncodeCharacter = (character) => `%${character.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`;
|
|
53
|
+
/**
|
|
54
|
+
* Encodes a query name or value exactly as `URLSearchParams.toString` does.
|
|
55
|
+
*
|
|
56
|
+
* @param text - the raw name or value
|
|
57
|
+
* @returns the encoded text
|
|
58
|
+
*/
|
|
59
|
+
const encodeQueryComponent = (text) => encodeURIComponent(toWellFormed(text))
|
|
60
|
+
.replace(formEscapedCharacters, percentEncodeCharacter)
|
|
61
|
+
.replace(/%20/g, '+');
|
|
62
|
+
exports.encodeQueryComponent = encodeQueryComponent;
|
|
63
|
+
/**
|
|
64
|
+
* Counts the continuation bytes a UTF-8 lead byte announces, and the bounds
|
|
65
|
+
* the first of them must fall in. The bounds reject overlong forms and
|
|
66
|
+
* surrogates, as the WHATWG decoder does.
|
|
67
|
+
*
|
|
68
|
+
* @param lead - the first byte of a sequence
|
|
69
|
+
* @returns the count, the lead byte's own bits, and the first continuation byte's bounds; null for a byte that cannot start one
|
|
70
|
+
*/
|
|
71
|
+
const utf8Lead = (lead) => {
|
|
72
|
+
if (lead >= 0xc2 && lead <= 0xdf)
|
|
73
|
+
return { count: 1, bits: lead & 0x1f, lower: 0x80, upper: 0xbf };
|
|
74
|
+
if (lead === 0xe0)
|
|
75
|
+
return { count: 2, bits: lead & 0x0f, lower: 0xa0, upper: 0xbf };
|
|
76
|
+
if (lead === 0xed)
|
|
77
|
+
return { count: 2, bits: lead & 0x0f, lower: 0x80, upper: 0x9f };
|
|
78
|
+
if (lead >= 0xe1 && lead <= 0xef)
|
|
79
|
+
return { count: 2, bits: lead & 0x0f, lower: 0x80, upper: 0xbf };
|
|
80
|
+
if (lead === 0xf0)
|
|
81
|
+
return { count: 3, bits: lead & 0x07, lower: 0x90, upper: 0xbf };
|
|
82
|
+
if (lead === 0xf4)
|
|
83
|
+
return { count: 3, bits: lead & 0x07, lower: 0x80, upper: 0x8f };
|
|
84
|
+
if (lead >= 0xf1 && lead <= 0xf3)
|
|
85
|
+
return { count: 3, bits: lead & 0x07, lower: 0x80, upper: 0xbf };
|
|
86
|
+
return null;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Decodes UTF-8 bytes as the WHATWG decoder does: each malformed part becomes
|
|
90
|
+
* one U+FFFD, and a byte that ends a malformed part is read again as the
|
|
91
|
+
* start of the next. Only reached when `decodeURIComponent` refuses a run.
|
|
92
|
+
*
|
|
93
|
+
* @param bytes - the bytes
|
|
94
|
+
* @returns the decoded text
|
|
95
|
+
*/
|
|
96
|
+
const decodeUtf8Leniently = (bytes) => {
|
|
97
|
+
const decode = (index, decoded) => {
|
|
98
|
+
if (index >= bytes.length)
|
|
99
|
+
return decoded;
|
|
100
|
+
const lead = bytes[index];
|
|
101
|
+
if (lead < 0x80)
|
|
102
|
+
return decode(index + 1, decoded + String.fromCharCode(lead));
|
|
103
|
+
const shape = utf8Lead(lead);
|
|
104
|
+
if (shape === null)
|
|
105
|
+
return decode(index + 1, `${decoded}�`);
|
|
106
|
+
const readContinuation = (offset, codePoint) => {
|
|
107
|
+
if (offset > shape.count)
|
|
108
|
+
return { codePoint, length: offset };
|
|
109
|
+
const byte = bytes[index + offset];
|
|
110
|
+
const lower = offset === 1 ? shape.lower : 0x80;
|
|
111
|
+
const upper = offset === 1 ? shape.upper : 0xbf;
|
|
112
|
+
if (byte === undefined || byte < lower || byte > upper)
|
|
113
|
+
return { codePoint: -1, length: offset };
|
|
114
|
+
return readContinuation(offset + 1, (codePoint << 6) | (byte & 0x3f));
|
|
115
|
+
};
|
|
116
|
+
const { codePoint, length } = readContinuation(1, shape.bits);
|
|
117
|
+
if (codePoint === -1)
|
|
118
|
+
return decode(index + length, `${decoded}�`);
|
|
119
|
+
return decode(index + length, decoded + String.fromCodePoint(codePoint));
|
|
120
|
+
};
|
|
121
|
+
return decode(0, '');
|
|
122
|
+
};
|
|
123
|
+
/**
|
|
124
|
+
* Decodes one run of `%XX` bytes.
|
|
125
|
+
*
|
|
126
|
+
* @param run - consecutive `%XX` sequences
|
|
127
|
+
* @returns the text they encode
|
|
128
|
+
*/
|
|
129
|
+
const decodePercentRun = (run) => {
|
|
130
|
+
try {
|
|
131
|
+
return decodeURIComponent(run);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return decodeUtf8Leniently(run.slice(1).split('%').map((pair) => Number.parseInt(pair, 16)));
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Decodes a query name or value as the WHATWG URL standard says
|
|
139
|
+
* `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
|
|
140
|
+
* starts no valid sequence stays as it is, and each malformed UTF-8 part, or
|
|
141
|
+
* lone surrogate half, becomes U+FFFD.
|
|
142
|
+
*
|
|
143
|
+
* @param text - the encoded name or value
|
|
144
|
+
* @returns the decoded text
|
|
145
|
+
*/
|
|
146
|
+
const decodeQueryComponent = (text) => toWellFormed(text).replace(/\+/g, ' ').replace(percentEncodedRun, decodePercentRun);
|
|
147
|
+
exports.decodeQueryComponent = decodeQueryComponent;
|
|
148
|
+
/**
|
|
149
|
+
* Lists the parameters of a query, decoded and in order.
|
|
150
|
+
*
|
|
151
|
+
* @param query - a query, with or without its leading `?`
|
|
152
|
+
* @returns each parameter's name and value; a parameter with no `=` has an empty value
|
|
153
|
+
*/
|
|
154
|
+
const parseQuery = (query) => query
|
|
155
|
+
.replace(/^\?/, '')
|
|
156
|
+
.split('&')
|
|
157
|
+
.filter((part) => part !== '')
|
|
158
|
+
.map((part) => {
|
|
159
|
+
const equalsAt = part.indexOf('=');
|
|
160
|
+
const name = equalsAt === -1 ? part : part.slice(0, equalsAt);
|
|
161
|
+
const value = equalsAt === -1 ? '' : part.slice(equalsAt + 1);
|
|
162
|
+
return [(0, exports.decodeQueryComponent)(name), (0, exports.decodeQueryComponent)(value)];
|
|
163
|
+
});
|
|
164
|
+
exports.parseQuery = parseQuery;
|
|
165
|
+
/**
|
|
166
|
+
* Turns query parameters into ordered pairs.
|
|
167
|
+
*
|
|
168
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
169
|
+
* @returns the pairs, with every `undefined` record value left out
|
|
170
|
+
*/
|
|
171
|
+
const toEntries = (parameters) => {
|
|
172
|
+
if (Array.isArray(parameters))
|
|
173
|
+
return parameters;
|
|
174
|
+
return Object.entries(parameters).filter((entry) => entry[1] !== undefined);
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Writes a query, as `new URLSearchParams(parameters).toString()` does.
|
|
178
|
+
*
|
|
179
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
180
|
+
* @returns the encoded query, with no leading `?`; empty for no parameters
|
|
181
|
+
*/
|
|
182
|
+
const buildQuery = (parameters) => toEntries(parameters)
|
|
183
|
+
.map(([name, value]) => `${(0, exports.encodeQueryComponent)(name)}=${(0, exports.encodeQueryComponent)(value)}`)
|
|
184
|
+
.join('&');
|
|
185
|
+
exports.buildQuery = buildQuery;
|
|
186
|
+
/**
|
|
187
|
+
* Sets one parameter in a query, as `URLSearchParams.set` does: the first
|
|
188
|
+
* parameter of that name takes the value and any later ones are removed, or
|
|
189
|
+
* the parameter is appended when none has the name.
|
|
190
|
+
*
|
|
191
|
+
* @param query - an encoded query, with or without its leading `?`
|
|
192
|
+
* @param name - the parameter's name
|
|
193
|
+
* @param value - its value
|
|
194
|
+
* @returns the encoded query, with no leading `?`
|
|
195
|
+
*/
|
|
196
|
+
const setQueryParameter = (query, name, value) => {
|
|
197
|
+
const entries = (0, exports.parseQuery)(query);
|
|
198
|
+
const firstAt = entries.findIndex(([entryName]) => entryName === name);
|
|
199
|
+
if (firstAt === -1)
|
|
200
|
+
return (0, exports.buildQuery)([...entries, [name, value]]);
|
|
201
|
+
return (0, exports.buildQuery)(entries.flatMap((entry, index) => {
|
|
202
|
+
if (index === firstAt)
|
|
203
|
+
return [[name, value]];
|
|
204
|
+
return entry[0] === name ? [] : [entry];
|
|
205
|
+
}));
|
|
206
|
+
};
|
|
207
|
+
exports.setQueryParameter = setQueryParameter;
|
|
208
|
+
/**
|
|
209
|
+
* Splits an address at its first `#` and then at its first `?`.
|
|
210
|
+
*
|
|
211
|
+
* @param address - any address
|
|
212
|
+
* @returns everything before the query, the query and the fragment, each without its delimiter
|
|
213
|
+
*/
|
|
214
|
+
const splitAtQuery = (address) => {
|
|
215
|
+
const fragmentAt = address.indexOf('#');
|
|
216
|
+
const beforeFragment = fragmentAt === -1 ? address : address.slice(0, fragmentAt);
|
|
217
|
+
const fragment = fragmentAt === -1 ? '' : address.slice(fragmentAt + 1);
|
|
218
|
+
const queryAt = beforeFragment.indexOf('?');
|
|
219
|
+
if (queryAt === -1)
|
|
220
|
+
return { beforeQuery: beforeFragment, query: '', fragment };
|
|
221
|
+
return { beforeQuery: beforeFragment.slice(0, queryAt), query: beforeFragment.slice(queryAt + 1), fragment };
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Adds query parameters to an address, as `url.searchParams.set` does for
|
|
225
|
+
* each one in turn. A query already on the address is kept, and a fragment
|
|
226
|
+
* stays at the end.
|
|
227
|
+
*
|
|
228
|
+
* @param address - the address, with or without a query
|
|
229
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
230
|
+
* @returns the address with the parameters set; the address unchanged when there are none
|
|
231
|
+
*/
|
|
232
|
+
const buildUrl = (address, parameters) => {
|
|
233
|
+
const entries = toEntries(parameters);
|
|
234
|
+
if (entries.length === 0)
|
|
235
|
+
return address;
|
|
236
|
+
const { beforeQuery, query, fragment } = splitAtQuery(address);
|
|
237
|
+
const updated = entries.reduce((current, [name, value]) => (0, exports.setQueryParameter)(current, name, value), query);
|
|
238
|
+
return `${beforeQuery}?${updated}${fragment === '' ? '' : `#${fragment}`}`;
|
|
239
|
+
};
|
|
240
|
+
exports.buildUrl = buildUrl;
|
|
241
|
+
/**
|
|
242
|
+
* Reads the host the WHATWG `URL` reports from an authority: lowercase, with
|
|
243
|
+
* any user name and password dropped, and the port dropped when it is the
|
|
244
|
+
* scheme's default.
|
|
245
|
+
*
|
|
246
|
+
* @param authority - the text between `//` and the path
|
|
247
|
+
* @param scheme - the lowercase scheme
|
|
248
|
+
* @returns the host
|
|
249
|
+
*/
|
|
250
|
+
const hostOfAuthority = (authority, scheme) => {
|
|
251
|
+
const hostAndPort = authority.slice(authority.lastIndexOf('@') + 1).toLowerCase();
|
|
252
|
+
const port = /:(\d*)$/.exec(hostAndPort);
|
|
253
|
+
if (port === null)
|
|
254
|
+
return hostAndPort;
|
|
255
|
+
const hostname = hostAndPort.slice(0, port.index);
|
|
256
|
+
if (port[1] === '')
|
|
257
|
+
return hostname;
|
|
258
|
+
const portNumber = String(Number(port[1]));
|
|
259
|
+
return portNumber === defaultPorts[scheme] ? hostname : `${hostname}:${portNumber}`;
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Splits an address into its parts, without the WHATWG `URL` class.
|
|
263
|
+
*
|
|
264
|
+
* @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
|
|
265
|
+
* @returns the parts; see {@link UrlParts}
|
|
266
|
+
*/
|
|
267
|
+
const splitUrl = (address) => {
|
|
268
|
+
const trimmed = address.replace(/^[\u0000- ]+|[\u0000- ]+$/g, '');
|
|
269
|
+
const { beforeQuery, query, fragment } = splitAtQuery(trimmed);
|
|
270
|
+
const schemeMatch = /^([A-Za-z][A-Za-z0-9+.-]*):/.exec(beforeQuery);
|
|
271
|
+
const scheme = schemeMatch === null ? '' : schemeMatch[1].toLowerCase();
|
|
272
|
+
const rest = schemeMatch === null ? beforeQuery : beforeQuery.slice(schemeMatch[0].length);
|
|
273
|
+
if (!rest.startsWith('//'))
|
|
274
|
+
return { scheme, host: '', path: rest, query, fragment };
|
|
275
|
+
const pathAt = rest.indexOf('/', 2);
|
|
276
|
+
const authority = pathAt === -1 ? rest.slice(2) : rest.slice(2, pathAt);
|
|
277
|
+
const path = pathAt === -1 ? '/' : rest.slice(pathAt);
|
|
278
|
+
return { scheme, host: hostOfAuthority(authority, scheme), path, query, fragment };
|
|
279
|
+
};
|
|
280
|
+
exports.splitUrl = splitUrl;
|
|
281
|
+
/**
|
|
282
|
+
* Says whether a value is an absolute `http:` or `https:` address with a host.
|
|
283
|
+
* It reads the scheme by its prefix, so it needs no `URL` class.
|
|
284
|
+
*
|
|
285
|
+
* @param value - any string
|
|
286
|
+
* @returns true for an absolute `http:` or `https:` address
|
|
287
|
+
*/
|
|
288
|
+
const isHttpUrl = (value) => {
|
|
289
|
+
const { scheme, host } = (0, exports.splitUrl)(value);
|
|
290
|
+
return (scheme === 'http' || scheme === 'https') && host !== '';
|
|
291
|
+
};
|
|
292
|
+
exports.isHttpUrl = isHttpUrl;
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createPublicClient, createTransport, http, webSocket, } from 'viem';
|
|
2
2
|
import { countMethods, createRpcPacer, MAX_SAME_METHOD_CALLS_PER_SECOND, } from './rpcPacer.js';
|
|
3
|
+
import { splitUrl } from './url.js';
|
|
3
4
|
export { MAX_SAME_METHOD_CALLS_PER_SECOND } from './rpcPacer.js';
|
|
4
5
|
/**
|
|
5
6
|
* JSON-RPC request batching config. When enabled, viem coalesces requests made
|
|
@@ -65,7 +66,7 @@ const pacer = createRpcPacer({ ceiling: MAX_SAME_METHOD_CALLS_PER_SECOND });
|
|
|
65
66
|
const paceRequest = async (request) => {
|
|
66
67
|
// The HOST, not the full url: one proxy serves every chain from one budget,
|
|
67
68
|
// so `/evm/1` and `/evm/369` must count against the same tally.
|
|
68
|
-
const host =
|
|
69
|
+
const { host } = splitUrl(request.url);
|
|
69
70
|
await pacer.reserve(host, countMethods(await request.clone().text().catch(() => null)));
|
|
70
71
|
};
|
|
71
72
|
/**
|
package/dist/erc20.d.ts
CHANGED
|
@@ -7,11 +7,9 @@ export declare const erc20MetadataCalls: {
|
|
|
7
7
|
/**
|
|
8
8
|
* The multicall for the erc20 metadata.
|
|
9
9
|
*
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* signature also keeps the memoization an implementation detail — no caller
|
|
14
|
-
* touches the `.cache` property it would otherwise publish.
|
|
10
|
+
* The annotation spells out the plain call signature, which keeps the
|
|
11
|
+
* memoization an implementation detail: no caller touches the `.cache`
|
|
12
|
+
* property it would otherwise publish.
|
|
15
13
|
*/
|
|
16
14
|
export declare const multicallErc20: (inputs: {
|
|
17
15
|
client: PublicClient;
|
package/dist/erc20.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as _ from 'lodash-es';
|
|
2
1
|
import { erc20Abi, erc20Abi_bytes32, getAddress, } from 'viem';
|
|
2
|
+
import { memoizeByKey } from './cache.js';
|
|
3
3
|
import { multicallRead } from './multicall.js';
|
|
4
4
|
/** The erc20 metadata calls */
|
|
5
5
|
export const erc20MetadataCalls = [
|
|
@@ -10,13 +10,11 @@ export const erc20MetadataCalls = [
|
|
|
10
10
|
/**
|
|
11
11
|
* The multicall for the erc20 metadata.
|
|
12
12
|
*
|
|
13
|
-
* The
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* signature also keeps the memoization an implementation detail — no caller
|
|
17
|
-
* touches the `.cache` property it would otherwise publish.
|
|
13
|
+
* The annotation spells out the plain call signature, which keeps the
|
|
14
|
+
* memoization an implementation detail: no caller touches the `.cache`
|
|
15
|
+
* property it would otherwise publish.
|
|
18
16
|
*/
|
|
19
|
-
export const multicallErc20 =
|
|
17
|
+
export const multicallErc20 = memoizeByKey(async ({ client, target, chain }) => {
|
|
20
18
|
const options = {
|
|
21
19
|
chain: chain,
|
|
22
20
|
client: client,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/multicall.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as _ from 'lodash-es';
|
|
2
1
|
import { decodeFunctionResult, encodeFunctionData, getContract, multicall3Abi, } from 'viem';
|
|
3
2
|
import { ensureResult } from './assert.js';
|
|
4
3
|
/**
|
|
@@ -206,7 +205,7 @@ export const multicallRead = async ({ chain, client, abi, calls, target, }) => {
|
|
|
206
205
|
chainId: client.chain?.id,
|
|
207
206
|
target,
|
|
208
207
|
callCount: calls.length,
|
|
209
|
-
functionNames:
|
|
208
|
+
functionNames: [...new Set(calls.map((call) => call.functionName))],
|
|
210
209
|
});
|
|
211
210
|
throw err;
|
|
212
211
|
}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request addresses, built and read with plain string work.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS: React Native 0.77 installs its own `URL` and
|
|
5
|
+
* `URLSearchParams` (react-native/Libraries/Blob/URL.js), and both are partial.
|
|
6
|
+
* `new URL(x).protocol`, `.host`, `.pathname` and `.search` throw "not
|
|
7
|
+
* implemented". `searchParams` is always a new, empty list, so a reader of it
|
|
8
|
+
* silently sees no parameters. `URLSearchParams` can only append and
|
|
9
|
+
* serialize: `get`, `set`, `has` and `delete` throw, and a string passed to
|
|
10
|
+
* its constructor is ignored. A package that uses either class needs a
|
|
11
|
+
* polyfill in a wallet. These functions need nothing from the runtime but
|
|
12
|
+
* `encodeURIComponent` and `decodeURIComponent`.
|
|
13
|
+
*
|
|
14
|
+
* Their output matches what the WHATWG classes produce for the addresses the
|
|
15
|
+
* Gibs packages build: a lowercase `http:` or `https:` origin with a plain
|
|
16
|
+
* path. A query is encoded exactly as `URLSearchParams` encodes it, as
|
|
17
|
+
* `application/x-www-form-urlencoded`: a space becomes `+`, and only letters,
|
|
18
|
+
* digits and `*-._` stay as they are. They do not normalize the address
|
|
19
|
+
* itself: an uppercase host or a `..` path segment passes through as written.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* Query parameters, as a record or as ordered name and value pairs.
|
|
23
|
+
*
|
|
24
|
+
* In a record, a value of `undefined` leaves that parameter out, so an
|
|
25
|
+
* optional parameter can be written in place. Pairs keep their order and may
|
|
26
|
+
* repeat a name.
|
|
27
|
+
*/
|
|
28
|
+
export type QueryParameters = Readonly<Record<string, string | undefined>> | ReadonlyArray<readonly [string, string]>;
|
|
29
|
+
/** One query parameter: its decoded name and value. */
|
|
30
|
+
export type QueryEntry = readonly [name: string, value: string];
|
|
31
|
+
/** The parts of an address, as the WHATWG `URL` getters name them. */
|
|
32
|
+
export type UrlParts = {
|
|
33
|
+
/** The scheme in lowercase, with no colon, such as `https`. Empty when the address has none. */
|
|
34
|
+
readonly scheme: string;
|
|
35
|
+
/** The host in lowercase, with the port unless it is the scheme's default. Empty when the address has no authority. */
|
|
36
|
+
readonly host: string;
|
|
37
|
+
/** The path. `/` for an address with an authority and no path. */
|
|
38
|
+
readonly path: string;
|
|
39
|
+
/** The query, with no leading `?`. */
|
|
40
|
+
readonly query: string;
|
|
41
|
+
/** The fragment, with no leading `#`. */
|
|
42
|
+
readonly fragment: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Encodes a query name or value exactly as `URLSearchParams.toString` does.
|
|
46
|
+
*
|
|
47
|
+
* @param text - the raw name or value
|
|
48
|
+
* @returns the encoded text
|
|
49
|
+
*/
|
|
50
|
+
export declare const encodeQueryComponent: (text: string) => string;
|
|
51
|
+
/**
|
|
52
|
+
* Decodes a query name or value as the WHATWG URL standard says
|
|
53
|
+
* `URLSearchParams` must: `+` is a space, a valid `%XX` is a byte, a `%` that
|
|
54
|
+
* starts no valid sequence stays as it is, and each malformed UTF-8 part, or
|
|
55
|
+
* lone surrogate half, becomes U+FFFD.
|
|
56
|
+
*
|
|
57
|
+
* @param text - the encoded name or value
|
|
58
|
+
* @returns the decoded text
|
|
59
|
+
*/
|
|
60
|
+
export declare const decodeQueryComponent: (text: string) => string;
|
|
61
|
+
/**
|
|
62
|
+
* Lists the parameters of a query, decoded and in order.
|
|
63
|
+
*
|
|
64
|
+
* @param query - a query, with or without its leading `?`
|
|
65
|
+
* @returns each parameter's name and value; a parameter with no `=` has an empty value
|
|
66
|
+
*/
|
|
67
|
+
export declare const parseQuery: (query: string) => readonly QueryEntry[];
|
|
68
|
+
/**
|
|
69
|
+
* Writes a query, as `new URLSearchParams(parameters).toString()` does.
|
|
70
|
+
*
|
|
71
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
72
|
+
* @returns the encoded query, with no leading `?`; empty for no parameters
|
|
73
|
+
*/
|
|
74
|
+
export declare const buildQuery: (parameters: QueryParameters) => string;
|
|
75
|
+
/**
|
|
76
|
+
* Sets one parameter in a query, as `URLSearchParams.set` does: the first
|
|
77
|
+
* parameter of that name takes the value and any later ones are removed, or
|
|
78
|
+
* the parameter is appended when none has the name.
|
|
79
|
+
*
|
|
80
|
+
* @param query - an encoded query, with or without its leading `?`
|
|
81
|
+
* @param name - the parameter's name
|
|
82
|
+
* @param value - its value
|
|
83
|
+
* @returns the encoded query, with no leading `?`
|
|
84
|
+
*/
|
|
85
|
+
export declare const setQueryParameter: (query: string, name: string, value: string) => string;
|
|
86
|
+
/**
|
|
87
|
+
* Adds query parameters to an address, as `url.searchParams.set` does for
|
|
88
|
+
* each one in turn. A query already on the address is kept, and a fragment
|
|
89
|
+
* stays at the end.
|
|
90
|
+
*
|
|
91
|
+
* @param address - the address, with or without a query
|
|
92
|
+
* @param parameters - a record or pairs; see {@link QueryParameters}
|
|
93
|
+
* @returns the address with the parameters set; the address unchanged when there are none
|
|
94
|
+
*/
|
|
95
|
+
export declare const buildUrl: (address: string, parameters: QueryParameters) => string;
|
|
96
|
+
/**
|
|
97
|
+
* Splits an address into its parts, without the WHATWG `URL` class.
|
|
98
|
+
*
|
|
99
|
+
* @param address - any address; leading and trailing spaces and control characters are ignored, as the WHATWG parser ignores them
|
|
100
|
+
* @returns the parts; see {@link UrlParts}
|
|
101
|
+
*/
|
|
102
|
+
export declare const splitUrl: (address: string) => UrlParts;
|
|
103
|
+
/**
|
|
104
|
+
* Says whether a value is an absolute `http:` or `https:` address with a host.
|
|
105
|
+
* It reads the scheme by its prefix, so it needs no `URL` class.
|
|
106
|
+
*
|
|
107
|
+
* @param value - any string
|
|
108
|
+
* @returns true for an absolute `http:` or `https:` address
|
|
109
|
+
*/
|
|
110
|
+
export declare const isHttpUrl: (value: string) => boolean;
|